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