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