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