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