OpenWrt – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 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 }