2 Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
30 Image generation tool for the TP-LINK SafeLoader as seen on
31 TP-LINK Pharos devices (CPE210/220/510/520)
45 #include <arpa/inet.h>
47 #include <sys/types.h>
54 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
57 #define MAX_PARTITIONS 32
59 /** An image partition table entry */
60 struct image_partition_entry
{
66 /** A flash partition table entry */
67 struct flash_partition_entry
{
73 /** Firmware layout description */
77 const char *support_list
;
80 struct flash_partition_entry partitions
[MAX_PARTITIONS
+1];
81 const char *first_sysupgrade_partition
;
82 const char *last_sysupgrade_partition
;
85 /** The content of the soft-version structure */
86 struct __attribute__((__packed__
)) soft_version
{
90 uint8_t version_major
;
91 uint8_t version_minor
;
92 uint8_t version_patch
;
102 static const uint8_t jffs2_eof_mark
[4] = {0xde, 0xad, 0xc0, 0xde};
106 Salt for the MD5 hash
108 Fortunately, TP-LINK seems to use the same salt for most devices which use
109 the new image format.
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,
119 /** Firmware layout table */
120 static struct device_info boards
[] = {
121 /** Firmware layout for the CPE210/220 */
124 .vendor
= "CPE510(TP-LINK|UN|N300-5):1.0\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',
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},
154 .first_sysupgrade_partition
= "os-image",
155 .last_sysupgrade_partition
= "support-list",
158 /** Firmware layout for the CPE210 V2 */
161 .vendor
= "CPE210(TP-LINK|UN|N300-2|00000000):2.0\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',
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},
196 .first_sysupgrade_partition
= "os-image",
197 .last_sysupgrade_partition
= "support-list",
200 /** Firmware layout for the CPE510/520 */
203 .vendor
= "CPE510(TP-LINK|UN|N300-5):1.0\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 .support_trail
= '\xff',
218 {"fs-uboot", 0x00000, 0x20000},
219 {"partition-table", 0x20000, 0x02000},
220 {"default-mac", 0x30000, 0x00020},
221 {"product-info", 0x31100, 0x00100},
222 {"signature", 0x32000, 0x00400},
223 {"os-image", 0x40000, 0x1c0000},
224 {"file-system", 0x200000, 0x5b0000},
225 {"soft-version", 0x7b0000, 0x00100},
226 {"support-list", 0x7b1000, 0x00400},
227 {"user-config", 0x7c0000, 0x10000},
228 {"default-config", 0x7d0000, 0x10000},
229 {"log", 0x7e0000, 0x10000},
230 {"radio", 0x7f0000, 0x10000},
234 .first_sysupgrade_partition
= "os-image",
235 .last_sysupgrade_partition
= "support-list",
240 .vendor
= "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
243 "WBS210(TP-LINK|UN|N300-2):1.20\r\n"
244 "WBS210(TP-LINK|US|N300-2):1.20\r\n"
245 "WBS210(TP-LINK|EU|N300-2):1.20\r\n",
246 .support_trail
= '\xff',
250 {"fs-uboot", 0x00000, 0x20000},
251 {"partition-table", 0x20000, 0x02000},
252 {"default-mac", 0x30000, 0x00020},
253 {"product-info", 0x31100, 0x00100},
254 {"signature", 0x32000, 0x00400},
255 {"os-image", 0x40000, 0x1c0000},
256 {"file-system", 0x200000, 0x5b0000},
257 {"soft-version", 0x7b0000, 0x00100},
258 {"support-list", 0x7b1000, 0x00400},
259 {"user-config", 0x7c0000, 0x10000},
260 {"default-config", 0x7d0000, 0x10000},
261 {"log", 0x7e0000, 0x10000},
262 {"radio", 0x7f0000, 0x10000},
266 .first_sysupgrade_partition
= "os-image",
267 .last_sysupgrade_partition
= "support-list",
272 .vendor
= "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
275 "WBS510(TP-LINK|UN|N300-5):1.20\r\n"
276 "WBS510(TP-LINK|US|N300-5):1.20\r\n"
277 "WBS510(TP-LINK|EU|N300-5):1.20\r\n",
278 .support_trail
= '\xff',
282 {"fs-uboot", 0x00000, 0x20000},
283 {"partition-table", 0x20000, 0x02000},
284 {"default-mac", 0x30000, 0x00020},
285 {"product-info", 0x31100, 0x00100},
286 {"signature", 0x32000, 0x00400},
287 {"os-image", 0x40000, 0x1c0000},
288 {"file-system", 0x200000, 0x5b0000},
289 {"soft-version", 0x7b0000, 0x00100},
290 {"support-list", 0x7b1000, 0x00400},
291 {"user-config", 0x7c0000, 0x10000},
292 {"default-config", 0x7d0000, 0x10000},
293 {"log", 0x7e0000, 0x10000},
294 {"radio", 0x7f0000, 0x10000},
298 .first_sysupgrade_partition
= "os-image",
299 .last_sysupgrade_partition
= "support-list",
302 /** Firmware layout for the C2600 */
308 "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n",
309 .support_trail
= '\x00',
313 We use a bigger os-image partition than the stock images (and thus
314 smaller file-system), as our kernel doesn't fit in the stock firmware's
315 2 MB os-image since kernel 4.14.
318 {"SBL1", 0x00000, 0x20000},
319 {"MIBIB", 0x20000, 0x20000},
320 {"SBL2", 0x40000, 0x20000},
321 {"SBL3", 0x60000, 0x30000},
322 {"DDRCONFIG", 0x90000, 0x10000},
323 {"SSD", 0xa0000, 0x10000},
324 {"TZ", 0xb0000, 0x30000},
325 {"RPM", 0xe0000, 0x20000},
326 {"fs-uboot", 0x100000, 0x70000},
327 {"uboot-env", 0x170000, 0x40000},
328 {"radio", 0x1b0000, 0x40000},
329 {"os-image", 0x1f0000, 0x400000}, /* Stock: base 0x1f0000 size 0x200000 */
330 {"file-system", 0x5f0000, 0x1900000}, /* Stock: base 0x3f0000 size 0x1b00000 */
331 {"default-mac", 0x1ef0000, 0x00200},
332 {"pin", 0x1ef0200, 0x00200},
333 {"product-info", 0x1ef0400, 0x0fc00},
334 {"partition-table", 0x1f00000, 0x10000},
335 {"soft-version", 0x1f10000, 0x10000},
336 {"support-list", 0x1f20000, 0x10000},
337 {"profile", 0x1f30000, 0x10000},
338 {"default-config", 0x1f40000, 0x10000},
339 {"user-config", 0x1f50000, 0x40000},
340 {"qos-db", 0x1f90000, 0x40000},
341 {"usb-config", 0x1fd0000, 0x10000},
342 {"log", 0x1fe0000, 0x20000},
346 .first_sysupgrade_partition
= "os-image",
347 .last_sysupgrade_partition
= "file-system"
350 /** Firmware layout for the C25v1 */
352 .id
= "ARCHER-C25-V1",
355 "{product_name:ArcherC25,product_ver:1.0.0,special_id:00000000}\n"
356 "{product_name:ArcherC25,product_ver:1.0.0,special_id:55530000}\n"
357 "{product_name:ArcherC25,product_ver:1.0.0,special_id:45550000}\n",
358 .support_trail
= '\x00',
359 .soft_ver
= "soft_ver:1.0.0\n",
362 We use a bigger os-image partition than the stock images (and thus
363 smaller file-system), as our kernel doesn't fit in the stock firmware's
367 {"factory-boot", 0x00000, 0x20000},
368 {"fs-uboot", 0x20000, 0x10000},
369 {"os-image", 0x30000, 0x180000}, /* Stock: base 0x30000 size 0x100000 */
370 {"file-system", 0x1b0000, 0x620000}, /* Stock: base 0x130000 size 0x6a0000 */
371 {"user-config", 0x7d0000, 0x04000},
372 {"default-mac", 0x7e0000, 0x00100},
373 {"device-id", 0x7e0100, 0x00100},
374 {"extra-para", 0x7e0200, 0x00100},
375 {"pin", 0x7e0300, 0x00100},
376 {"support-list", 0x7e0400, 0x00400},
377 {"soft-version", 0x7e0800, 0x00400},
378 {"product-info", 0x7e0c00, 0x01400},
379 {"partition-table", 0x7e2000, 0x01000},
380 {"profile", 0x7e3000, 0x01000},
381 {"default-config", 0x7e4000, 0x04000},
382 {"merge-config", 0x7ec000, 0x02000},
383 {"qos-db", 0x7ee000, 0x02000},
384 {"radio", 0x7f0000, 0x10000},
388 .first_sysupgrade_partition
= "os-image",
389 .last_sysupgrade_partition
= "file-system",
392 /** Firmware layout for the C58v1 */
394 .id
= "ARCHER-C58-V1",
398 "{product_name:Archer C58,product_ver:1.0.0,special_id:00000000}\r\n"
399 "{product_name:Archer C58,product_ver:1.0.0,special_id:45550000}\r\n"
400 "{product_name:Archer C58,product_ver:1.0.0,special_id:55530000}\r\n",
401 .support_trail
= '\x00',
402 .soft_ver
= "soft_ver:1.0.0\n",
405 {"fs-uboot", 0x00000, 0x10000},
406 {"default-mac", 0x10000, 0x00200},
407 {"pin", 0x10200, 0x00200},
408 {"product-info", 0x10400, 0x00100},
409 {"partition-table", 0x10500, 0x00800},
410 {"soft-version", 0x11300, 0x00200},
411 {"support-list", 0x11500, 0x00100},
412 {"device-id", 0x11600, 0x00100},
413 {"profile", 0x11700, 0x03900},
414 {"default-config", 0x15000, 0x04000},
415 {"user-config", 0x19000, 0x04000},
416 {"os-image", 0x20000, 0x180000},
417 {"file-system", 0x1a0000, 0x648000},
418 {"certyficate", 0x7e8000, 0x08000},
419 {"radio", 0x7f0000, 0x10000},
423 .first_sysupgrade_partition
= "os-image",
424 .last_sysupgrade_partition
= "file-system",
427 /** Firmware layout for the C59v1 */
429 .id
= "ARCHER-C59-V1",
433 "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n"
434 "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n"
435 "{product_name:Archer C59,product_ver:1.0.0,special_id:52550000}\r\n"
436 "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n",
437 .support_trail
= '\x00',
438 .soft_ver
= "soft_ver:1.0.0\n",
441 {"fs-uboot", 0x00000, 0x10000},
442 {"default-mac", 0x10000, 0x00200},
443 {"pin", 0x10200, 0x00200},
444 {"device-id", 0x10400, 0x00100},
445 {"product-info", 0x10500, 0x0fb00},
446 {"os-image", 0x20000, 0x180000},
447 {"file-system", 0x1a0000, 0xcb0000},
448 {"partition-table", 0xe50000, 0x10000},
449 {"soft-version", 0xe60000, 0x10000},
450 {"support-list", 0xe70000, 0x10000},
451 {"profile", 0xe80000, 0x10000},
452 {"default-config", 0xe90000, 0x10000},
453 {"user-config", 0xea0000, 0x40000},
454 {"usb-config", 0xee0000, 0x10000},
455 {"certificate", 0xef0000, 0x10000},
456 {"qos-db", 0xf00000, 0x40000},
457 {"log", 0xfe0000, 0x10000},
458 {"radio", 0xff0000, 0x10000},
462 .first_sysupgrade_partition
= "os-image",
463 .last_sysupgrade_partition
= "file-system",
466 /** Firmware layout for the C60v1 */
468 .id
= "ARCHER-C60-V1",
472 "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n"
473 "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n"
474 "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n",
475 .support_trail
= '\x00',
476 .soft_ver
= "soft_ver:1.0.0\n",
479 {"fs-uboot", 0x00000, 0x10000},
480 {"default-mac", 0x10000, 0x00200},
481 {"pin", 0x10200, 0x00200},
482 {"product-info", 0x10400, 0x00100},
483 {"partition-table", 0x10500, 0x00800},
484 {"soft-version", 0x11300, 0x00200},
485 {"support-list", 0x11500, 0x00100},
486 {"device-id", 0x11600, 0x00100},
487 {"profile", 0x11700, 0x03900},
488 {"default-config", 0x15000, 0x04000},
489 {"user-config", 0x19000, 0x04000},
490 {"os-image", 0x20000, 0x180000},
491 {"file-system", 0x1a0000, 0x648000},
492 {"certyficate", 0x7e8000, 0x08000},
493 {"radio", 0x7f0000, 0x10000},
497 .first_sysupgrade_partition
= "os-image",
498 .last_sysupgrade_partition
= "file-system",
501 /** Firmware layout for the C60v2 */
503 .id
= "ARCHER-C60-V2",
507 "{product_name:Archer C60,product_ver:2.0.0,special_id:42520000}\r\n"
508 "{product_name:Archer C60,product_ver:2.0.0,special_id:45550000}\r\n"
509 "{product_name:Archer C60,product_ver:2.0.0,special_id:55530000}\r\n",
510 .support_trail
= '\x00',
511 .soft_ver
= "soft_ver:2.0.0\n",
514 {"factory-boot", 0x00000, 0x1fb00},
515 {"default-mac", 0x1fb00, 0x00200},
516 {"pin", 0x1fd00, 0x00100},
517 {"product-info", 0x1fe00, 0x00100},
518 {"device-id", 0x1ff00, 0x00100},
519 {"fs-uboot", 0x20000, 0x10000},
520 {"os-image", 0x30000, 0x180000},
521 {"file-system", 0x1b0000, 0x620000},
522 {"soft-version", 0x7d9500, 0x00100},
523 {"support-list", 0x7d9600, 0x00100},
524 {"extra-para", 0x7d9700, 0x00100},
525 {"profile", 0x7d9800, 0x03000},
526 {"default-config", 0x7dc800, 0x03000},
527 {"partition-table", 0x7df800, 0x00800},
528 {"user-config", 0x7e0000, 0x0c000},
529 {"certificate", 0x7ec000, 0x04000},
530 {"radio", 0x7f0000, 0x10000},
534 .first_sysupgrade_partition
= "os-image",
535 .last_sysupgrade_partition
= "file-system",
538 /** Firmware layout for the C5 */
540 .id
= "ARCHER-C5-V2",
544 "{product_name:ArcherC5,product_ver:2.0.0,special_id:00000000}\r\n"
545 "{product_name:ArcherC5,product_ver:2.0.0,special_id:55530000}\r\n"
546 "{product_name:ArcherC5,product_ver:2.0.0,special_id:4A500000}\r\n", /* JP version */
547 .support_trail
= '\x00',
551 {"fs-uboot", 0x00000, 0x40000},
552 {"os-image", 0x40000, 0x200000},
553 {"file-system", 0x240000, 0xc00000},
554 {"default-mac", 0xe40000, 0x00200},
555 {"pin", 0xe40200, 0x00200},
556 {"product-info", 0xe40400, 0x00200},
557 {"partition-table", 0xe50000, 0x10000},
558 {"soft-version", 0xe60000, 0x00200},
559 {"support-list", 0xe61000, 0x0f000},
560 {"profile", 0xe70000, 0x10000},
561 {"default-config", 0xe80000, 0x10000},
562 {"user-config", 0xe90000, 0x50000},
563 {"log", 0xee0000, 0x100000},
564 {"radio_bk", 0xfe0000, 0x10000},
565 {"radio", 0xff0000, 0x10000},
569 .first_sysupgrade_partition
= "os-image",
570 .last_sysupgrade_partition
= "file-system"
573 /** Firmware layout for the C7 */
575 .id
= "ARCHER-C7-V4",
578 "{product_name:Archer C7,product_ver:4.0.0,special_id:00000000}\n"
579 "{product_name:Archer C7,product_ver:4.0.0,special_id:41550000}\n"
580 "{product_name:Archer C7,product_ver:4.0.0,special_id:45550000}\n"
581 "{product_name:Archer C7,product_ver:4.0.0,special_id:4B520000}\n"
582 "{product_name:Archer C7,product_ver:4.0.0,special_id:42520000}\n"
583 "{product_name:Archer C7,product_ver:4.0.0,special_id:4A500000}\n"
584 "{product_name:Archer C7,product_ver:4.0.0,special_id:52550000}\n"
585 "{product_name:Archer C7,product_ver:4.0.0,special_id:54570000}\n"
586 "{product_name:Archer C7,product_ver:4.0.0,special_id:55530000}\n"
587 "{product_name:Archer C7,product_ver:4.0.0,special_id:43410000}\n",
588 .support_trail
= '\x00',
589 .soft_ver
= "soft_ver:1.0.0\n",
592 We use a bigger os-image partition than the stock images (and thus
593 smaller file-system), as our kernel doesn't fit in the stock firmware's
597 {"factory-boot", 0x00000, 0x20000},
598 {"fs-uboot", 0x20000, 0x20000},
599 {"os-image", 0x40000, 0x180000}, /* Stock: base 0x40000 size 0x120000 */
600 {"file-system", 0x1c0000, 0xd40000}, /* Stock: base 0x160000 size 0xda0000 */
601 {"default-mac", 0xf00000, 0x00200},
602 {"pin", 0xf00200, 0x00200},
603 {"device-id", 0xf00400, 0x00100},
604 {"product-info", 0xf00500, 0x0fb00},
605 {"soft-version", 0xf10000, 0x00100},
606 {"extra-para", 0xf11000, 0x01000},
607 {"support-list", 0xf12000, 0x0a000},
608 {"profile", 0xf1c000, 0x04000},
609 {"default-config", 0xf20000, 0x10000},
610 {"user-config", 0xf30000, 0x40000},
611 {"qos-db", 0xf70000, 0x40000},
612 {"certificate", 0xfb0000, 0x10000},
613 {"partition-table", 0xfc0000, 0x10000},
614 {"log", 0xfd0000, 0x20000},
615 {"radio", 0xff0000, 0x10000},
619 .first_sysupgrade_partition
= "os-image",
620 .last_sysupgrade_partition
= "file-system",
623 /** Firmware layout for the C7 v5*/
625 .id
= "ARCHER-C7-V5",
628 "{product_name:Archer C7,product_ver:5.0.0,special_id:00000000}\n"
629 "{product_name:Archer C7,product_ver:5.0.0,special_id:55530000}\n",
631 .support_trail
= '\x00',
632 .soft_ver
= "soft_ver:1.0.0\n",
635 We use a bigger os-image partition than the stock images (and thus
636 smaller file-system), as our kernel doesn't fit in the stock firmware's
640 {"factory-boot", 0x00000, 0x20000},
641 {"fs-uboot", 0x20000, 0x20000},
642 {"partition-table", 0x40000, 0x10000},
643 {"radio", 0x50000, 0x10000},
644 {"default-mac", 0x60000, 0x00200},
645 {"pin", 0x60200, 0x00200},
646 {"device-id", 0x60400, 0x00100},
647 {"product-info", 0x60500, 0x0fb00},
648 {"soft-version", 0x70000, 0x01000},
649 {"extra-para", 0x71000, 0x01000},
650 {"support-list", 0x72000, 0x0a000},
651 {"profile", 0x7c000, 0x04000},
652 {"user-config", 0x80000, 0x40000},
655 {"os-image", 0xc0000, 0x180000}, /* Stock: base 0xc0000 size 0x120000 */
656 {"file-system", 0x240000, 0xd80000}, /* Stock: base 0x1e0000 size 0xde0000 */
658 {"log", 0xfc0000, 0x20000},
659 {"certificate", 0xfe0000, 0x10000},
660 {"default-config", 0xff0000, 0x10000},
665 .first_sysupgrade_partition
= "os-image",
666 .last_sysupgrade_partition
= "file-system",
669 /** Firmware layout for the C9 */
675 "{product_name:ArcherC9,"
677 "special_id:00000000}\n",
678 .support_trail
= '\x00',
682 {"fs-uboot", 0x00000, 0x40000},
683 {"os-image", 0x40000, 0x200000},
684 {"file-system", 0x240000, 0xc00000},
685 {"default-mac", 0xe40000, 0x00200},
686 {"pin", 0xe40200, 0x00200},
687 {"product-info", 0xe40400, 0x00200},
688 {"partition-table", 0xe50000, 0x10000},
689 {"soft-version", 0xe60000, 0x00200},
690 {"support-list", 0xe61000, 0x0f000},
691 {"profile", 0xe70000, 0x10000},
692 {"default-config", 0xe80000, 0x10000},
693 {"user-config", 0xe90000, 0x50000},
694 {"log", 0xee0000, 0x100000},
695 {"radio_bk", 0xfe0000, 0x10000},
696 {"radio", 0xff0000, 0x10000},
700 .first_sysupgrade_partition
= "os-image",
701 .last_sysupgrade_partition
= "file-system"
704 /** Firmware layout for the EAP120 */
707 .vendor
= "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
710 "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
711 .support_trail
= '\xff',
715 {"fs-uboot", 0x00000, 0x20000},
716 {"partition-table", 0x20000, 0x02000},
717 {"default-mac", 0x30000, 0x00020},
718 {"support-list", 0x31000, 0x00100},
719 {"product-info", 0x31100, 0x00100},
720 {"soft-version", 0x32000, 0x00100},
721 {"os-image", 0x40000, 0x180000},
722 {"file-system", 0x1c0000, 0x600000},
723 {"user-config", 0x7c0000, 0x10000},
724 {"backup-config", 0x7d0000, 0x10000},
725 {"log", 0x7e0000, 0x10000},
726 {"radio", 0x7f0000, 0x10000},
730 .first_sysupgrade_partition
= "os-image",
731 .last_sysupgrade_partition
= "file-system"
734 /** Firmware layout for the TL-WA850RE v2 */
740 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55530000}\n"
741 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:00000000}\n"
742 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55534100}\n"
743 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:45550000}\n"
744 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4B520000}\n"
745 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:42520000}\n"
746 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4A500000}\n"
747 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:43410000}\n"
748 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:41550000}\n"
749 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:52550000}\n",
750 .support_trail
= '\x00',
754 576KB were moved from file-system to os-image
755 in comparison to the stock image
758 {"fs-uboot", 0x00000, 0x20000},
759 {"os-image", 0x20000, 0x150000},
760 {"file-system", 0x170000, 0x240000},
761 {"partition-table", 0x3b0000, 0x02000},
762 {"default-mac", 0x3c0000, 0x00020},
763 {"pin", 0x3c0100, 0x00020},
764 {"product-info", 0x3c1000, 0x01000},
765 {"soft-version", 0x3c2000, 0x00100},
766 {"support-list", 0x3c3000, 0x01000},
767 {"profile", 0x3c4000, 0x08000},
768 {"user-config", 0x3d0000, 0x10000},
769 {"default-config", 0x3e0000, 0x10000},
770 {"radio", 0x3f0000, 0x10000},
774 .first_sysupgrade_partition
= "os-image",
775 .last_sysupgrade_partition
= "file-system"
778 /** Firmware layout for the TL-WA855RE v1 */
784 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:00000000}\n"
785 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:55530000}\n"
786 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:45550000}\n"
787 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4B520000}\n"
788 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:42520000}\n"
789 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4A500000}\n"
790 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:43410000}\n"
791 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:41550000}\n"
792 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:52550000}\n",
793 .support_trail
= '\x00',
797 {"fs-uboot", 0x00000, 0x20000},
798 {"os-image", 0x20000, 0x150000},
799 {"file-system", 0x170000, 0x240000},
800 {"partition-table", 0x3b0000, 0x02000},
801 {"default-mac", 0x3c0000, 0x00020},
802 {"pin", 0x3c0100, 0x00020},
803 {"product-info", 0x3c1000, 0x01000},
804 {"soft-version", 0x3c2000, 0x00100},
805 {"support-list", 0x3c3000, 0x01000},
806 {"profile", 0x3c4000, 0x08000},
807 {"user-config", 0x3d0000, 0x10000},
808 {"default-config", 0x3e0000, 0x10000},
809 {"radio", 0x3f0000, 0x10000},
813 .first_sysupgrade_partition
= "os-image",
814 .last_sysupgrade_partition
= "file-system"
817 /** Firmware layout for the TL-WR1043 v5 */
823 "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:45550000}\n"
824 "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:55530000}\n",
825 .support_trail
= '\x00',
826 .soft_ver
= "soft_ver:1.0.0\n",
828 {"factory-boot", 0x00000, 0x20000},
829 {"fs-uboot", 0x20000, 0x20000},
830 {"os-image", 0x40000, 0x180000},
831 {"file-system", 0x1c0000, 0xd40000},
832 {"default-mac", 0xf00000, 0x00200},
833 {"pin", 0xf00200, 0x00200},
834 {"device-id", 0xf00400, 0x00100},
835 {"product-info", 0xf00500, 0x0fb00},
836 {"soft-version", 0xf10000, 0x01000},
837 {"extra-para", 0xf11000, 0x01000},
838 {"support-list", 0xf12000, 0x0a000},
839 {"profile", 0xf1c000, 0x04000},
840 {"default-config", 0xf20000, 0x10000},
841 {"user-config", 0xf30000, 0x40000},
842 {"qos-db", 0xf70000, 0x40000},
843 {"certificate", 0xfb0000, 0x10000},
844 {"partition-table", 0xfc0000, 0x10000},
845 {"log", 0xfd0000, 0x20000},
846 {"radio", 0xff0000, 0x10000},
849 .first_sysupgrade_partition
= "os-image",
850 .last_sysupgrade_partition
= "file-system"
853 /** Firmware layout for the TL-WR1043 v4 */
855 .id
= "TLWR1043NDV4",
859 "{product_name:TL-WR1043ND,product_ver:4.0.0,special_id:45550000}\n",
860 .support_trail
= '\x00',
864 We use a bigger os-image partition than the stock images (and thus
865 smaller file-system), as our kernel doesn't fit in the stock firmware's
869 {"fs-uboot", 0x00000, 0x20000},
870 {"os-image", 0x20000, 0x200000},
871 {"file-system", 0x220000, 0xd30000},
872 {"default-mac", 0xf50000, 0x00200},
873 {"pin", 0xf50200, 0x00200},
874 {"product-info", 0xf50400, 0x0fc00},
875 {"soft-version", 0xf60000, 0x0b000},
876 {"support-list", 0xf6b000, 0x04000},
877 {"profile", 0xf70000, 0x04000},
878 {"default-config", 0xf74000, 0x0b000},
879 {"user-config", 0xf80000, 0x40000},
880 {"partition-table", 0xfc0000, 0x10000},
881 {"log", 0xfd0000, 0x20000},
882 {"radio", 0xff0000, 0x10000},
886 .first_sysupgrade_partition
= "os-image",
887 .last_sysupgrade_partition
= "file-system"
890 /** Firmware layout for the TL-WR902AC v1 */
892 .id
= "TL-WR902AC-V1",
896 "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:45550000}\n"
897 "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:55530000}\n",
898 .support_trail
= '\x00',
902 384KB were moved from file-system to os-image
903 in comparison to the stock image
906 {"fs-uboot", 0x00000, 0x20000},
907 {"os-image", 0x20000, 0x180000},
908 {"file-system", 0x1a0000, 0x5b0000},
909 {"default-mac", 0x750000, 0x00200},
910 {"pin", 0x750200, 0x00200},
911 {"product-info", 0x750400, 0x0fc00},
912 {"soft-version", 0x760000, 0x0b000},
913 {"support-list", 0x76b000, 0x04000},
914 {"profile", 0x770000, 0x04000},
915 {"default-config", 0x774000, 0x0b000},
916 {"user-config", 0x780000, 0x40000},
917 {"partition-table", 0x7c0000, 0x10000},
918 {"log", 0x7d0000, 0x20000},
919 {"radio", 0x7f0000, 0x10000},
923 .first_sysupgrade_partition
= "os-image",
924 .last_sysupgrade_partition
= "file-system",
927 /** Firmware layout for the TL-WR942N V1 */
933 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:00000000}\r\n"
934 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:52550000}\r\n",
935 .support_trail
= '\x00',
939 {"fs-uboot", 0x00000, 0x20000},
940 {"os-image", 0x20000, 0x180000},
941 {"file-system", 0x1a0000, 0xca0000},
942 {"default-mac", 0xe40000, 0x00200},
943 {"pin", 0xe40200, 0x00200},
944 {"product-info", 0xe40400, 0x0fc00},
945 {"partition-table", 0xe50000, 0x10000},
946 {"soft-version", 0xe60000, 0x10000},
947 {"support-list", 0xe70000, 0x10000},
948 {"profile", 0xe80000, 0x10000},
949 {"default-config", 0xe90000, 0x10000},
950 {"user-config", 0xea0000, 0x40000},
951 {"qos-db", 0xee0000, 0x40000},
952 {"certificate", 0xf20000, 0x10000},
953 {"usb-config", 0xfb0000, 0x10000},
954 {"log", 0xfc0000, 0x20000},
955 {"radio-bk", 0xfe0000, 0x10000},
956 {"radio", 0xff0000, 0x10000},
960 .first_sysupgrade_partition
= "os-image",
961 .last_sysupgrade_partition
= "file-system",
964 /** Firmware layout for the RE350 v1 */
970 "{product_name:RE350,product_ver:1.0.0,special_id:45550000}\n"
971 "{product_name:RE350,product_ver:1.0.0,special_id:00000000}\n"
972 "{product_name:RE350,product_ver:1.0.0,special_id:41550000}\n"
973 "{product_name:RE350,product_ver:1.0.0,special_id:55530000}\n"
974 "{product_name:RE350,product_ver:1.0.0,special_id:43410000}\n"
975 "{product_name:RE350,product_ver:1.0.0,special_id:4b520000}\n"
976 "{product_name:RE350,product_ver:1.0.0,special_id:4a500000}\n",
977 .support_trail
= '\x00',
981 The original os-image partition is too small,
982 so we enlarge it to 1.75M
985 {"fs-uboot", 0x00000, 0x20000},
986 {"os-image", 0x20000, 0x1c0000},
987 {"file-system", 0x1e0000, 0x420000},
988 {"partition-table", 0x600000, 0x02000},
989 {"default-mac", 0x610000, 0x00020},
990 {"pin", 0x610100, 0x00020},
991 {"product-info", 0x611100, 0x01000},
992 {"soft-version", 0x620000, 0x01000},
993 {"support-list", 0x621000, 0x01000},
994 {"profile", 0x622000, 0x08000},
995 {"user-config", 0x630000, 0x10000},
996 {"default-config", 0x640000, 0x10000},
997 {"radio", 0x7f0000, 0x10000},
1001 .first_sysupgrade_partition
= "os-image",
1002 .last_sysupgrade_partition
= "file-system"
1005 /** Firmware layout for the RE355 */
1011 "{product_name:RE355,product_ver:1.0.0,special_id:00000000}\r\n"
1012 "{product_name:RE355,product_ver:1.0.0,special_id:55530000}\r\n"
1013 "{product_name:RE355,product_ver:1.0.0,special_id:45550000}\r\n"
1014 "{product_name:RE355,product_ver:1.0.0,special_id:4A500000}\r\n"
1015 "{product_name:RE355,product_ver:1.0.0,special_id:43410000}\r\n"
1016 "{product_name:RE355,product_ver:1.0.0,special_id:41550000}\r\n"
1017 "{product_name:RE355,product_ver:1.0.0,special_id:4B520000}\r\n"
1018 "{product_name:RE355,product_ver:1.0.0,special_id:55534100}\r\n",
1019 .support_trail
= '\x00',
1023 The flash partition table for RE355;
1024 it is almost the same as the one used by the stock images,
1025 576KB were moved from file-system to os-image.
1028 {"fs-uboot", 0x00000, 0x20000},
1029 {"os-image", 0x20000, 0x180000},
1030 {"file-system", 0x1a0000, 0x460000},
1031 {"partition-table", 0x600000, 0x02000},
1032 {"default-mac", 0x610000, 0x00020},
1033 {"pin", 0x610100, 0x00020},
1034 {"product-info", 0x611100, 0x01000},
1035 {"soft-version", 0x620000, 0x01000},
1036 {"support-list", 0x621000, 0x01000},
1037 {"profile", 0x622000, 0x08000},
1038 {"user-config", 0x630000, 0x10000},
1039 {"default-config", 0x640000, 0x10000},
1040 {"radio", 0x7f0000, 0x10000},
1044 .first_sysupgrade_partition
= "os-image",
1045 .last_sysupgrade_partition
= "file-system"
1048 /** Firmware layout for the RE450 */
1054 "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n"
1055 "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n"
1056 "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n"
1057 "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n"
1058 "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n"
1059 "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n"
1060 "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n"
1061 "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n",
1062 .support_trail
= '\x00',
1066 The flash partition table for RE450;
1067 it is almost the same as the one used by the stock images,
1068 576KB were moved from file-system to os-image.
1071 {"fs-uboot", 0x00000, 0x20000},
1072 {"os-image", 0x20000, 0x180000},
1073 {"file-system", 0x1a0000, 0x460000},
1074 {"partition-table", 0x600000, 0x02000},
1075 {"default-mac", 0x610000, 0x00020},
1076 {"pin", 0x610100, 0x00020},
1077 {"product-info", 0x611100, 0x01000},
1078 {"soft-version", 0x620000, 0x01000},
1079 {"support-list", 0x621000, 0x01000},
1080 {"profile", 0x622000, 0x08000},
1081 {"user-config", 0x630000, 0x10000},
1082 {"default-config", 0x640000, 0x10000},
1083 {"radio", 0x7f0000, 0x10000},
1087 .first_sysupgrade_partition
= "os-image",
1088 .last_sysupgrade_partition
= "file-system"
1094 #define error(_ret, _errno, _str, ...) \
1096 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
1097 strerror(_errno)); \
1103 /** Stores a uint32 as big endian */
1104 static inline void put32(uint8_t *buf
, uint32_t val
) {
1111 /** Allocates a new image partition */
1112 static struct image_partition_entry
alloc_image_partition(const char *name
, size_t len
) {
1113 struct image_partition_entry entry
= {name
, len
, malloc(len
)};
1115 error(1, errno
, "malloc");
1120 /** Frees an image partition */
1121 static void free_image_partition(struct image_partition_entry entry
) {
1125 static time_t source_date_epoch
= -1;
1126 static void set_source_date_epoch() {
1127 char *env
= getenv("SOURCE_DATE_EPOCH");
1131 source_date_epoch
= strtoull(env
, &endptr
, 10);
1132 if (errno
|| (endptr
&& *endptr
!= '\0')) {
1133 fprintf(stderr
, "Invalid SOURCE_DATE_EPOCH");
1139 /** Generates the partition-table partition */
1140 static struct image_partition_entry
make_partition_table(const struct flash_partition_entry
*p
) {
1141 struct image_partition_entry entry
= alloc_image_partition("partition-table", 0x800);
1143 char *s
= (char *)entry
.data
, *end
= (char *)(s
+entry
.size
);
1151 for (i
= 0; p
[i
].name
; i
++) {
1153 size_t w
= snprintf(s
, len
, "partition %s base 0x%05x size 0x%05x\n", p
[i
].name
, p
[i
].base
, p
[i
].size
);
1156 error(1, 0, "flash partition table overflow?");
1163 memset(s
, 0xff, end
-s
);
1169 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
1170 static inline uint8_t bcd(uint8_t v
) {
1171 return 0x10 * (v
/10) + v
%10;
1175 /** Generates the soft-version partition */
1176 static struct image_partition_entry
make_soft_version(uint32_t rev
) {
1177 struct image_partition_entry entry
= alloc_image_partition("soft-version", sizeof(struct soft_version
));
1178 struct soft_version
*s
= (struct soft_version
*)entry
.data
;
1182 if (source_date_epoch
!= -1)
1183 t
= source_date_epoch
;
1184 else if (time(&t
) == (time_t)(-1))
1185 error(1, errno
, "time");
1187 struct tm
*tm
= localtime(&t
);
1189 s
->magic
= htonl(0x0000000c);
1193 s
->version_major
= 0;
1194 s
->version_minor
= 0;
1195 s
->version_patch
= 0;
1197 s
->year_hi
= bcd((1900+tm
->tm_year
)/100);
1198 s
->year_lo
= bcd(tm
->tm_year
%100);
1199 s
->month
= bcd(tm
->tm_mon
+1);
1200 s
->day
= bcd(tm
->tm_mday
);
1201 s
->rev
= htonl(rev
);
1208 static struct image_partition_entry
make_soft_version_from_string(const char *soft_ver
) {
1209 /** String length _including_ the terminating zero byte */
1210 uint32_t ver_len
= strlen(soft_ver
) + 1;
1211 /** Partition contains 64 bit header, the version string, and one additional null byte */
1212 size_t partition_len
= 2*sizeof(uint32_t) + ver_len
+ 1;
1213 struct image_partition_entry entry
= alloc_image_partition("soft-version", partition_len
);
1215 uint32_t *len
= (uint32_t *)entry
.data
;
1216 len
[0] = htonl(ver_len
);
1218 memcpy(&len
[2], soft_ver
, ver_len
);
1220 entry
.data
[partition_len
- 1] = 0;
1225 /** Generates the support-list partition */
1226 static struct image_partition_entry
make_support_list(struct device_info
*info
) {
1227 size_t len
= strlen(info
->support_list
);
1228 struct image_partition_entry entry
= alloc_image_partition("support-list", len
+ 9);
1230 put32(entry
.data
, len
);
1231 memset(entry
.data
+4, 0, 4);
1232 memcpy(entry
.data
+8, info
->support_list
, len
);
1233 entry
.data
[len
+8] = info
->support_trail
;
1238 /** Creates a new image partition with an arbitrary name from a file */
1239 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
) {
1240 struct stat statbuf
;
1242 if (stat(filename
, &statbuf
) < 0)
1243 error(1, errno
, "unable to stat file `%s'", filename
);
1245 size_t len
= statbuf
.st_size
;
1248 if (file_system_partition
)
1249 len
= ALIGN(len
+ file_system_partition
->base
, 0x10000) + sizeof(jffs2_eof_mark
) - file_system_partition
->base
;
1251 len
= ALIGN(len
, 0x10000) + sizeof(jffs2_eof_mark
);
1253 struct image_partition_entry entry
= alloc_image_partition(part_name
, len
);
1255 FILE *file
= fopen(filename
, "rb");
1257 error(1, errno
, "unable to open file `%s'", filename
);
1259 if (fread(entry
.data
, statbuf
.st_size
, 1, file
) != 1)
1260 error(1, errno
, "unable to read file `%s'", filename
);
1262 if (add_jffs2_eof
) {
1263 uint8_t *eof
= entry
.data
+ statbuf
.st_size
, *end
= entry
.data
+entry
.size
;
1265 memset(eof
, 0xff, end
- eof
- sizeof(jffs2_eof_mark
));
1266 memcpy(end
- sizeof(jffs2_eof_mark
), jffs2_eof_mark
, sizeof(jffs2_eof_mark
));
1274 /** Creates a new image partition from arbitrary data */
1275 static struct image_partition_entry
put_data(const char *part_name
, const char *datain
, size_t len
) {
1277 struct image_partition_entry entry
= alloc_image_partition(part_name
, len
);
1279 memcpy(entry
.data
, datain
, len
);
1285 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
1287 Example image partition table:
1289 fwup-ptn partition-table base 0x00800 size 0x00800
1290 fwup-ptn os-image base 0x01000 size 0x113b45
1291 fwup-ptn file-system base 0x114b45 size 0x1d0004
1292 fwup-ptn support-list base 0x2e4b49 size 0x000d1
1294 Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
1295 the end of the partition table is marked with a zero byte.
1297 The firmware image must contain at least the partition-table and support-list partitions
1298 to be accepted. There aren't any alignment constraints for the image partitions.
1300 The partition-table partition contains the actual flash layout; partitions
1301 from the image partition table are mapped to the corresponding flash partitions during
1302 the firmware upgrade. The support-list partition contains a list of devices supported by
1305 The base offsets in the firmware partition table are relative to the end
1306 of the vendor information block, so the partition-table partition will
1307 actually start at offset 0x1814 of the image.
1309 I think partition-table must be the first partition in the firmware image.
1311 static void put_partitions(uint8_t *buffer
, const struct flash_partition_entry
*flash_parts
, const struct image_partition_entry
*parts
) {
1313 char *image_pt
= (char *)buffer
, *end
= image_pt
+ 0x800;
1315 size_t base
= 0x800;
1316 for (i
= 0; parts
[i
].name
; i
++) {
1317 for (j
= 0; flash_parts
[j
].name
; j
++) {
1318 if (!strcmp(flash_parts
[j
].name
, parts
[i
].name
)) {
1319 if (parts
[i
].size
> flash_parts
[j
].size
)
1320 error(1, 0, "%s partition too big (more than %u bytes)", flash_parts
[j
].name
, (unsigned)flash_parts
[j
].size
);
1325 assert(flash_parts
[j
].name
);
1327 memcpy(buffer
+ base
, parts
[i
].data
, parts
[i
].size
);
1329 size_t len
= end
-image_pt
;
1330 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
);
1333 error(1, 0, "image partition table overflow?");
1337 base
+= parts
[i
].size
;
1341 /** Generates and writes the image MD5 checksum */
1342 static void put_md5(uint8_t *md5
, uint8_t *buffer
, unsigned int len
) {
1346 MD5_Update(&ctx
, md5_salt
, (unsigned int)sizeof(md5_salt
));
1347 MD5_Update(&ctx
, buffer
, len
);
1348 MD5_Final(md5
, &ctx
);
1353 Generates the firmware image in factory format
1359 0000-0003 Image size (4 bytes, big endian)
1360 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
1361 0014-0017 Vendor information length (without padding) (4 bytes, big endian)
1362 0018-1013 Vendor information (4092 bytes, padded with 0xff; there seem to be older
1363 (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
1364 1014-1813 Image partition table (2048 bytes, padded with 0xff)
1365 1814-xxxx Firmware partitions
1367 static void * generate_factory_image(struct device_info
*info
, const struct image_partition_entry
*parts
, size_t *len
) {
1371 for (i
= 0; parts
[i
].name
; i
++)
1372 *len
+= parts
[i
].size
;
1374 uint8_t *image
= malloc(*len
);
1376 error(1, errno
, "malloc");
1378 memset(image
, 0xff, *len
);
1382 size_t vendor_len
= strlen(info
->vendor
);
1383 put32(image
+0x14, vendor_len
);
1384 memcpy(image
+0x18, info
->vendor
, vendor_len
);
1387 put_partitions(image
+ 0x1014, info
->partitions
, parts
);
1388 put_md5(image
+0x04, image
+0x14, *len
-0x14);
1394 Generates the firmware image in sysupgrade format
1396 This makes some assumptions about the provided flash and image partition tables and
1397 should be generalized when TP-LINK starts building its safeloader into hardware with
1398 different flash layouts.
1400 static void * generate_sysupgrade_image(struct device_info
*info
, const struct image_partition_entry
*image_parts
, size_t *len
) {
1402 size_t flash_first_partition_index
= 0;
1403 size_t flash_last_partition_index
= 0;
1404 const struct flash_partition_entry
*flash_first_partition
= NULL
;
1405 const struct flash_partition_entry
*flash_last_partition
= NULL
;
1406 const struct image_partition_entry
*image_last_partition
= NULL
;
1408 /** Find first and last partitions */
1409 for (i
= 0; info
->partitions
[i
].name
; i
++) {
1410 if (!strcmp(info
->partitions
[i
].name
, info
->first_sysupgrade_partition
)) {
1411 flash_first_partition
= &info
->partitions
[i
];
1412 flash_first_partition_index
= i
;
1413 } else if (!strcmp(info
->partitions
[i
].name
, info
->last_sysupgrade_partition
)) {
1414 flash_last_partition
= &info
->partitions
[i
];
1415 flash_last_partition_index
= i
;
1419 assert(flash_first_partition
&& flash_last_partition
);
1420 assert(flash_first_partition_index
< flash_last_partition_index
);
1422 /** Find last partition from image to calculate needed size */
1423 for (i
= 0; image_parts
[i
].name
; i
++) {
1424 if (!strcmp(image_parts
[i
].name
, info
->last_sysupgrade_partition
)) {
1425 image_last_partition
= &image_parts
[i
];
1430 assert(image_last_partition
);
1432 *len
= flash_last_partition
->base
- flash_first_partition
->base
+ image_last_partition
->size
;
1434 uint8_t *image
= malloc(*len
);
1436 error(1, errno
, "malloc");
1438 memset(image
, 0xff, *len
);
1440 for (i
= flash_first_partition_index
; i
<= flash_last_partition_index
; i
++) {
1441 for (j
= 0; image_parts
[j
].name
; j
++) {
1442 if (!strcmp(info
->partitions
[i
].name
, image_parts
[j
].name
)) {
1443 if (image_parts
[j
].size
> info
->partitions
[i
].size
)
1444 error(1, 0, "%s partition too big (more than %u bytes)", info
->partitions
[i
].name
, (unsigned)info
->partitions
[i
].size
);
1445 memcpy(image
+ info
->partitions
[i
].base
- flash_first_partition
->base
, image_parts
[j
].data
, image_parts
[j
].size
);
1449 assert(image_parts
[j
].name
);
1456 /** Generates an image according to a given layout and writes it to a file */
1457 static void build_image(const char *output
,
1458 const char *kernel_image
,
1459 const char *rootfs_image
,
1463 struct device_info
*info
) {
1467 struct image_partition_entry parts
[7] = {};
1469 struct flash_partition_entry
*firmware_partition
= NULL
;
1470 struct flash_partition_entry
*os_image_partition
= NULL
;
1471 struct flash_partition_entry
*file_system_partition
= NULL
;
1472 size_t firmware_partition_index
= 0;
1474 for (i
= 0; info
->partitions
[i
].name
; i
++) {
1475 if (!strcmp(info
->partitions
[i
].name
, "firmware"))
1477 firmware_partition
= &info
->partitions
[i
];
1478 firmware_partition_index
= i
;
1482 if (firmware_partition
)
1484 os_image_partition
= &info
->partitions
[firmware_partition_index
];
1485 file_system_partition
= &info
->partitions
[firmware_partition_index
+ 1];
1488 if (stat(kernel_image
, &kernel
) < 0)
1489 error(1, errno
, "unable to stat file `%s'", kernel_image
);
1491 if (kernel
.st_size
> firmware_partition
->size
)
1492 error(1, 0, "kernel overflowed firmware partition\n");
1494 for (i
= MAX_PARTITIONS
-1; i
>= firmware_partition_index
+ 1; i
--)
1495 info
->partitions
[i
+1] = info
->partitions
[i
];
1497 file_system_partition
->name
= "file-system";
1498 file_system_partition
->base
= firmware_partition
->base
+ kernel
.st_size
;
1500 /* Align partition start to erase blocks for factory images only */
1502 file_system_partition
->base
= ALIGN(firmware_partition
->base
+ kernel
.st_size
, 0x10000);
1504 file_system_partition
->size
= firmware_partition
->size
- file_system_partition
->base
;
1506 os_image_partition
->name
= "os-image";
1507 os_image_partition
->size
= kernel
.st_size
;
1510 parts
[0] = make_partition_table(info
->partitions
);
1512 parts
[1] = make_soft_version_from_string(info
->soft_ver
);
1514 parts
[1] = make_soft_version(rev
);
1516 parts
[2] = make_support_list(info
);
1517 parts
[3] = read_file("os-image", kernel_image
, false, NULL
);
1518 parts
[4] = read_file("file-system", rootfs_image
, add_jffs2_eof
, file_system_partition
);
1520 /* Some devices need the extra-para partition to accept the firmware */
1521 if (strcasecmp(info
->id
, "ARCHER-C25-V1") == 0 ||
1522 strcasecmp(info
->id
, "ARCHER-C60-V2") == 0 ||
1523 strcasecmp(info
->id
, "TLWR1043NV5") == 0) {
1524 const char mdat
[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
1525 parts
[5] = put_data("extra-para", mdat
, 11);
1526 } else if (strcasecmp(info
->id
, "ARCHER-C7-V4") == 0 || strcasecmp(info
->id
, "ARCHER-C7-V5") == 0) {
1527 const char mdat
[11] = {0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x01, 0x00, 0x00};
1528 parts
[5] = put_data("extra-para", mdat
, 11);
1534 image
= generate_sysupgrade_image(info
, parts
, &len
);
1536 image
= generate_factory_image(info
, parts
, &len
);
1538 FILE *file
= fopen(output
, "wb");
1540 error(1, errno
, "unable to open output file");
1542 if (fwrite(image
, len
, 1, file
) != 1)
1543 error(1, 0, "unable to write output file");
1549 for (i
= 0; parts
[i
].name
; i
++)
1550 free_image_partition(parts
[i
]);
1554 static void usage(const char *argv0
) {
1556 "Usage: %s [OPTIONS...]\n"
1559 " -h show this help\n"
1561 "Create a new image:\n"
1562 " -B <board> create image for the board specified with <board>\n"
1563 " -k <file> read kernel image from the file <file>\n"
1564 " -r <file> read rootfs image from the file <file>\n"
1565 " -o <file> write output to the file <file>\n"
1566 " -V <rev> sets the revision number to <rev>\n"
1567 " -j add jffs2 end-of-filesystem markers\n"
1568 " -S create sysupgrade instead of factory image\n"
1569 "Extract an old image:\n"
1570 " -x <file> extract all oem firmware partition\n"
1571 " -d <dir> destination to extract the firmware partition\n"
1572 " -z <file> convert an oem firmware into a sysupgade file. Use -o for output file\n",
1578 static struct device_info
*find_board(const char *id
)
1580 struct device_info
*board
= NULL
;
1582 for (board
= boards
; board
->id
!= NULL
; board
++)
1583 if (strcasecmp(id
, board
->id
) == 0)
1589 static int add_flash_partition(
1590 struct flash_partition_entry
*part_list
,
1597 /* check if the list has a free entry */
1598 for (ptr
= 0; ptr
< max_entries
; ptr
++, part_list
++) {
1599 if (part_list
->name
== NULL
&&
1600 part_list
->base
== 0 &&
1601 part_list
->size
== 0)
1605 if (ptr
== max_entries
) {
1606 error(1, 0, "No free flash part entry available.");
1609 part_list
->name
= calloc(1, strlen(name
) + 1);
1610 memcpy((char *)part_list
->name
, name
, strlen(name
));
1611 part_list
->base
= base
;
1612 part_list
->size
= size
;
1617 /** read the partition table into struct flash_partition_entry */
1618 static int read_partition_table(
1619 FILE *file
, long offset
,
1620 struct flash_partition_entry
*entries
, size_t max_entries
,
1625 const char *parthdr
= NULL
;
1626 const char *fwuphdr
= "fwup-ptn";
1627 const char *flashhdr
= "partition";
1629 /* TODO: search for the partition table */
1639 error(1, 0, "Invalid partition table");
1642 if (fseek(file
, offset
, SEEK_SET
) < 0)
1643 error(1, errno
, "Can not seek in the firmware");
1645 if (fread(buf
, 1, 2048, file
) < 0)
1646 error(1, errno
, "Can not read fwup-ptn from the firmware");
1650 /* look for the partition header */
1651 if (memcmp(buf
, parthdr
, strlen(parthdr
)) != 0) {
1652 fprintf(stderr
, "DEBUG: can not find fwuphdr\n");
1657 end
= buf
+ sizeof(buf
);
1658 while ((ptr
+ strlen(parthdr
)) < end
&&
1659 memcmp(ptr
, parthdr
, strlen(parthdr
)) == 0) {
1663 char name
[32] = { 0 };
1665 unsigned long base
= 0;
1666 unsigned long size
= 0;
1668 end_part
= memchr(ptr
, '\n', (end
- ptr
));
1669 if (end_part
== NULL
) {
1670 /* in theory this should never happen, because a partition always ends with 0x09, 0x0D, 0x0A */
1674 for (int i
= 0; i
<= 4; i
++) {
1675 if (end_part
<= ptr
)
1678 end_element
= memchr(ptr
, 0x20, (end_part
- ptr
));
1679 if (end_element
== NULL
) {
1680 error(1, errno
, "Ignoring the rest of the partition entries.");
1685 /* partition header */
1687 ptr
= end_element
+ 1;
1691 name_len
= (end_element
- ptr
) > 31 ? 31 : (end_element
- ptr
);
1692 strncpy(name
, ptr
, name_len
);
1693 name
[name_len
] = '\0';
1694 ptr
= end_element
+ 1;
1699 ptr
= end_element
+ 1;
1704 base
= strtoul(ptr
, NULL
, 16);
1705 ptr
= end_element
+ 1;
1710 ptr
= end_element
+ 1;
1711 /* actual size. The last element doesn't have a sepeartor */
1712 size
= strtoul(ptr
, NULL
, 16);
1713 /* the part ends with 0x09, 0x0d, 0x0a */
1715 add_flash_partition(entries
, max_entries
, name
, base
, size
);
1724 static void write_partition(
1726 size_t firmware_offset
,
1727 struct flash_partition_entry
*entry
,
1733 fseek(input_file
, entry
->base
+ firmware_offset
, SEEK_SET
);
1735 for (offset
= 0; sizeof(buf
) + offset
<= entry
->size
; offset
+= sizeof(buf
)) {
1736 if (fread(buf
, sizeof(buf
), 1, input_file
) < 0)
1737 error(1, errno
, "Can not read partition from input_file");
1739 if (fwrite(buf
, sizeof(buf
), 1, output_file
) < 0)
1740 error(1, errno
, "Can not write partition to output_file");
1742 /* write last chunk smaller than buffer */
1743 if (offset
< entry
->size
) {
1744 offset
= entry
->size
- offset
;
1745 if (fread(buf
, offset
, 1, input_file
) < 0)
1746 error(1, errno
, "Can not read partition from input_file");
1747 if (fwrite(buf
, offset
, 1, output_file
) < 0)
1748 error(1, errno
, "Can not write partition to output_file");
1752 static int extract_firmware_partition(FILE *input_file
, size_t firmware_offset
, struct flash_partition_entry
*entry
, const char *output_directory
)
1755 char output
[PATH_MAX
];
1757 snprintf(output
, PATH_MAX
, "%s/%s", output_directory
, entry
->name
);
1758 output_file
= fopen(output
, "wb+");
1759 if (output_file
== NULL
) {
1760 error(1, errno
, "Can not open output file %s", output
);
1763 write_partition(input_file
, firmware_offset
, entry
, output_file
);
1765 fclose(output_file
);
1770 /** extract all partitions from the firmware file */
1771 static int extract_firmware(const char *input
, const char *output_directory
)
1773 struct flash_partition_entry entries
[16] = { 0 };
1774 size_t max_entries
= 16;
1775 size_t firmware_offset
= 0x1014;
1778 struct stat statbuf
;
1780 /* check input file */
1781 if (stat(input
, &statbuf
)) {
1782 error(1, errno
, "Can not read input firmware %s", input
);
1785 /* check if output directory exists */
1786 if (stat(output_directory
, &statbuf
)) {
1787 error(1, errno
, "Failed to stat output directory %s", output_directory
);
1790 if ((statbuf
.st_mode
& S_IFMT
) != S_IFDIR
) {
1791 error(1, errno
, "Given output directory is not a directory %s", output_directory
);
1794 input_file
= fopen(input
, "rb");
1796 if (read_partition_table(input_file
, firmware_offset
, entries
, 16, 0) != 0) {
1797 error(1, 0, "Error can not read the partition table (fwup-ptn)");
1800 for (int i
= 0; i
< max_entries
; i
++) {
1801 if (entries
[i
].name
== NULL
&&
1802 entries
[i
].base
== 0 &&
1803 entries
[i
].size
== 0)
1806 extract_firmware_partition(input_file
, firmware_offset
, &entries
[i
], output_directory
);
1812 static struct flash_partition_entry
*find_partition(
1813 struct flash_partition_entry
*entries
, size_t max_entries
,
1814 const char *name
, const char *error_msg
)
1816 for (int i
= 0; i
< max_entries
; i
++, entries
++) {
1817 if (strcmp(entries
->name
, name
) == 0)
1821 error(1, 0, "%s", error_msg
);
1825 static void write_ff(FILE *output_file
, size_t size
)
1830 memset(buf
, 0xff, sizeof(buf
));
1832 for (offset
= 0; offset
+ sizeof(buf
) < size
; offset
+= sizeof(buf
)) {
1833 if (fwrite(buf
, sizeof(buf
), 1, output_file
) < 0)
1834 error(1, errno
, "Can not write 0xff to output_file");
1837 /* write last chunk smaller than buffer */
1838 if (offset
< size
) {
1839 offset
= size
- offset
;
1840 if (fwrite(buf
, offset
, 1, output_file
) < 0)
1841 error(1, errno
, "Can not write partition to output_file");
1845 static void convert_firmware(const char *input
, const char *output
)
1847 struct flash_partition_entry fwup
[MAX_PARTITIONS
] = { 0 };
1848 struct flash_partition_entry flash
[MAX_PARTITIONS
] = { 0 };
1849 struct flash_partition_entry
*fwup_os_image
= NULL
, *fwup_file_system
= NULL
;
1850 struct flash_partition_entry
*flash_os_image
= NULL
, *flash_file_system
= NULL
;
1851 struct flash_partition_entry
*fwup_partition_table
= NULL
;
1852 size_t firmware_offset
= 0x1014;
1853 FILE *input_file
, *output_file
;
1855 struct stat statbuf
;
1857 /* check input file */
1858 if (stat(input
, &statbuf
)) {
1859 error(1, errno
, "Can not read input firmware %s", input
);
1862 input_file
= fopen(input
, "rb");
1864 error(1, 0, "Can not open input firmware %s", input
);
1866 output_file
= fopen(output
, "wb");
1868 error(1, 0, "Can not open output firmware %s", output
);
1870 if (read_partition_table(input_file
, firmware_offset
, fwup
, MAX_PARTITIONS
, 0) != 0) {
1871 error(1, 0, "Error can not read the partition table (fwup-ptn)");
1874 fwup_os_image
= find_partition(fwup
, MAX_PARTITIONS
,
1875 "os-image", "Error can not find os-image partition (fwup)");
1876 fwup_file_system
= find_partition(fwup
, MAX_PARTITIONS
,
1877 "file-system", "Error can not find file-system partition (fwup)");
1878 fwup_partition_table
= find_partition(fwup
, MAX_PARTITIONS
,
1879 "partition-table", "Error can not find partition-table partition");
1881 /* the flash partition table has a 0x00000004 magic haeder */
1882 if (read_partition_table(input_file
, firmware_offset
+ fwup_partition_table
->base
+ 4, flash
, MAX_PARTITIONS
, 1) != 0)
1883 error(1, 0, "Error can not read the partition table (flash)");
1885 flash_os_image
= find_partition(flash
, MAX_PARTITIONS
,
1886 "os-image", "Error can not find os-image partition (flash)");
1887 flash_file_system
= find_partition(flash
, MAX_PARTITIONS
,
1888 "file-system", "Error can not find file-system partition (flash)");
1890 /* write os_image to 0x0 */
1891 write_partition(input_file
, firmware_offset
, fwup_os_image
, output_file
);
1892 write_ff(output_file
, flash_os_image
->size
- fwup_os_image
->size
);
1894 /* write file-system behind os_image */
1895 fseek(output_file
, flash_file_system
->base
- flash_os_image
->base
, SEEK_SET
);
1896 write_partition(input_file
, firmware_offset
, fwup_file_system
, output_file
);
1897 write_ff(output_file
, flash_file_system
->size
- fwup_file_system
->size
);
1899 fclose(output_file
);
1903 int main(int argc
, char *argv
[]) {
1904 const char *board
= NULL
, *kernel_image
= NULL
, *rootfs_image
= NULL
, *output
= NULL
;
1905 const char *extract_image
= NULL
, *output_directory
= NULL
, *convert_image
= NULL
;
1906 bool add_jffs2_eof
= false, sysupgrade
= false;
1908 struct device_info
*info
;
1909 set_source_date_epoch();
1914 c
= getopt(argc
, argv
, "B:k:r:o:V:jSh:x:d:z:");
1924 kernel_image
= optarg
;
1928 rootfs_image
= optarg
;
1936 sscanf(optarg
, "r%u", &rev
);
1940 add_jffs2_eof
= true;
1952 output_directory
= optarg
;
1956 extract_image
= optarg
;
1960 convert_image
= optarg
;
1969 if (extract_image
|| output_directory
) {
1971 error(1, 0, "No factory/oem image given via -x <file>. Output directory is only valid with -x");
1972 if (!output_directory
)
1973 error(1, 0, "Can not extract an image without output directory. Use -d <dir>");
1974 extract_firmware(extract_image
, output_directory
);
1975 } else if (convert_image
) {
1977 error(1, 0, "Can not convert a factory/oem image into sysupgrade image without output file. Use -o <file>");
1978 convert_firmware(convert_image
, output
);
1981 error(1, 0, "no board has been specified");
1983 error(1, 0, "no kernel image has been specified");
1985 error(1, 0, "no rootfs image has been specified");
1987 error(1, 0, "no output filename has been specified");
1989 info
= find_board(board
);
1992 error(1, 0, "unsupported board %s", board
);
1994 build_image(output
, kernel_image
, rootfs_image
, rev
, add_jffs2_eof
, sysupgrade
, info
);