ath79: add support for TP-Link Archer A7
[openwrt/staging/chunkeey.git] / tools / firmware-utils / src / tplink-safeloader.c
1 /*
2 Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27 /*
28 tplink-safeloader
29
30 Image generation tool for the TP-LINK SafeLoader as seen on
31 TP-LINK Pharos devices (CPE210/220/510/520)
32 */
33
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #include <arpa/inet.h>
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <limits.h>
50
51 #include "md5.h"
52
53
54 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
55
56
57 #define MAX_PARTITIONS 32
58
59 /** An image partition table entry */
60 struct image_partition_entry {
61 const char *name;
62 size_t size;
63 uint8_t *data;
64 };
65
66 /** A flash partition table entry */
67 struct flash_partition_entry {
68 char *name;
69 uint32_t base;
70 uint32_t size;
71 };
72
73 /** Firmware layout description */
74 struct device_info {
75 const char *id;
76 const char *vendor;
77 const char *support_list;
78 char support_trail;
79 const char *soft_ver;
80 struct flash_partition_entry partitions[MAX_PARTITIONS+1];
81 const char *first_sysupgrade_partition;
82 const char *last_sysupgrade_partition;
83 };
84
85 /** The content of the soft-version structure */
86 struct __attribute__((__packed__)) soft_version {
87 uint32_t magic;
88 uint32_t zero;
89 uint8_t pad1;
90 uint8_t version_major;
91 uint8_t version_minor;
92 uint8_t version_patch;
93 uint8_t year_hi;
94 uint8_t year_lo;
95 uint8_t month;
96 uint8_t day;
97 uint32_t rev;
98 uint8_t pad2;
99 };
100
101
102 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
103
104
105 /**
106 Salt for the MD5 hash
107
108 Fortunately, TP-LINK seems to use the same salt for most devices which use
109 the new image format.
110 */
111 static const uint8_t md5_salt[16] = {
112 0x7a, 0x2b, 0x15, 0xed,
113 0x9b, 0x98, 0x59, 0x6d,
114 0xe5, 0x04, 0xab, 0x44,
115 0xac, 0x2a, 0x9f, 0x4e,
116 };
117
118
119 /** Firmware layout table */
120 static struct device_info boards[] = {
121 /** Firmware layout for the CPE210/220 */
122 {
123 .id = "CPE210",
124 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
125 .support_list =
126 "SupportList:\r\n"
127 "CPE210(TP-LINK|UN|N300-2):1.0\r\n"
128 "CPE210(TP-LINK|UN|N300-2):1.1\r\n"
129 "CPE210(TP-LINK|US|N300-2):1.1\r\n"
130 "CPE210(TP-LINK|EU|N300-2):1.1\r\n"
131 "CPE220(TP-LINK|UN|N300-2):1.1\r\n"
132 "CPE220(TP-LINK|US|N300-2):1.1\r\n"
133 "CPE220(TP-LINK|EU|N300-2):1.1\r\n",
134 .support_trail = '\xff',
135 .soft_ver = NULL,
136
137 .partitions = {
138 {"fs-uboot", 0x00000, 0x20000},
139 {"partition-table", 0x20000, 0x02000},
140 {"default-mac", 0x30000, 0x00020},
141 {"product-info", 0x31100, 0x00100},
142 {"signature", 0x32000, 0x00400},
143 {"os-image", 0x40000, 0x1c0000},
144 {"file-system", 0x200000, 0x5b0000},
145 {"soft-version", 0x7b0000, 0x00100},
146 {"support-list", 0x7b1000, 0x00400},
147 {"user-config", 0x7c0000, 0x10000},
148 {"default-config", 0x7d0000, 0x10000},
149 {"log", 0x7e0000, 0x10000},
150 {"radio", 0x7f0000, 0x10000},
151 {NULL, 0, 0}
152 },
153
154 .first_sysupgrade_partition = "os-image",
155 .last_sysupgrade_partition = "support-list",
156 },
157
158 /** Firmware layout for the CPE210 V2 */
159 {
160 .id = "CPE210V2",
161 .vendor = "CPE210(TP-LINK|UN|N300-2|00000000):2.0\r\n",
162 .support_list =
163 "SupportList:\r\n"
164 "CPE210(TP-LINK|EU|N300-2|00000000):2.0\r\n"
165 "CPE210(TP-LINK|EU|N300-2|45550000):2.0\r\n"
166 "CPE210(TP-LINK|EU|N300-2|55530000):2.0\r\n"
167 "CPE210(TP-LINK|UN|N300-2|00000000):2.0\r\n"
168 "CPE210(TP-LINK|UN|N300-2|45550000):2.0\r\n"
169 "CPE210(TP-LINK|UN|N300-2|55530000):2.0\r\n"
170 "CPE210(TP-LINK|US|N300-2|55530000):2.0\r\n"
171 "CPE210(TP-LINK|UN|N300-2):2.0\r\n"
172 "CPE210(TP-LINK|EU|N300-2):2.0\r\n"
173 "CPE210(TP-LINK|US|N300-2):2.0\r\n",
174 .support_trail = '\xff',
175 .soft_ver = NULL,
176
177 .partitions = {
178 {"fs-uboot", 0x00000, 0x20000},
179 {"partition-table", 0x20000, 0x02000},
180 {"default-mac", 0x30000, 0x00020},
181 {"product-info", 0x31100, 0x00100},
182 {"device-info", 0x31400, 0x00400},
183 {"signature", 0x32000, 0x00400},
184 {"device-id", 0x33000, 0x00100},
185 {"os-image", 0x40000, 0x1c0000},
186 {"file-system", 0x200000, 0x5b0000},
187 {"soft-version", 0x7b0000, 0x00100},
188 {"support-list", 0x7b1000, 0x01000},
189 {"user-config", 0x7c0000, 0x10000},
190 {"default-config", 0x7d0000, 0x10000},
191 {"log", 0x7e0000, 0x10000},
192 {"radio", 0x7f0000, 0x10000},
193 {NULL, 0, 0}
194 },
195
196 .first_sysupgrade_partition = "os-image",
197 .last_sysupgrade_partition = "support-list",
198 },
199
200 /** Firmware layout for the CPE510/520 */
201 {
202 .id = "CPE510",
203 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
204 .support_list =
205 "SupportList:\r\n"
206 "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
207 "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
208 "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
209 "CPE510(TP-LINK|US|N300-5):1.1\r\n"
210 "CPE510(TP-LINK|EU|N300-5):1.1\r\n"
211 "CPE520(TP-LINK|UN|N300-5):1.1\r\n"
212 "CPE520(TP-LINK|US|N300-5):1.1\r\n"
213 "CPE520(TP-LINK|EU|N300-5):1.1\r\n"
214 "CPE510(TP-LINK|EU|N300-5|00000000):2.0\r\n"
215 "CPE510(TP-LINK|EU|N300-5|45550000):2.0\r\n"
216 "CPE510(TP-LINK|EU|N300-5|55530000):2.0\r\n"
217 "CPE510(TP-LINK|UN|N300-5|00000000):2.0\r\n"
218 "CPE510(TP-LINK|UN|N300-5|45550000):2.0\r\n"
219 "CPE510(TP-LINK|UN|N300-5|55530000):2.0\r\n"
220 "CPE510(TP-LINK|US|N300-5|55530000):2.0\r\n"
221 "CPE510(TP-LINK|UN|N300-5):2.0\r\n"
222 "CPE510(TP-LINK|EU|N300-5):2.0\r\n"
223 "CPE510(TP-LINK|US|N300-5):2.0\r\n",
224 .support_trail = '\xff',
225 .soft_ver = NULL,
226
227 .partitions = {
228 {"fs-uboot", 0x00000, 0x20000},
229 {"partition-table", 0x20000, 0x02000},
230 {"default-mac", 0x30000, 0x00020},
231 {"product-info", 0x31100, 0x00100},
232 {"signature", 0x32000, 0x00400},
233 {"os-image", 0x40000, 0x1c0000},
234 {"file-system", 0x200000, 0x5b0000},
235 {"soft-version", 0x7b0000, 0x00100},
236 {"support-list", 0x7b1000, 0x00400},
237 {"user-config", 0x7c0000, 0x10000},
238 {"default-config", 0x7d0000, 0x10000},
239 {"log", 0x7e0000, 0x10000},
240 {"radio", 0x7f0000, 0x10000},
241 {NULL, 0, 0}
242 },
243
244 .first_sysupgrade_partition = "os-image",
245 .last_sysupgrade_partition = "support-list",
246 },
247
248 {
249 .id = "WBS210",
250 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
251 .support_list =
252 "SupportList:\r\n"
253 "WBS210(TP-LINK|UN|N300-2):1.20\r\n"
254 "WBS210(TP-LINK|US|N300-2):1.20\r\n"
255 "WBS210(TP-LINK|EU|N300-2):1.20\r\n",
256 .support_trail = '\xff',
257 .soft_ver = NULL,
258
259 .partitions = {
260 {"fs-uboot", 0x00000, 0x20000},
261 {"partition-table", 0x20000, 0x02000},
262 {"default-mac", 0x30000, 0x00020},
263 {"product-info", 0x31100, 0x00100},
264 {"signature", 0x32000, 0x00400},
265 {"os-image", 0x40000, 0x1c0000},
266 {"file-system", 0x200000, 0x5b0000},
267 {"soft-version", 0x7b0000, 0x00100},
268 {"support-list", 0x7b1000, 0x00400},
269 {"user-config", 0x7c0000, 0x10000},
270 {"default-config", 0x7d0000, 0x10000},
271 {"log", 0x7e0000, 0x10000},
272 {"radio", 0x7f0000, 0x10000},
273 {NULL, 0, 0}
274 },
275
276 .first_sysupgrade_partition = "os-image",
277 .last_sysupgrade_partition = "support-list",
278 },
279
280 {
281 .id = "WBS510",
282 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
283 .support_list =
284 "SupportList:\r\n"
285 "WBS510(TP-LINK|UN|N300-5):1.20\r\n"
286 "WBS510(TP-LINK|US|N300-5):1.20\r\n"
287 "WBS510(TP-LINK|EU|N300-5):1.20\r\n",
288 .support_trail = '\xff',
289 .soft_ver = NULL,
290
291 .partitions = {
292 {"fs-uboot", 0x00000, 0x20000},
293 {"partition-table", 0x20000, 0x02000},
294 {"default-mac", 0x30000, 0x00020},
295 {"product-info", 0x31100, 0x00100},
296 {"signature", 0x32000, 0x00400},
297 {"os-image", 0x40000, 0x1c0000},
298 {"file-system", 0x200000, 0x5b0000},
299 {"soft-version", 0x7b0000, 0x00100},
300 {"support-list", 0x7b1000, 0x00400},
301 {"user-config", 0x7c0000, 0x10000},
302 {"default-config", 0x7d0000, 0x10000},
303 {"log", 0x7e0000, 0x10000},
304 {"radio", 0x7f0000, 0x10000},
305 {NULL, 0, 0}
306 },
307
308 .first_sysupgrade_partition = "os-image",
309 .last_sysupgrade_partition = "support-list",
310 },
311
312 /** Firmware layout for the C2600 */
313 {
314 .id = "C2600",
315 .vendor = "",
316 .support_list =
317 "SupportList:\r\n"
318 "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n",
319 .support_trail = '\x00',
320 .soft_ver = NULL,
321
322 /**
323 We use a bigger os-image partition than the stock images (and thus
324 smaller file-system), as our kernel doesn't fit in the stock firmware's
325 2 MB os-image since kernel 4.14.
326 */
327 .partitions = {
328 {"SBL1", 0x00000, 0x20000},
329 {"MIBIB", 0x20000, 0x20000},
330 {"SBL2", 0x40000, 0x20000},
331 {"SBL3", 0x60000, 0x30000},
332 {"DDRCONFIG", 0x90000, 0x10000},
333 {"SSD", 0xa0000, 0x10000},
334 {"TZ", 0xb0000, 0x30000},
335 {"RPM", 0xe0000, 0x20000},
336 {"fs-uboot", 0x100000, 0x70000},
337 {"uboot-env", 0x170000, 0x40000},
338 {"radio", 0x1b0000, 0x40000},
339 {"os-image", 0x1f0000, 0x400000}, /* Stock: base 0x1f0000 size 0x200000 */
340 {"file-system", 0x5f0000, 0x1900000}, /* Stock: base 0x3f0000 size 0x1b00000 */
341 {"default-mac", 0x1ef0000, 0x00200},
342 {"pin", 0x1ef0200, 0x00200},
343 {"product-info", 0x1ef0400, 0x0fc00},
344 {"partition-table", 0x1f00000, 0x10000},
345 {"soft-version", 0x1f10000, 0x10000},
346 {"support-list", 0x1f20000, 0x10000},
347 {"profile", 0x1f30000, 0x10000},
348 {"default-config", 0x1f40000, 0x10000},
349 {"user-config", 0x1f50000, 0x40000},
350 {"qos-db", 0x1f90000, 0x40000},
351 {"usb-config", 0x1fd0000, 0x10000},
352 {"log", 0x1fe0000, 0x20000},
353 {NULL, 0, 0}
354 },
355
356 .first_sysupgrade_partition = "os-image",
357 .last_sysupgrade_partition = "file-system"
358 },
359
360 /** Firmware layout for the A7-V5 */
361 {
362 .id = "ARCHER-A7-V5",
363 .support_list =
364 "SupportList:\n"
365 "{product_name:Archer A7,product_ver:5.0.0,special_id:45550000}\n"
366 "{product_name:Archer A7,product_ver:5.0.0,special_id:55530000}\n"
367 "{product_name:Archer A7,product_ver:5.0.0,special_id:43410000}\n"
368 "{product_name:Archer A7,product_ver:5.0.0,special_id:4A500000}\n"
369 "{product_name:Archer A7,product_ver:5.0.0,special_id:54570000}\n",
370 .support_trail = '\x00',
371 .soft_ver = "soft_ver:1.0.0\n",
372
373 /* We're using a dynamic kernel/rootfs split here */
374 .partitions = {
375 {"factory-boot", 0x00000, 0x20000},
376 {"fs-uboot", 0x20000, 0x20000},
377 {"firmware", 0x40000, 0xec0000}, /* Stock: name os-image base 0x40000 size 0x120000 */
378 /* Stock: name file-system base 0x160000 size 0xda0000 */
379 {"default-mac", 0xf40000, 0x00200},
380 {"pin", 0xf40200, 0x00200},
381 {"device-id", 0xf40400, 0x00100},
382 {"product-info", 0xf40500, 0x0fb00},
383 {"soft-version", 0xf50000, 0x00100},
384 {"extra-para", 0xf51000, 0x01000},
385 {"support-list", 0xf52000, 0x0a000},
386 {"profile", 0xf5c000, 0x04000},
387 {"default-config", 0xf60000, 0x10000},
388 {"user-config", 0xf70000, 0x40000},
389 {"certificate", 0xfb0000, 0x10000},
390 {"partition-table", 0xfc0000, 0x10000},
391 {"log", 0xfd0000, 0x20000},
392 {"radio", 0xff0000, 0x10000},
393 {NULL, 0, 0}
394 },
395
396 .first_sysupgrade_partition = "os-image",
397 .last_sysupgrade_partition = "file-system",
398 },
399
400 /** Firmware layout for the C25v1 */
401 {
402 .id = "ARCHER-C25-V1",
403 .support_list =
404 "SupportList:\n"
405 "{product_name:ArcherC25,product_ver:1.0.0,special_id:00000000}\n"
406 "{product_name:ArcherC25,product_ver:1.0.0,special_id:55530000}\n"
407 "{product_name:ArcherC25,product_ver:1.0.0,special_id:45550000}\n",
408 .support_trail = '\x00',
409 .soft_ver = "soft_ver:1.0.0\n",
410
411 /* We're using a dynamic kernel/rootfs split here */
412 .partitions = {
413 {"factory-boot", 0x00000, 0x20000},
414 {"fs-uboot", 0x20000, 0x10000},
415 {"firmware", 0x30000, 0x7a0000}, /* Stock: name os-image base 0x30000 size 0x100000 */
416 /* Stock: name file-system base 0x130000 size 0x6a0000 */
417 {"user-config", 0x7d0000, 0x04000},
418 {"default-mac", 0x7e0000, 0x00100},
419 {"device-id", 0x7e0100, 0x00100},
420 {"extra-para", 0x7e0200, 0x00100},
421 {"pin", 0x7e0300, 0x00100},
422 {"support-list", 0x7e0400, 0x00400},
423 {"soft-version", 0x7e0800, 0x00400},
424 {"product-info", 0x7e0c00, 0x01400},
425 {"partition-table", 0x7e2000, 0x01000},
426 {"profile", 0x7e3000, 0x01000},
427 {"default-config", 0x7e4000, 0x04000},
428 {"merge-config", 0x7ec000, 0x02000},
429 {"qos-db", 0x7ee000, 0x02000},
430 {"radio", 0x7f0000, 0x10000},
431 {NULL, 0, 0}
432 },
433
434 .first_sysupgrade_partition = "os-image",
435 .last_sysupgrade_partition = "file-system",
436 },
437
438 /** Firmware layout for the C58v1 */
439 {
440 .id = "ARCHER-C58-V1",
441 .vendor = "",
442 .support_list =
443 "SupportList:\r\n"
444 "{product_name:Archer C58,product_ver:1.0.0,special_id:00000000}\r\n"
445 "{product_name:Archer C58,product_ver:1.0.0,special_id:45550000}\r\n"
446 "{product_name:Archer C58,product_ver:1.0.0,special_id:55530000}\r\n",
447 .support_trail = '\x00',
448 .soft_ver = "soft_ver:1.0.0\n",
449
450 .partitions = {
451 {"fs-uboot", 0x00000, 0x10000},
452 {"default-mac", 0x10000, 0x00200},
453 {"pin", 0x10200, 0x00200},
454 {"product-info", 0x10400, 0x00100},
455 {"partition-table", 0x10500, 0x00800},
456 {"soft-version", 0x11300, 0x00200},
457 {"support-list", 0x11500, 0x00100},
458 {"device-id", 0x11600, 0x00100},
459 {"profile", 0x11700, 0x03900},
460 {"default-config", 0x15000, 0x04000},
461 {"user-config", 0x19000, 0x04000},
462 {"firmware", 0x20000, 0x7c8000},
463 {"certyficate", 0x7e8000, 0x08000},
464 {"radio", 0x7f0000, 0x10000},
465 {NULL, 0, 0}
466 },
467
468 .first_sysupgrade_partition = "os-image",
469 .last_sysupgrade_partition = "file-system",
470 },
471
472 /** Firmware layout for the C59v1 */
473 {
474 .id = "ARCHER-C59-V1",
475 .vendor = "",
476 .support_list =
477 "SupportList:\r\n"
478 "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n"
479 "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n"
480 "{product_name:Archer C59,product_ver:1.0.0,special_id:52550000}\r\n"
481 "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n",
482 .support_trail = '\x00',
483 .soft_ver = "soft_ver:1.0.0\n",
484
485 /* We're using a dynamic kernel/rootfs split here */
486 .partitions = {
487 {"fs-uboot", 0x00000, 0x10000},
488 {"default-mac", 0x10000, 0x00200},
489 {"pin", 0x10200, 0x00200},
490 {"device-id", 0x10400, 0x00100},
491 {"product-info", 0x10500, 0x0fb00},
492 {"firmware", 0x20000, 0xe30000},
493 {"partition-table", 0xe50000, 0x10000},
494 {"soft-version", 0xe60000, 0x10000},
495 {"support-list", 0xe70000, 0x10000},
496 {"profile", 0xe80000, 0x10000},
497 {"default-config", 0xe90000, 0x10000},
498 {"user-config", 0xea0000, 0x40000},
499 {"usb-config", 0xee0000, 0x10000},
500 {"certificate", 0xef0000, 0x10000},
501 {"qos-db", 0xf00000, 0x40000},
502 {"log", 0xfe0000, 0x10000},
503 {"radio", 0xff0000, 0x10000},
504 {NULL, 0, 0}
505 },
506
507 .first_sysupgrade_partition = "os-image",
508 .last_sysupgrade_partition = "file-system",
509 },
510
511 /** Firmware layout for the C59v2 */
512 {
513 .id = "ARCHER-C59-V2",
514 .vendor = "",
515 .support_list =
516 "SupportList:\r\n"
517 "{product_name:Archer C59,product_ver:2.0.0,special_id:00000000}\r\n"
518 "{product_name:Archer C59,product_ver:2.0.0,special_id:45550000}\r\n"
519 "{product_name:Archer C59,product_ver:2.0.0,special_id:55530000}\r\n",
520 .support_trail = '\x00',
521 .soft_ver = "soft_ver:2.0.0 Build 20161206 rel.7303\n",
522
523 /** We're using a dynamic kernel/rootfs split here */
524 .partitions = {
525 {"factory-boot", 0x00000, 0x20000},
526 {"fs-uboot", 0x20000, 0x10000},
527 {"default-mac", 0x30000, 0x00200},
528 {"pin", 0x30200, 0x00200},
529 {"device-id", 0x30400, 0x00100},
530 {"product-info", 0x30500, 0x0fb00},
531 {"firmware", 0x40000, 0xe10000},
532 {"partition-table", 0xe50000, 0x10000},
533 {"soft-version", 0xe60000, 0x10000},
534 {"support-list", 0xe70000, 0x10000},
535 {"profile", 0xe80000, 0x10000},
536 {"default-config", 0xe90000, 0x10000},
537 {"user-config", 0xea0000, 0x40000},
538 {"usb-config", 0xee0000, 0x10000},
539 {"certificate", 0xef0000, 0x10000},
540 {"extra-para", 0xf00000, 0x10000},
541 {"qos-db", 0xf10000, 0x30000},
542 {"log", 0xfe0000, 0x10000},
543 {"radio", 0xff0000, 0x10000},
544 {NULL, 0, 0}
545 },
546
547 .first_sysupgrade_partition = "os-image",
548 .last_sysupgrade_partition = "file-system",
549 },
550
551 /** Firmware layout for the C60v1 */
552 {
553 .id = "ARCHER-C60-V1",
554 .vendor = "",
555 .support_list =
556 "SupportList:\r\n"
557 "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n"
558 "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n"
559 "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n",
560 .support_trail = '\x00',
561 .soft_ver = "soft_ver:1.0.0\n",
562
563 .partitions = {
564 {"fs-uboot", 0x00000, 0x10000},
565 {"default-mac", 0x10000, 0x00200},
566 {"pin", 0x10200, 0x00200},
567 {"product-info", 0x10400, 0x00100},
568 {"partition-table", 0x10500, 0x00800},
569 {"soft-version", 0x11300, 0x00200},
570 {"support-list", 0x11500, 0x00100},
571 {"device-id", 0x11600, 0x00100},
572 {"profile", 0x11700, 0x03900},
573 {"default-config", 0x15000, 0x04000},
574 {"user-config", 0x19000, 0x04000},
575 {"firmware", 0x20000, 0x7c8000},
576 {"certyficate", 0x7e8000, 0x08000},
577 {"radio", 0x7f0000, 0x10000},
578 {NULL, 0, 0}
579 },
580
581 .first_sysupgrade_partition = "os-image",
582 .last_sysupgrade_partition = "file-system",
583 },
584
585 /** Firmware layout for the C60v2 */
586 {
587 .id = "ARCHER-C60-V2",
588 .vendor = "",
589 .support_list =
590 "SupportList:\r\n"
591 "{product_name:Archer C60,product_ver:2.0.0,special_id:42520000}\r\n"
592 "{product_name:Archer C60,product_ver:2.0.0,special_id:45550000}\r\n"
593 "{product_name:Archer C60,product_ver:2.0.0,special_id:55530000}\r\n",
594 .support_trail = '\x00',
595 .soft_ver = "soft_ver:2.0.0\n",
596
597 .partitions = {
598 {"factory-boot", 0x00000, 0x1fb00},
599 {"default-mac", 0x1fb00, 0x00200},
600 {"pin", 0x1fd00, 0x00100},
601 {"product-info", 0x1fe00, 0x00100},
602 {"device-id", 0x1ff00, 0x00100},
603 {"fs-uboot", 0x20000, 0x10000},
604 {"firmware", 0x30000, 0x7a0000},
605 {"soft-version", 0x7d9500, 0x00100},
606 {"support-list", 0x7d9600, 0x00100},
607 {"extra-para", 0x7d9700, 0x00100},
608 {"profile", 0x7d9800, 0x03000},
609 {"default-config", 0x7dc800, 0x03000},
610 {"partition-table", 0x7df800, 0x00800},
611 {"user-config", 0x7e0000, 0x0c000},
612 {"certificate", 0x7ec000, 0x04000},
613 {"radio", 0x7f0000, 0x10000},
614 {NULL, 0, 0}
615 },
616
617 .first_sysupgrade_partition = "os-image",
618 .last_sysupgrade_partition = "file-system",
619 },
620
621 /** Firmware layout for the C5 */
622 {
623 .id = "ARCHER-C5-V2",
624 .vendor = "",
625 .support_list =
626 "SupportList:\r\n"
627 "{product_name:ArcherC5,product_ver:2.0.0,special_id:00000000}\r\n"
628 "{product_name:ArcherC5,product_ver:2.0.0,special_id:55530000}\r\n"
629 "{product_name:ArcherC5,product_ver:2.0.0,special_id:4A500000}\r\n", /* JP version */
630 .support_trail = '\x00',
631 .soft_ver = NULL,
632
633 .partitions = {
634 {"fs-uboot", 0x00000, 0x40000},
635 {"os-image", 0x40000, 0x200000},
636 {"file-system", 0x240000, 0xc00000},
637 {"default-mac", 0xe40000, 0x00200},
638 {"pin", 0xe40200, 0x00200},
639 {"product-info", 0xe40400, 0x00200},
640 {"partition-table", 0xe50000, 0x10000},
641 {"soft-version", 0xe60000, 0x00200},
642 {"support-list", 0xe61000, 0x0f000},
643 {"profile", 0xe70000, 0x10000},
644 {"default-config", 0xe80000, 0x10000},
645 {"user-config", 0xe90000, 0x50000},
646 {"log", 0xee0000, 0x100000},
647 {"radio_bk", 0xfe0000, 0x10000},
648 {"radio", 0xff0000, 0x10000},
649 {NULL, 0, 0}
650 },
651
652 .first_sysupgrade_partition = "os-image",
653 .last_sysupgrade_partition = "file-system"
654 },
655
656 /** Firmware layout for the C7 */
657 {
658 .id = "ARCHER-C7-V4",
659 .support_list =
660 "SupportList:\n"
661 "{product_name:Archer C7,product_ver:4.0.0,special_id:00000000}\n"
662 "{product_name:Archer C7,product_ver:4.0.0,special_id:41550000}\n"
663 "{product_name:Archer C7,product_ver:4.0.0,special_id:45550000}\n"
664 "{product_name:Archer C7,product_ver:4.0.0,special_id:4B520000}\n"
665 "{product_name:Archer C7,product_ver:4.0.0,special_id:42520000}\n"
666 "{product_name:Archer C7,product_ver:4.0.0,special_id:4A500000}\n"
667 "{product_name:Archer C7,product_ver:4.0.0,special_id:52550000}\n"
668 "{product_name:Archer C7,product_ver:4.0.0,special_id:54570000}\n"
669 "{product_name:Archer C7,product_ver:4.0.0,special_id:55530000}\n"
670 "{product_name:Archer C7,product_ver:4.0.0,special_id:43410000}\n",
671 .support_trail = '\x00',
672 .soft_ver = "soft_ver:1.0.0\n",
673
674 /* We're using a dynamic kernel/rootfs split here */
675 .partitions = {
676 {"factory-boot", 0x00000, 0x20000},
677 {"fs-uboot", 0x20000, 0x20000},
678 {"firmware", 0x40000, 0xEC0000}, /* Stock: name os-image base 0x40000 size 0x120000 */
679 /* Stock: name file-system base 0x160000 size 0xda0000 */
680 {"default-mac", 0xf00000, 0x00200},
681 {"pin", 0xf00200, 0x00200},
682 {"device-id", 0xf00400, 0x00100},
683 {"product-info", 0xf00500, 0x0fb00},
684 {"soft-version", 0xf10000, 0x00100},
685 {"extra-para", 0xf11000, 0x01000},
686 {"support-list", 0xf12000, 0x0a000},
687 {"profile", 0xf1c000, 0x04000},
688 {"default-config", 0xf20000, 0x10000},
689 {"user-config", 0xf30000, 0x40000},
690 {"qos-db", 0xf70000, 0x40000},
691 {"certificate", 0xfb0000, 0x10000},
692 {"partition-table", 0xfc0000, 0x10000},
693 {"log", 0xfd0000, 0x20000},
694 {"radio", 0xff0000, 0x10000},
695 {NULL, 0, 0}
696 },
697
698 .first_sysupgrade_partition = "os-image",
699 .last_sysupgrade_partition = "file-system",
700 },
701
702 /** Firmware layout for the C7 v5*/
703 {
704 .id = "ARCHER-C7-V5",
705 .support_list =
706 "SupportList:\n"
707 "{product_name:Archer C7,product_ver:5.0.0,special_id:00000000}\n"
708 "{product_name:Archer C7,product_ver:5.0.0,special_id:45550000}\n"
709 "{product_name:Archer C7,product_ver:5.0.0,special_id:55530000}\n"
710 "{product_name:Archer C7,product_ver:5.0.0,special_id:43410000}\n"
711 "{product_name:Archer C7,product_ver:5.0.0,special_id:4A500000}\n"
712 "{product_name:Archer C7,product_ver:5.0.0,special_id:54570000}\n",
713
714 .support_trail = '\x00',
715 .soft_ver = "soft_ver:1.0.0\n",
716
717 /* We're using a dynamic kernel/rootfs split here */
718 .partitions = {
719 {"factory-boot", 0x00000, 0x20000},
720 {"fs-uboot", 0x20000, 0x20000},
721 {"partition-table", 0x40000, 0x10000},
722 {"radio", 0x50000, 0x10000},
723 {"default-mac", 0x60000, 0x00200},
724 {"pin", 0x60200, 0x00200},
725 {"device-id", 0x60400, 0x00100},
726 {"product-info", 0x60500, 0x0fb00},
727 {"soft-version", 0x70000, 0x01000},
728 {"extra-para", 0x71000, 0x01000},
729 {"support-list", 0x72000, 0x0a000},
730 {"profile", 0x7c000, 0x04000},
731 {"user-config", 0x80000, 0x40000},
732
733
734 {"firmware", 0xc0000, 0xf00000}, /* Stock: name os-image base 0xc0000 size 0x120000 */
735 /* Stock: name file-system base 0x1e0000 size 0xde0000 */
736
737 {"log", 0xfc0000, 0x20000},
738 {"certificate", 0xfe0000, 0x10000},
739 {"default-config", 0xff0000, 0x10000},
740 {NULL, 0, 0}
741
742 },
743
744 .first_sysupgrade_partition = "os-image",
745 .last_sysupgrade_partition = "file-system",
746 },
747
748 /** Firmware layout for the C9 */
749 {
750 .id = "ARCHERC9",
751 .vendor = "",
752 .support_list =
753 "SupportList:\n"
754 "{product_name:ArcherC9,"
755 "product_ver:1.0.0,"
756 "special_id:00000000}\n",
757 .support_trail = '\x00',
758 .soft_ver = NULL,
759
760 .partitions = {
761 {"fs-uboot", 0x00000, 0x40000},
762 {"os-image", 0x40000, 0x200000},
763 {"file-system", 0x240000, 0xc00000},
764 {"default-mac", 0xe40000, 0x00200},
765 {"pin", 0xe40200, 0x00200},
766 {"product-info", 0xe40400, 0x00200},
767 {"partition-table", 0xe50000, 0x10000},
768 {"soft-version", 0xe60000, 0x00200},
769 {"support-list", 0xe61000, 0x0f000},
770 {"profile", 0xe70000, 0x10000},
771 {"default-config", 0xe80000, 0x10000},
772 {"user-config", 0xe90000, 0x50000},
773 {"log", 0xee0000, 0x100000},
774 {"radio_bk", 0xfe0000, 0x10000},
775 {"radio", 0xff0000, 0x10000},
776 {NULL, 0, 0}
777 },
778
779 .first_sysupgrade_partition = "os-image",
780 .last_sysupgrade_partition = "file-system"
781 },
782
783 /** Firmware layout for the EAP120 */
784 {
785 .id = "EAP120",
786 .vendor = "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
787 .support_list =
788 "SupportList:\r\n"
789 "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
790 .support_trail = '\xff',
791 .soft_ver = NULL,
792
793 .partitions = {
794 {"fs-uboot", 0x00000, 0x20000},
795 {"partition-table", 0x20000, 0x02000},
796 {"default-mac", 0x30000, 0x00020},
797 {"support-list", 0x31000, 0x00100},
798 {"product-info", 0x31100, 0x00100},
799 {"soft-version", 0x32000, 0x00100},
800 {"os-image", 0x40000, 0x180000},
801 {"file-system", 0x1c0000, 0x600000},
802 {"user-config", 0x7c0000, 0x10000},
803 {"backup-config", 0x7d0000, 0x10000},
804 {"log", 0x7e0000, 0x10000},
805 {"radio", 0x7f0000, 0x10000},
806 {NULL, 0, 0}
807 },
808
809 .first_sysupgrade_partition = "os-image",
810 .last_sysupgrade_partition = "file-system"
811 },
812
813 /** Firmware layout for the TL-WA850RE v2 */
814 {
815 .id = "TLWA850REV2",
816 .vendor = "",
817 .support_list =
818 "SupportList:\n"
819 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55530000}\n"
820 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:00000000}\n"
821 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55534100}\n"
822 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:45550000}\n"
823 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4B520000}\n"
824 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:42520000}\n"
825 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4A500000}\n"
826 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:43410000}\n"
827 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:41550000}\n"
828 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:52550000}\n",
829 .support_trail = '\x00',
830 .soft_ver = NULL,
831
832 /**
833 576KB were moved from file-system to os-image
834 in comparison to the stock image
835 */
836 .partitions = {
837 {"fs-uboot", 0x00000, 0x20000},
838 {"os-image", 0x20000, 0x150000},
839 {"file-system", 0x170000, 0x240000},
840 {"partition-table", 0x3b0000, 0x02000},
841 {"default-mac", 0x3c0000, 0x00020},
842 {"pin", 0x3c0100, 0x00020},
843 {"product-info", 0x3c1000, 0x01000},
844 {"soft-version", 0x3c2000, 0x00100},
845 {"support-list", 0x3c3000, 0x01000},
846 {"profile", 0x3c4000, 0x08000},
847 {"user-config", 0x3d0000, 0x10000},
848 {"default-config", 0x3e0000, 0x10000},
849 {"radio", 0x3f0000, 0x10000},
850 {NULL, 0, 0}
851 },
852
853 .first_sysupgrade_partition = "os-image",
854 .last_sysupgrade_partition = "file-system"
855 },
856
857 /** Firmware layout for the TL-WA855RE v1 */
858 {
859 .id = "TLWA855REV1",
860 .vendor = "",
861 .support_list =
862 "SupportList:\n"
863 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:00000000}\n"
864 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:55530000}\n"
865 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:45550000}\n"
866 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4B520000}\n"
867 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:42520000}\n"
868 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4A500000}\n"
869 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:43410000}\n"
870 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:41550000}\n"
871 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:52550000}\n",
872 .support_trail = '\x00',
873 .soft_ver = NULL,
874
875 .partitions = {
876 {"fs-uboot", 0x00000, 0x20000},
877 {"os-image", 0x20000, 0x150000},
878 {"file-system", 0x170000, 0x240000},
879 {"partition-table", 0x3b0000, 0x02000},
880 {"default-mac", 0x3c0000, 0x00020},
881 {"pin", 0x3c0100, 0x00020},
882 {"product-info", 0x3c1000, 0x01000},
883 {"soft-version", 0x3c2000, 0x00100},
884 {"support-list", 0x3c3000, 0x01000},
885 {"profile", 0x3c4000, 0x08000},
886 {"user-config", 0x3d0000, 0x10000},
887 {"default-config", 0x3e0000, 0x10000},
888 {"radio", 0x3f0000, 0x10000},
889 {NULL, 0, 0}
890 },
891
892 .first_sysupgrade_partition = "os-image",
893 .last_sysupgrade_partition = "file-system"
894 },
895
896 /** Firmware layout for the TL-WR1043 v5 */
897 {
898 .id = "TLWR1043NV5",
899 .vendor = "",
900 .support_list =
901 "SupportList:\n"
902 "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:45550000}\n"
903 "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:55530000}\n",
904 .support_trail = '\x00',
905 .soft_ver = "soft_ver:1.0.0\n",
906 .partitions = {
907 {"factory-boot", 0x00000, 0x20000},
908 {"fs-uboot", 0x20000, 0x20000},
909 {"firmware", 0x40000, 0xec0000},
910 {"default-mac", 0xf00000, 0x00200},
911 {"pin", 0xf00200, 0x00200},
912 {"device-id", 0xf00400, 0x00100},
913 {"product-info", 0xf00500, 0x0fb00},
914 {"soft-version", 0xf10000, 0x01000},
915 {"extra-para", 0xf11000, 0x01000},
916 {"support-list", 0xf12000, 0x0a000},
917 {"profile", 0xf1c000, 0x04000},
918 {"default-config", 0xf20000, 0x10000},
919 {"user-config", 0xf30000, 0x40000},
920 {"qos-db", 0xf70000, 0x40000},
921 {"certificate", 0xfb0000, 0x10000},
922 {"partition-table", 0xfc0000, 0x10000},
923 {"log", 0xfd0000, 0x20000},
924 {"radio", 0xff0000, 0x10000},
925 {NULL, 0, 0}
926 },
927 .first_sysupgrade_partition = "os-image",
928 .last_sysupgrade_partition = "file-system"
929 },
930
931 /** Firmware layout for the TL-WR1043 v4 */
932 {
933 .id = "TLWR1043NDV4",
934 .vendor = "",
935 .support_list =
936 "SupportList:\n"
937 "{product_name:TL-WR1043ND,product_ver:4.0.0,special_id:45550000}\n",
938 .support_trail = '\x00',
939 .soft_ver = NULL,
940
941 /* We're using a dynamic kernel/rootfs split here */
942 .partitions = {
943 {"fs-uboot", 0x00000, 0x20000},
944 {"firmware", 0x20000, 0xf30000},
945 {"default-mac", 0xf50000, 0x00200},
946 {"pin", 0xf50200, 0x00200},
947 {"product-info", 0xf50400, 0x0fc00},
948 {"soft-version", 0xf60000, 0x0b000},
949 {"support-list", 0xf6b000, 0x04000},
950 {"profile", 0xf70000, 0x04000},
951 {"default-config", 0xf74000, 0x0b000},
952 {"user-config", 0xf80000, 0x40000},
953 {"partition-table", 0xfc0000, 0x10000},
954 {"log", 0xfd0000, 0x20000},
955 {"radio", 0xff0000, 0x10000},
956 {NULL, 0, 0}
957 },
958
959 .first_sysupgrade_partition = "os-image",
960 .last_sysupgrade_partition = "file-system"
961 },
962
963 /** Firmware layout for the TL-WR902AC v1 */
964 {
965 .id = "TL-WR902AC-V1",
966 .vendor = "",
967 .support_list =
968 "SupportList:\n"
969 "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:45550000}\n"
970 "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:55530000}\n",
971 .support_trail = '\x00',
972 .soft_ver = NULL,
973
974 /**
975 384KB were moved from file-system to os-image
976 in comparison to the stock image
977 */
978 .partitions = {
979 {"fs-uboot", 0x00000, 0x20000},
980 {"firmware", 0x20000, 0x730000},
981 {"default-mac", 0x750000, 0x00200},
982 {"pin", 0x750200, 0x00200},
983 {"product-info", 0x750400, 0x0fc00},
984 {"soft-version", 0x760000, 0x0b000},
985 {"support-list", 0x76b000, 0x04000},
986 {"profile", 0x770000, 0x04000},
987 {"default-config", 0x774000, 0x0b000},
988 {"user-config", 0x780000, 0x40000},
989 {"partition-table", 0x7c0000, 0x10000},
990 {"log", 0x7d0000, 0x20000},
991 {"radio", 0x7f0000, 0x10000},
992 {NULL, 0, 0}
993 },
994
995 .first_sysupgrade_partition = "os-image",
996 .last_sysupgrade_partition = "file-system",
997 },
998
999 /** Firmware layout for the TL-WR942N V1 */
1000 {
1001 .id = "TLWR942NV1",
1002 .vendor = "",
1003 .support_list =
1004 "SupportList:\r\n"
1005 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:00000000}\r\n"
1006 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:52550000}\r\n",
1007 .support_trail = '\x00',
1008 .soft_ver = NULL,
1009
1010 .partitions = {
1011 {"fs-uboot", 0x00000, 0x20000},
1012 {"firmware", 0x20000, 0xe20000},
1013 {"default-mac", 0xe40000, 0x00200},
1014 {"pin", 0xe40200, 0x00200},
1015 {"product-info", 0xe40400, 0x0fc00},
1016 {"partition-table", 0xe50000, 0x10000},
1017 {"soft-version", 0xe60000, 0x10000},
1018 {"support-list", 0xe70000, 0x10000},
1019 {"profile", 0xe80000, 0x10000},
1020 {"default-config", 0xe90000, 0x10000},
1021 {"user-config", 0xea0000, 0x40000},
1022 {"qos-db", 0xee0000, 0x40000},
1023 {"certificate", 0xf20000, 0x10000},
1024 {"usb-config", 0xfb0000, 0x10000},
1025 {"log", 0xfc0000, 0x20000},
1026 {"radio-bk", 0xfe0000, 0x10000},
1027 {"radio", 0xff0000, 0x10000},
1028 {NULL, 0, 0}
1029 },
1030
1031 .first_sysupgrade_partition = "os-image",
1032 .last_sysupgrade_partition = "file-system",
1033 },
1034
1035 /** Firmware layout for the RE350 v1 */
1036 {
1037 .id = "RE350-V1",
1038 .vendor = "",
1039 .support_list =
1040 "SupportList:\n"
1041 "{product_name:RE350,product_ver:1.0.0,special_id:45550000}\n"
1042 "{product_name:RE350,product_ver:1.0.0,special_id:00000000}\n"
1043 "{product_name:RE350,product_ver:1.0.0,special_id:41550000}\n"
1044 "{product_name:RE350,product_ver:1.0.0,special_id:55530000}\n"
1045 "{product_name:RE350,product_ver:1.0.0,special_id:43410000}\n"
1046 "{product_name:RE350,product_ver:1.0.0,special_id:4b520000}\n"
1047 "{product_name:RE350,product_ver:1.0.0,special_id:4a500000}\n",
1048 .support_trail = '\x00',
1049 .soft_ver = NULL,
1050
1051 /** We're using a dynamic kernel/rootfs split here */
1052 .partitions = {
1053 {"fs-uboot", 0x00000, 0x20000},
1054 {"firmware", 0x20000, 0x5e0000},
1055 {"partition-table", 0x600000, 0x02000},
1056 {"default-mac", 0x610000, 0x00020},
1057 {"pin", 0x610100, 0x00020},
1058 {"product-info", 0x611100, 0x01000},
1059 {"soft-version", 0x620000, 0x01000},
1060 {"support-list", 0x621000, 0x01000},
1061 {"profile", 0x622000, 0x08000},
1062 {"user-config", 0x630000, 0x10000},
1063 {"default-config", 0x640000, 0x10000},
1064 {"radio", 0x7f0000, 0x10000},
1065 {NULL, 0, 0}
1066 },
1067
1068 .first_sysupgrade_partition = "os-image",
1069 .last_sysupgrade_partition = "file-system"
1070 },
1071
1072 /** Firmware layout for the RE355 */
1073 {
1074 .id = "RE355",
1075 .vendor = "",
1076 .support_list =
1077 "SupportList:\r\n"
1078 "{product_name:RE355,product_ver:1.0.0,special_id:00000000}\r\n"
1079 "{product_name:RE355,product_ver:1.0.0,special_id:55530000}\r\n"
1080 "{product_name:RE355,product_ver:1.0.0,special_id:45550000}\r\n"
1081 "{product_name:RE355,product_ver:1.0.0,special_id:4A500000}\r\n"
1082 "{product_name:RE355,product_ver:1.0.0,special_id:43410000}\r\n"
1083 "{product_name:RE355,product_ver:1.0.0,special_id:41550000}\r\n"
1084 "{product_name:RE355,product_ver:1.0.0,special_id:4B520000}\r\n"
1085 "{product_name:RE355,product_ver:1.0.0,special_id:55534100}\r\n",
1086 .support_trail = '\x00',
1087 .soft_ver = NULL,
1088
1089 /* We're using a dynamic kernel/rootfs split here */
1090 .partitions = {
1091 {"fs-uboot", 0x00000, 0x20000},
1092 {"firmware", 0x20000, 0x5e0000},
1093 {"partition-table", 0x600000, 0x02000},
1094 {"default-mac", 0x610000, 0x00020},
1095 {"pin", 0x610100, 0x00020},
1096 {"product-info", 0x611100, 0x01000},
1097 {"soft-version", 0x620000, 0x01000},
1098 {"support-list", 0x621000, 0x01000},
1099 {"profile", 0x622000, 0x08000},
1100 {"user-config", 0x630000, 0x10000},
1101 {"default-config", 0x640000, 0x10000},
1102 {"radio", 0x7f0000, 0x10000},
1103 {NULL, 0, 0}
1104 },
1105
1106 .first_sysupgrade_partition = "os-image",
1107 .last_sysupgrade_partition = "file-system"
1108 },
1109
1110 /** Firmware layout for the RE450 */
1111 {
1112 .id = "RE450",
1113 .vendor = "",
1114 .support_list =
1115 "SupportList:\r\n"
1116 "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n"
1117 "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n"
1118 "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n"
1119 "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n"
1120 "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n"
1121 "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n"
1122 "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n"
1123 "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n",
1124 .support_trail = '\x00',
1125 .soft_ver = NULL,
1126
1127 /** We're using a dynamic kernel/rootfs split here */
1128 .partitions = {
1129 {"fs-uboot", 0x00000, 0x20000},
1130 {"firmware", 0x20000, 0x5e0000},
1131 {"partition-table", 0x600000, 0x02000},
1132 {"default-mac", 0x610000, 0x00020},
1133 {"pin", 0x610100, 0x00020},
1134 {"product-info", 0x611100, 0x01000},
1135 {"soft-version", 0x620000, 0x01000},
1136 {"support-list", 0x621000, 0x01000},
1137 {"profile", 0x622000, 0x08000},
1138 {"user-config", 0x630000, 0x10000},
1139 {"default-config", 0x640000, 0x10000},
1140 {"radio", 0x7f0000, 0x10000},
1141 {NULL, 0, 0}
1142 },
1143
1144 .first_sysupgrade_partition = "os-image",
1145 .last_sysupgrade_partition = "file-system"
1146 },
1147
1148 /** Firmware layout for the RE450 v2 */
1149 {
1150 .id = "RE450-V2",
1151 .vendor = "",
1152 .support_list =
1153 "SupportList:\r\n"
1154 "{product_name:RE450,product_ver:2.0.0,special_id:00000000}\r\n"
1155 "{product_name:RE450,product_ver:2.0.0,special_id:55530000}\r\n"
1156 "{product_name:RE450,product_ver:2.0.0,special_id:45550000}\r\n"
1157 "{product_name:RE450,product_ver:2.0.0,special_id:4A500000}\r\n"
1158 "{product_name:RE450,product_ver:2.0.0,special_id:43410000}\r\n"
1159 "{product_name:RE450,product_ver:2.0.0,special_id:41550000}\r\n"
1160 "{product_name:RE450,product_ver:2.0.0,special_id:41530000}\r\n"
1161 "{product_name:RE450,product_ver:2.0.0,special_id:4B520000}\r\n"
1162 "{product_name:RE450,product_ver:2.0.0,special_id:42520000}\r\n",
1163 .support_trail = '\x00',
1164 .soft_ver = NULL,
1165
1166 /* We're using a dynamic kernel/rootfs split here */
1167 .partitions = {
1168 {"fs-uboot", 0x00000, 0x20000},
1169 {"firmware", 0x20000, 0x5e0000},
1170 {"partition-table", 0x600000, 0x02000},
1171 {"default-mac", 0x610000, 0x00020},
1172 {"pin", 0x610100, 0x00020},
1173 {"product-info", 0x611100, 0x01000},
1174 {"soft-version", 0x620000, 0x01000},
1175 {"support-list", 0x621000, 0x01000},
1176 {"profile", 0x622000, 0x08000},
1177 {"user-config", 0x630000, 0x10000},
1178 {"default-config", 0x640000, 0x10000},
1179 {"radio", 0x7f0000, 0x10000},
1180
1181 {NULL, 0, 0}
1182 },
1183
1184 .first_sysupgrade_partition = "os-image",
1185 .last_sysupgrade_partition = "file-system"
1186 },
1187
1188 {}
1189 };
1190
1191 #define error(_ret, _errno, _str, ...) \
1192 do { \
1193 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
1194 strerror(_errno)); \
1195 if (_ret) \
1196 exit(_ret); \
1197 } while (0)
1198
1199
1200 /** Stores a uint32 as big endian */
1201 static inline void put32(uint8_t *buf, uint32_t val) {
1202 buf[0] = val >> 24;
1203 buf[1] = val >> 16;
1204 buf[2] = val >> 8;
1205 buf[3] = val;
1206 }
1207
1208 /** Allocates a new image partition */
1209 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
1210 struct image_partition_entry entry = {name, len, malloc(len)};
1211 if (!entry.data)
1212 error(1, errno, "malloc");
1213
1214 return entry;
1215 }
1216
1217 /** Frees an image partition */
1218 static void free_image_partition(struct image_partition_entry entry) {
1219 free(entry.data);
1220 }
1221
1222 static time_t source_date_epoch = -1;
1223 static void set_source_date_epoch() {
1224 char *env = getenv("SOURCE_DATE_EPOCH");
1225 char *endptr = env;
1226 errno = 0;
1227 if (env && *env) {
1228 source_date_epoch = strtoull(env, &endptr, 10);
1229 if (errno || (endptr && *endptr != '\0')) {
1230 fprintf(stderr, "Invalid SOURCE_DATE_EPOCH");
1231 exit(1);
1232 }
1233 }
1234 }
1235
1236 /** Generates the partition-table partition */
1237 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
1238 struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
1239
1240 char *s = (char *)entry.data, *end = (char *)(s+entry.size);
1241
1242 *(s++) = 0x00;
1243 *(s++) = 0x04;
1244 *(s++) = 0x00;
1245 *(s++) = 0x00;
1246
1247 size_t i;
1248 for (i = 0; p[i].name; i++) {
1249 size_t len = end-s;
1250 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
1251
1252 if (w > len-1)
1253 error(1, 0, "flash partition table overflow?");
1254
1255 s += w;
1256 }
1257
1258 s++;
1259
1260 memset(s, 0xff, end-s);
1261
1262 return entry;
1263 }
1264
1265
1266 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
1267 static inline uint8_t bcd(uint8_t v) {
1268 return 0x10 * (v/10) + v%10;
1269 }
1270
1271
1272 /** Generates the soft-version partition */
1273 static struct image_partition_entry make_soft_version(uint32_t rev) {
1274 struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
1275 struct soft_version *s = (struct soft_version *)entry.data;
1276
1277 time_t t;
1278
1279 if (source_date_epoch != -1)
1280 t = source_date_epoch;
1281 else if (time(&t) == (time_t)(-1))
1282 error(1, errno, "time");
1283
1284 struct tm *tm = localtime(&t);
1285
1286 s->magic = htonl(0x0000000c);
1287 s->zero = 0;
1288 s->pad1 = 0xff;
1289
1290 s->version_major = 0;
1291 s->version_minor = 0;
1292 s->version_patch = 0;
1293
1294 s->year_hi = bcd((1900+tm->tm_year)/100);
1295 s->year_lo = bcd(tm->tm_year%100);
1296 s->month = bcd(tm->tm_mon+1);
1297 s->day = bcd(tm->tm_mday);
1298 s->rev = htonl(rev);
1299
1300 s->pad2 = 0xff;
1301
1302 return entry;
1303 }
1304
1305 static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) {
1306 /** String length _including_ the terminating zero byte */
1307 uint32_t ver_len = strlen(soft_ver) + 1;
1308 /** Partition contains 64 bit header, the version string, and one additional null byte */
1309 size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1;
1310 struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len);
1311
1312 uint32_t *len = (uint32_t *)entry.data;
1313 len[0] = htonl(ver_len);
1314 len[1] = 0;
1315 memcpy(&len[2], soft_ver, ver_len);
1316
1317 entry.data[partition_len - 1] = 0;
1318
1319 return entry;
1320 }
1321
1322 /** Generates the support-list partition */
1323 static struct image_partition_entry make_support_list(struct device_info *info) {
1324 size_t len = strlen(info->support_list);
1325 struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
1326
1327 put32(entry.data, len);
1328 memset(entry.data+4, 0, 4);
1329 memcpy(entry.data+8, info->support_list, len);
1330 entry.data[len+8] = info->support_trail;
1331
1332 return entry;
1333 }
1334
1335 /** Creates a new image partition with an arbitrary name from a file */
1336 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof, struct flash_partition_entry *file_system_partition) {
1337 struct stat statbuf;
1338
1339 if (stat(filename, &statbuf) < 0)
1340 error(1, errno, "unable to stat file `%s'", filename);
1341
1342 size_t len = statbuf.st_size;
1343
1344 if (add_jffs2_eof)
1345 if (file_system_partition)
1346 len = ALIGN(len + file_system_partition->base, 0x10000) + sizeof(jffs2_eof_mark) - file_system_partition->base;
1347 else
1348 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
1349
1350 struct image_partition_entry entry = alloc_image_partition(part_name, len);
1351
1352 FILE *file = fopen(filename, "rb");
1353 if (!file)
1354 error(1, errno, "unable to open file `%s'", filename);
1355
1356 if (fread(entry.data, statbuf.st_size, 1, file) != 1)
1357 error(1, errno, "unable to read file `%s'", filename);
1358
1359 if (add_jffs2_eof) {
1360 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
1361
1362 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
1363 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
1364 }
1365
1366 fclose(file);
1367
1368 return entry;
1369 }
1370
1371 /** Creates a new image partition from arbitrary data */
1372 static struct image_partition_entry put_data(const char *part_name, const char *datain, size_t len) {
1373
1374 struct image_partition_entry entry = alloc_image_partition(part_name, len);
1375
1376 memcpy(entry.data, datain, len);
1377
1378 return entry;
1379 }
1380
1381 /**
1382 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
1383
1384 Example image partition table:
1385
1386 fwup-ptn partition-table base 0x00800 size 0x00800
1387 fwup-ptn os-image base 0x01000 size 0x113b45
1388 fwup-ptn file-system base 0x114b45 size 0x1d0004
1389 fwup-ptn support-list base 0x2e4b49 size 0x000d1
1390
1391 Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
1392 the end of the partition table is marked with a zero byte.
1393
1394 The firmware image must contain at least the partition-table and support-list partitions
1395 to be accepted. There aren't any alignment constraints for the image partitions.
1396
1397 The partition-table partition contains the actual flash layout; partitions
1398 from the image partition table are mapped to the corresponding flash partitions during
1399 the firmware upgrade. The support-list partition contains a list of devices supported by
1400 the firmware image.
1401
1402 The base offsets in the firmware partition table are relative to the end
1403 of the vendor information block, so the partition-table partition will
1404 actually start at offset 0x1814 of the image.
1405
1406 I think partition-table must be the first partition in the firmware image.
1407 */
1408 static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) {
1409 size_t i, j;
1410 char *image_pt = (char *)buffer, *end = image_pt + 0x800;
1411
1412 size_t base = 0x800;
1413 for (i = 0; parts[i].name; i++) {
1414 for (j = 0; flash_parts[j].name; j++) {
1415 if (!strcmp(flash_parts[j].name, parts[i].name)) {
1416 if (parts[i].size > flash_parts[j].size)
1417 error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size);
1418 break;
1419 }
1420 }
1421
1422 assert(flash_parts[j].name);
1423
1424 memcpy(buffer + base, parts[i].data, parts[i].size);
1425
1426 size_t len = end-image_pt;
1427 size_t w = snprintf(image_pt, len, "fwup-ptn %s base 0x%05x size 0x%05x\t\r\n", parts[i].name, (unsigned)base, (unsigned)parts[i].size);
1428
1429 if (w > len-1)
1430 error(1, 0, "image partition table overflow?");
1431
1432 image_pt += w;
1433
1434 base += parts[i].size;
1435 }
1436 }
1437
1438 /** Generates and writes the image MD5 checksum */
1439 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
1440 MD5_CTX ctx;
1441
1442 MD5_Init(&ctx);
1443 MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
1444 MD5_Update(&ctx, buffer, len);
1445 MD5_Final(md5, &ctx);
1446 }
1447
1448
1449 /**
1450 Generates the firmware image in factory format
1451
1452 Image format:
1453
1454 Bytes (hex) Usage
1455 ----------- -----
1456 0000-0003 Image size (4 bytes, big endian)
1457 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
1458 0014-0017 Vendor information length (without padding) (4 bytes, big endian)
1459 0018-1013 Vendor information (4092 bytes, padded with 0xff; there seem to be older
1460 (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
1461 1014-1813 Image partition table (2048 bytes, padded with 0xff)
1462 1814-xxxx Firmware partitions
1463 */
1464 static void * generate_factory_image(struct device_info *info, const struct image_partition_entry *parts, size_t *len) {
1465 *len = 0x1814;
1466
1467 size_t i;
1468 for (i = 0; parts[i].name; i++)
1469 *len += parts[i].size;
1470
1471 uint8_t *image = malloc(*len);
1472 if (!image)
1473 error(1, errno, "malloc");
1474
1475 memset(image, 0xff, *len);
1476 put32(image, *len);
1477
1478 if (info->vendor) {
1479 size_t vendor_len = strlen(info->vendor);
1480 put32(image+0x14, vendor_len);
1481 memcpy(image+0x18, info->vendor, vendor_len);
1482 }
1483
1484 put_partitions(image + 0x1014, info->partitions, parts);
1485 put_md5(image+0x04, image+0x14, *len-0x14);
1486
1487 return image;
1488 }
1489
1490 /**
1491 Generates the firmware image in sysupgrade format
1492
1493 This makes some assumptions about the provided flash and image partition tables and
1494 should be generalized when TP-LINK starts building its safeloader into hardware with
1495 different flash layouts.
1496 */
1497 static void * generate_sysupgrade_image(struct device_info *info, const struct image_partition_entry *image_parts, size_t *len) {
1498 size_t i, j;
1499 size_t flash_first_partition_index = 0;
1500 size_t flash_last_partition_index = 0;
1501 const struct flash_partition_entry *flash_first_partition = NULL;
1502 const struct flash_partition_entry *flash_last_partition = NULL;
1503 const struct image_partition_entry *image_last_partition = NULL;
1504
1505 /** Find first and last partitions */
1506 for (i = 0; info->partitions[i].name; i++) {
1507 if (!strcmp(info->partitions[i].name, info->first_sysupgrade_partition)) {
1508 flash_first_partition = &info->partitions[i];
1509 flash_first_partition_index = i;
1510 } else if (!strcmp(info->partitions[i].name, info->last_sysupgrade_partition)) {
1511 flash_last_partition = &info->partitions[i];
1512 flash_last_partition_index = i;
1513 }
1514 }
1515
1516 assert(flash_first_partition && flash_last_partition);
1517 assert(flash_first_partition_index < flash_last_partition_index);
1518
1519 /** Find last partition from image to calculate needed size */
1520 for (i = 0; image_parts[i].name; i++) {
1521 if (!strcmp(image_parts[i].name, info->last_sysupgrade_partition)) {
1522 image_last_partition = &image_parts[i];
1523 break;
1524 }
1525 }
1526
1527 assert(image_last_partition);
1528
1529 *len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size;
1530
1531 uint8_t *image = malloc(*len);
1532 if (!image)
1533 error(1, errno, "malloc");
1534
1535 memset(image, 0xff, *len);
1536
1537 for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) {
1538 for (j = 0; image_parts[j].name; j++) {
1539 if (!strcmp(info->partitions[i].name, image_parts[j].name)) {
1540 if (image_parts[j].size > info->partitions[i].size)
1541 error(1, 0, "%s partition too big (more than %u bytes)", info->partitions[i].name, (unsigned)info->partitions[i].size);
1542 memcpy(image + info->partitions[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size);
1543 break;
1544 }
1545
1546 assert(image_parts[j].name);
1547 }
1548 }
1549
1550 return image;
1551 }
1552
1553 /** Generates an image according to a given layout and writes it to a file */
1554 static void build_image(const char *output,
1555 const char *kernel_image,
1556 const char *rootfs_image,
1557 uint32_t rev,
1558 bool add_jffs2_eof,
1559 bool sysupgrade,
1560 struct device_info *info) {
1561
1562 size_t i;
1563
1564 struct image_partition_entry parts[7] = {};
1565
1566 struct flash_partition_entry *firmware_partition = NULL;
1567 struct flash_partition_entry *os_image_partition = NULL;
1568 struct flash_partition_entry *file_system_partition = NULL;
1569 size_t firmware_partition_index = 0;
1570
1571 for (i = 0; info->partitions[i].name; i++) {
1572 if (!strcmp(info->partitions[i].name, "firmware"))
1573 {
1574 firmware_partition = &info->partitions[i];
1575 firmware_partition_index = i;
1576 }
1577 }
1578
1579 if (firmware_partition)
1580 {
1581 os_image_partition = &info->partitions[firmware_partition_index];
1582 file_system_partition = &info->partitions[firmware_partition_index + 1];
1583
1584 struct stat kernel;
1585 if (stat(kernel_image, &kernel) < 0)
1586 error(1, errno, "unable to stat file `%s'", kernel_image);
1587
1588 if (kernel.st_size > firmware_partition->size)
1589 error(1, 0, "kernel overflowed firmware partition\n");
1590
1591 for (i = MAX_PARTITIONS-1; i >= firmware_partition_index + 1; i--)
1592 info->partitions[i+1] = info->partitions[i];
1593
1594 file_system_partition->name = "file-system";
1595 file_system_partition->base = firmware_partition->base + kernel.st_size;
1596
1597 /* Align partition start to erase blocks for factory images only */
1598 if (!sysupgrade)
1599 file_system_partition->base = ALIGN(firmware_partition->base + kernel.st_size, 0x10000);
1600
1601 file_system_partition->size = firmware_partition->size - file_system_partition->base;
1602
1603 os_image_partition->name = "os-image";
1604 os_image_partition->size = kernel.st_size;
1605 }
1606
1607 parts[0] = make_partition_table(info->partitions);
1608 if (info->soft_ver)
1609 parts[1] = make_soft_version_from_string(info->soft_ver);
1610 else
1611 parts[1] = make_soft_version(rev);
1612
1613 parts[2] = make_support_list(info);
1614 parts[3] = read_file("os-image", kernel_image, false, NULL);
1615 parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof, file_system_partition);
1616
1617 /* Some devices need the extra-para partition to accept the firmware */
1618 if (strcasecmp(info->id, "ARCHER-C25-V1") == 0 ||
1619 strcasecmp(info->id, "ARCHER-C59-V2") == 0 ||
1620 strcasecmp(info->id, "ARCHER-C60-V2") == 0 ||
1621 strcasecmp(info->id, "TLWR1043NV5") == 0) {
1622 const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
1623 parts[5] = put_data("extra-para", mdat, 11);
1624 } else if (strcasecmp(info->id, "ARCHER-A7-V5") == 0 || strcasecmp(info->id, "ARCHER-C7-V4") == 0 || strcasecmp(info->id, "ARCHER-C7-V5") == 0) {
1625 const char mdat[11] = {0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x01, 0x00, 0x00};
1626 parts[5] = put_data("extra-para", mdat, 11);
1627 }
1628
1629 size_t len;
1630 void *image;
1631 if (sysupgrade)
1632 image = generate_sysupgrade_image(info, parts, &len);
1633 else
1634 image = generate_factory_image(info, parts, &len);
1635
1636 FILE *file = fopen(output, "wb");
1637 if (!file)
1638 error(1, errno, "unable to open output file");
1639
1640 if (fwrite(image, len, 1, file) != 1)
1641 error(1, 0, "unable to write output file");
1642
1643 fclose(file);
1644
1645 free(image);
1646
1647 for (i = 0; parts[i].name; i++)
1648 free_image_partition(parts[i]);
1649 }
1650
1651 /** Usage output */
1652 static void usage(const char *argv0) {
1653 fprintf(stderr,
1654 "Usage: %s [OPTIONS...]\n"
1655 "\n"
1656 "Options:\n"
1657 " -h show this help\n"
1658 "\n"
1659 "Create a new image:\n"
1660 " -B <board> create image for the board specified with <board>\n"
1661 " -k <file> read kernel image from the file <file>\n"
1662 " -r <file> read rootfs image from the file <file>\n"
1663 " -o <file> write output to the file <file>\n"
1664 " -V <rev> sets the revision number to <rev>\n"
1665 " -j add jffs2 end-of-filesystem markers\n"
1666 " -S create sysupgrade instead of factory image\n"
1667 "Extract an old image:\n"
1668 " -x <file> extract all oem firmware partition\n"
1669 " -d <dir> destination to extract the firmware partition\n"
1670 " -z <file> convert an oem firmware into a sysupgade file. Use -o for output file\n",
1671 argv0
1672 );
1673 };
1674
1675
1676 static struct device_info *find_board(const char *id)
1677 {
1678 struct device_info *board = NULL;
1679
1680 for (board = boards; board->id != NULL; board++)
1681 if (strcasecmp(id, board->id) == 0)
1682 return board;
1683
1684 return NULL;
1685 }
1686
1687 static int add_flash_partition(
1688 struct flash_partition_entry *part_list,
1689 size_t max_entries,
1690 const char *name,
1691 unsigned long base,
1692 unsigned long size)
1693 {
1694 int ptr;
1695 /* check if the list has a free entry */
1696 for (ptr = 0; ptr < max_entries; ptr++, part_list++) {
1697 if (part_list->name == NULL &&
1698 part_list->base == 0 &&
1699 part_list->size == 0)
1700 break;
1701 }
1702
1703 if (ptr == max_entries) {
1704 error(1, 0, "No free flash part entry available.");
1705 }
1706
1707 part_list->name = calloc(1, strlen(name) + 1);
1708 if (!part_list->name) {
1709 error(1, 0, "Unable to allocate memory");
1710 }
1711
1712 memcpy((char *)part_list->name, name, strlen(name));
1713 part_list->base = base;
1714 part_list->size = size;
1715
1716 return 0;
1717 }
1718
1719 /** read the partition table into struct flash_partition_entry */
1720 static int read_partition_table(
1721 FILE *file, long offset,
1722 struct flash_partition_entry *entries, size_t max_entries,
1723 int type)
1724 {
1725 char buf[2048];
1726 char *ptr, *end;
1727 const char *parthdr = NULL;
1728 const char *fwuphdr = "fwup-ptn";
1729 const char *flashhdr = "partition";
1730
1731 /* TODO: search for the partition table */
1732
1733 switch(type) {
1734 case 0:
1735 parthdr = fwuphdr;
1736 break;
1737 case 1:
1738 parthdr = flashhdr;
1739 break;
1740 default:
1741 error(1, 0, "Invalid partition table");
1742 }
1743
1744 if (fseek(file, offset, SEEK_SET) < 0)
1745 error(1, errno, "Can not seek in the firmware");
1746
1747 if (fread(buf, 1, 2048, file) < 0)
1748 error(1, errno, "Can not read fwup-ptn from the firmware");
1749
1750 buf[2047] = '\0';
1751
1752 /* look for the partition header */
1753 if (memcmp(buf, parthdr, strlen(parthdr)) != 0) {
1754 fprintf(stderr, "DEBUG: can not find fwuphdr\n");
1755 return 1;
1756 }
1757
1758 ptr = buf;
1759 end = buf + sizeof(buf);
1760 while ((ptr + strlen(parthdr)) < end &&
1761 memcmp(ptr, parthdr, strlen(parthdr)) == 0) {
1762 char *end_part;
1763 char *end_element;
1764
1765 char name[32] = { 0 };
1766 int name_len = 0;
1767 unsigned long base = 0;
1768 unsigned long size = 0;
1769
1770 end_part = memchr(ptr, '\n', (end - ptr));
1771 if (end_part == NULL) {
1772 /* in theory this should never happen, because a partition always ends with 0x09, 0x0D, 0x0A */
1773 break;
1774 }
1775
1776 for (int i = 0; i <= 4; i++) {
1777 if (end_part <= ptr)
1778 break;
1779
1780 end_element = memchr(ptr, 0x20, (end_part - ptr));
1781 if (end_element == NULL) {
1782 error(1, errno, "Ignoring the rest of the partition entries.");
1783 break;
1784 }
1785
1786 switch (i) {
1787 /* partition header */
1788 case 0:
1789 ptr = end_element + 1;
1790 continue;
1791 /* name */
1792 case 1:
1793 name_len = (end_element - ptr) > 31 ? 31 : (end_element - ptr);
1794 strncpy(name, ptr, name_len);
1795 name[name_len] = '\0';
1796 ptr = end_element + 1;
1797 continue;
1798
1799 /* string "base" */
1800 case 2:
1801 ptr = end_element + 1;
1802 continue;
1803
1804 /* actual base */
1805 case 3:
1806 base = strtoul(ptr, NULL, 16);
1807 ptr = end_element + 1;
1808 continue;
1809
1810 /* string "size" */
1811 case 4:
1812 ptr = end_element + 1;
1813 /* actual size. The last element doesn't have a sepeartor */
1814 size = strtoul(ptr, NULL, 16);
1815 /* the part ends with 0x09, 0x0d, 0x0a */
1816 ptr = end_part + 1;
1817 add_flash_partition(entries, max_entries, name, base, size);
1818 continue;
1819 }
1820 }
1821 }
1822
1823 return 0;
1824 }
1825
1826 static void write_partition(
1827 FILE *input_file,
1828 size_t firmware_offset,
1829 struct flash_partition_entry *entry,
1830 FILE *output_file)
1831 {
1832 char buf[4096];
1833 size_t offset;
1834
1835 fseek(input_file, entry->base + firmware_offset, SEEK_SET);
1836
1837 for (offset = 0; sizeof(buf) + offset <= entry->size; offset += sizeof(buf)) {
1838 if (fread(buf, sizeof(buf), 1, input_file) < 0)
1839 error(1, errno, "Can not read partition from input_file");
1840
1841 if (fwrite(buf, sizeof(buf), 1, output_file) < 0)
1842 error(1, errno, "Can not write partition to output_file");
1843 }
1844 /* write last chunk smaller than buffer */
1845 if (offset < entry->size) {
1846 offset = entry->size - offset;
1847 if (fread(buf, offset, 1, input_file) < 0)
1848 error(1, errno, "Can not read partition from input_file");
1849 if (fwrite(buf, offset, 1, output_file) < 0)
1850 error(1, errno, "Can not write partition to output_file");
1851 }
1852 }
1853
1854 static int extract_firmware_partition(FILE *input_file, size_t firmware_offset, struct flash_partition_entry *entry, const char *output_directory)
1855 {
1856 FILE *output_file;
1857 char output[PATH_MAX];
1858
1859 snprintf(output, PATH_MAX, "%s/%s", output_directory, entry->name);
1860 output_file = fopen(output, "wb+");
1861 if (output_file == NULL) {
1862 error(1, errno, "Can not open output file %s", output);
1863 }
1864
1865 write_partition(input_file, firmware_offset, entry, output_file);
1866
1867 fclose(output_file);
1868
1869 return 0;
1870 }
1871
1872 /** extract all partitions from the firmware file */
1873 static int extract_firmware(const char *input, const char *output_directory)
1874 {
1875 struct flash_partition_entry entries[16] = { 0 };
1876 size_t max_entries = 16;
1877 size_t firmware_offset = 0x1014;
1878 FILE *input_file;
1879
1880 struct stat statbuf;
1881
1882 /* check input file */
1883 if (stat(input, &statbuf)) {
1884 error(1, errno, "Can not read input firmware %s", input);
1885 }
1886
1887 /* check if output directory exists */
1888 if (stat(output_directory, &statbuf)) {
1889 error(1, errno, "Failed to stat output directory %s", output_directory);
1890 }
1891
1892 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
1893 error(1, errno, "Given output directory is not a directory %s", output_directory);
1894 }
1895
1896 input_file = fopen(input, "rb");
1897
1898 if (read_partition_table(input_file, firmware_offset, entries, 16, 0) != 0) {
1899 error(1, 0, "Error can not read the partition table (fwup-ptn)");
1900 }
1901
1902 for (int i = 0; i < max_entries; i++) {
1903 if (entries[i].name == NULL &&
1904 entries[i].base == 0 &&
1905 entries[i].size == 0)
1906 continue;
1907
1908 extract_firmware_partition(input_file, firmware_offset, &entries[i], output_directory);
1909 }
1910
1911 return 0;
1912 }
1913
1914 static struct flash_partition_entry *find_partition(
1915 struct flash_partition_entry *entries, size_t max_entries,
1916 const char *name, const char *error_msg)
1917 {
1918 for (int i = 0; i < max_entries; i++, entries++) {
1919 if (strcmp(entries->name, name) == 0)
1920 return entries;
1921 }
1922
1923 error(1, 0, "%s", error_msg);
1924 return NULL;
1925 }
1926
1927 static void write_ff(FILE *output_file, size_t size)
1928 {
1929 char buf[4096];
1930 int offset;
1931
1932 memset(buf, 0xff, sizeof(buf));
1933
1934 for (offset = 0; offset + sizeof(buf) < size ; offset += sizeof(buf)) {
1935 if (fwrite(buf, sizeof(buf), 1, output_file) < 0)
1936 error(1, errno, "Can not write 0xff to output_file");
1937 }
1938
1939 /* write last chunk smaller than buffer */
1940 if (offset < size) {
1941 offset = size - offset;
1942 if (fwrite(buf, offset, 1, output_file) < 0)
1943 error(1, errno, "Can not write partition to output_file");
1944 }
1945 }
1946
1947 static void convert_firmware(const char *input, const char *output)
1948 {
1949 struct flash_partition_entry fwup[MAX_PARTITIONS] = { 0 };
1950 struct flash_partition_entry flash[MAX_PARTITIONS] = { 0 };
1951 struct flash_partition_entry *fwup_os_image = NULL, *fwup_file_system = NULL;
1952 struct flash_partition_entry *flash_os_image = NULL, *flash_file_system = NULL;
1953 struct flash_partition_entry *fwup_partition_table = NULL;
1954 size_t firmware_offset = 0x1014;
1955 FILE *input_file, *output_file;
1956
1957 struct stat statbuf;
1958
1959 /* check input file */
1960 if (stat(input, &statbuf)) {
1961 error(1, errno, "Can not read input firmware %s", input);
1962 }
1963
1964 input_file = fopen(input, "rb");
1965 if (!input_file)
1966 error(1, 0, "Can not open input firmware %s", input);
1967
1968 output_file = fopen(output, "wb");
1969 if (!output_file)
1970 error(1, 0, "Can not open output firmware %s", output);
1971
1972 if (read_partition_table(input_file, firmware_offset, fwup, MAX_PARTITIONS, 0) != 0) {
1973 error(1, 0, "Error can not read the partition table (fwup-ptn)");
1974 }
1975
1976 fwup_os_image = find_partition(fwup, MAX_PARTITIONS,
1977 "os-image", "Error can not find os-image partition (fwup)");
1978 fwup_file_system = find_partition(fwup, MAX_PARTITIONS,
1979 "file-system", "Error can not find file-system partition (fwup)");
1980 fwup_partition_table = find_partition(fwup, MAX_PARTITIONS,
1981 "partition-table", "Error can not find partition-table partition");
1982
1983 /* the flash partition table has a 0x00000004 magic haeder */
1984 if (read_partition_table(input_file, firmware_offset + fwup_partition_table->base + 4, flash, MAX_PARTITIONS, 1) != 0)
1985 error(1, 0, "Error can not read the partition table (flash)");
1986
1987 flash_os_image = find_partition(flash, MAX_PARTITIONS,
1988 "os-image", "Error can not find os-image partition (flash)");
1989 flash_file_system = find_partition(flash, MAX_PARTITIONS,
1990 "file-system", "Error can not find file-system partition (flash)");
1991
1992 /* write os_image to 0x0 */
1993 write_partition(input_file, firmware_offset, fwup_os_image, output_file);
1994 write_ff(output_file, flash_os_image->size - fwup_os_image->size);
1995
1996 /* write file-system behind os_image */
1997 fseek(output_file, flash_file_system->base - flash_os_image->base, SEEK_SET);
1998 write_partition(input_file, firmware_offset, fwup_file_system, output_file);
1999 write_ff(output_file, flash_file_system->size - fwup_file_system->size);
2000
2001 fclose(output_file);
2002 fclose(input_file);
2003 }
2004
2005 int main(int argc, char *argv[]) {
2006 const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
2007 const char *extract_image = NULL, *output_directory = NULL, *convert_image = NULL;
2008 bool add_jffs2_eof = false, sysupgrade = false;
2009 unsigned rev = 0;
2010 struct device_info *info;
2011 set_source_date_epoch();
2012
2013 while (true) {
2014 int c;
2015
2016 c = getopt(argc, argv, "B:k:r:o:V:jSh:x:d:z:");
2017 if (c == -1)
2018 break;
2019
2020 switch (c) {
2021 case 'B':
2022 board = optarg;
2023 break;
2024
2025 case 'k':
2026 kernel_image = optarg;
2027 break;
2028
2029 case 'r':
2030 rootfs_image = optarg;
2031 break;
2032
2033 case 'o':
2034 output = optarg;
2035 break;
2036
2037 case 'V':
2038 sscanf(optarg, "r%u", &rev);
2039 break;
2040
2041 case 'j':
2042 add_jffs2_eof = true;
2043 break;
2044
2045 case 'S':
2046 sysupgrade = true;
2047 break;
2048
2049 case 'h':
2050 usage(argv[0]);
2051 return 0;
2052
2053 case 'd':
2054 output_directory = optarg;
2055 break;
2056
2057 case 'x':
2058 extract_image = optarg;
2059 break;
2060
2061 case 'z':
2062 convert_image = optarg;
2063 break;
2064
2065 default:
2066 usage(argv[0]);
2067 return 1;
2068 }
2069 }
2070
2071 if (extract_image || output_directory) {
2072 if (!extract_image)
2073 error(1, 0, "No factory/oem image given via -x <file>. Output directory is only valid with -x");
2074 if (!output_directory)
2075 error(1, 0, "Can not extract an image without output directory. Use -d <dir>");
2076 extract_firmware(extract_image, output_directory);
2077 } else if (convert_image) {
2078 if (!output)
2079 error(1, 0, "Can not convert a factory/oem image into sysupgrade image without output file. Use -o <file>");
2080 convert_firmware(convert_image, output);
2081 } else {
2082 if (!board)
2083 error(1, 0, "no board has been specified");
2084 if (!kernel_image)
2085 error(1, 0, "no kernel image has been specified");
2086 if (!rootfs_image)
2087 error(1, 0, "no rootfs image has been specified");
2088 if (!output)
2089 error(1, 0, "no output filename has been specified");
2090
2091 info = find_board(board);
2092
2093 if (info == NULL)
2094 error(1, 0, "unsupported board %s", board);
2095
2096 build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
2097 }
2098
2099 return 0;
2100 }