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