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