firmware-utils: tplink-safeloader: add support for TP-Link RE350
[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
50 #include "md5.h"
51
52
53 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
54
55
56 #define MAX_PARTITIONS 32
57
58 /** An image partition table entry */
59 struct image_partition_entry {
60 const char *name;
61 size_t size;
62 uint8_t *data;
63 };
64
65 /** A flash partition table entry */
66 struct flash_partition_entry {
67 const char *name;
68 uint32_t base;
69 uint32_t size;
70 };
71
72 /** Firmware layout description */
73 struct device_info {
74 const char *id;
75 const char *vendor;
76 const char *support_list;
77 char support_trail;
78 const char *soft_ver;
79 const struct flash_partition_entry partitions[MAX_PARTITIONS+1];
80 const char *first_sysupgrade_partition;
81 const char *last_sysupgrade_partition;
82 };
83
84 /** The content of the soft-version structure */
85 struct __attribute__((__packed__)) soft_version {
86 uint32_t magic;
87 uint32_t zero;
88 uint8_t pad1;
89 uint8_t version_major;
90 uint8_t version_minor;
91 uint8_t version_patch;
92 uint8_t year_hi;
93 uint8_t year_lo;
94 uint8_t month;
95 uint8_t day;
96 uint32_t rev;
97 uint8_t pad2;
98 };
99
100
101 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
102
103
104 /**
105 Salt for the MD5 hash
106
107 Fortunately, TP-LINK seems to use the same salt for most devices which use
108 the new image format.
109 */
110 static const uint8_t md5_salt[16] = {
111 0x7a, 0x2b, 0x15, 0xed,
112 0x9b, 0x98, 0x59, 0x6d,
113 0xe5, 0x04, 0xab, 0x44,
114 0xac, 0x2a, 0x9f, 0x4e,
115 };
116
117
118 /** Firmware layout table */
119 static struct device_info boards[] = {
120 /** Firmware layout for the CPE210/220 */
121 {
122 .id = "CPE210",
123 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
124 .support_list =
125 "SupportList:\r\n"
126 "CPE210(TP-LINK|UN|N300-2):1.0\r\n"
127 "CPE210(TP-LINK|UN|N300-2):1.1\r\n"
128 "CPE210(TP-LINK|US|N300-2):1.1\r\n"
129 "CPE210(TP-LINK|EU|N300-2):1.1\r\n"
130 "CPE220(TP-LINK|UN|N300-2):1.1\r\n"
131 "CPE220(TP-LINK|US|N300-2):1.1\r\n"
132 "CPE220(TP-LINK|EU|N300-2):1.1\r\n",
133 .support_trail = '\xff',
134 .soft_ver = NULL,
135
136 .partitions = {
137 {"fs-uboot", 0x00000, 0x20000},
138 {"partition-table", 0x20000, 0x02000},
139 {"default-mac", 0x30000, 0x00020},
140 {"product-info", 0x31100, 0x00100},
141 {"signature", 0x32000, 0x00400},
142 {"os-image", 0x40000, 0x170000},
143 {"soft-version", 0x1b0000, 0x00100},
144 {"support-list", 0x1b1000, 0x00400},
145 {"file-system", 0x1c0000, 0x600000},
146 {"user-config", 0x7c0000, 0x10000},
147 {"default-config", 0x7d0000, 0x10000},
148 {"log", 0x7e0000, 0x10000},
149 {"radio", 0x7f0000, 0x10000},
150 {NULL, 0, 0}
151 },
152
153 .first_sysupgrade_partition = "os-image",
154 .last_sysupgrade_partition = "file-system",
155 },
156
157 /** Firmware layout for the CPE510/520 */
158 {
159 .id = "CPE510",
160 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
161 .support_list =
162 "SupportList:\r\n"
163 "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
164 "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
165 "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
166 "CPE510(TP-LINK|US|N300-5):1.1\r\n"
167 "CPE510(TP-LINK|EU|N300-5):1.1\r\n"
168 "CPE520(TP-LINK|UN|N300-5):1.1\r\n"
169 "CPE520(TP-LINK|US|N300-5):1.1\r\n"
170 "CPE520(TP-LINK|EU|N300-5):1.1\r\n",
171 .support_trail = '\xff',
172 .soft_ver = NULL,
173
174 .partitions = {
175 {"fs-uboot", 0x00000, 0x20000},
176 {"partition-table", 0x20000, 0x02000},
177 {"default-mac", 0x30000, 0x00020},
178 {"product-info", 0x31100, 0x00100},
179 {"signature", 0x32000, 0x00400},
180 {"os-image", 0x40000, 0x170000},
181 {"soft-version", 0x1b0000, 0x00100},
182 {"support-list", 0x1b1000, 0x00400},
183 {"file-system", 0x1c0000, 0x600000},
184 {"user-config", 0x7c0000, 0x10000},
185 {"default-config", 0x7d0000, 0x10000},
186 {"log", 0x7e0000, 0x10000},
187 {"radio", 0x7f0000, 0x10000},
188 {NULL, 0, 0}
189 },
190
191 .first_sysupgrade_partition = "os-image",
192 .last_sysupgrade_partition = "file-system",
193 },
194
195 {
196 .id = "WBS210",
197 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
198 .support_list =
199 "SupportList:\r\n"
200 "WBS210(TP-LINK|UN|N300-2):1.20\r\n"
201 "WBS210(TP-LINK|US|N300-2):1.20\r\n"
202 "WBS210(TP-LINK|EU|N300-2):1.20\r\n",
203 .support_trail = '\xff',
204 .soft_ver = NULL,
205
206 .partitions = {
207 {"fs-uboot", 0x00000, 0x20000},
208 {"partition-table", 0x20000, 0x02000},
209 {"default-mac", 0x30000, 0x00020},
210 {"product-info", 0x31100, 0x00100},
211 {"signature", 0x32000, 0x00400},
212 {"os-image", 0x40000, 0x170000},
213 {"soft-version", 0x1b0000, 0x00100},
214 {"support-list", 0x1b1000, 0x00400},
215 {"file-system", 0x1c0000, 0x600000},
216 {"user-config", 0x7c0000, 0x10000},
217 {"default-config", 0x7d0000, 0x10000},
218 {"log", 0x7e0000, 0x10000},
219 {"radio", 0x7f0000, 0x10000},
220 {NULL, 0, 0}
221 },
222
223 .first_sysupgrade_partition = "os-image",
224 .last_sysupgrade_partition = "file-system",
225 },
226
227 {
228 .id = "WBS510",
229 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
230 .support_list =
231 "SupportList:\r\n"
232 "WBS510(TP-LINK|UN|N300-5):1.20\r\n"
233 "WBS510(TP-LINK|US|N300-5):1.20\r\n"
234 "WBS510(TP-LINK|EU|N300-5):1.20\r\n",
235 .support_trail = '\xff',
236 .soft_ver = NULL,
237
238 .partitions = {
239 {"fs-uboot", 0x00000, 0x20000},
240 {"partition-table", 0x20000, 0x02000},
241 {"default-mac", 0x30000, 0x00020},
242 {"product-info", 0x31100, 0x00100},
243 {"signature", 0x32000, 0x00400},
244 {"os-image", 0x40000, 0x170000},
245 {"soft-version", 0x1b0000, 0x00100},
246 {"support-list", 0x1b1000, 0x00400},
247 {"file-system", 0x1c0000, 0x600000},
248 {"user-config", 0x7c0000, 0x10000},
249 {"default-config", 0x7d0000, 0x10000},
250 {"log", 0x7e0000, 0x10000},
251 {"radio", 0x7f0000, 0x10000},
252 {NULL, 0, 0}
253 },
254
255 .first_sysupgrade_partition = "os-image",
256 .last_sysupgrade_partition = "file-system",
257 },
258
259 /** Firmware layout for the C2600 */
260 {
261 .id = "C2600",
262 .vendor = "",
263 .support_list =
264 "SupportList:\r\n"
265 "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n",
266 .support_trail = '\x00',
267 .soft_ver = NULL,
268
269 .partitions = {
270 {"SBL1", 0x00000, 0x20000},
271 {"MIBIB", 0x20000, 0x20000},
272 {"SBL2", 0x40000, 0x20000},
273 {"SBL3", 0x60000, 0x30000},
274 {"DDRCONFIG", 0x90000, 0x10000},
275 {"SSD", 0xa0000, 0x10000},
276 {"TZ", 0xb0000, 0x30000},
277 {"RPM", 0xe0000, 0x20000},
278 {"fs-uboot", 0x100000, 0x70000},
279 {"uboot-env", 0x170000, 0x40000},
280 {"radio", 0x1b0000, 0x40000},
281 {"os-image", 0x1f0000, 0x200000},
282 {"file-system", 0x3f0000, 0x1b00000},
283 {"default-mac", 0x1ef0000, 0x00200},
284 {"pin", 0x1ef0200, 0x00200},
285 {"product-info", 0x1ef0400, 0x0fc00},
286 {"partition-table", 0x1f00000, 0x10000},
287 {"soft-version", 0x1f10000, 0x10000},
288 {"support-list", 0x1f20000, 0x10000},
289 {"profile", 0x1f30000, 0x10000},
290 {"default-config", 0x1f40000, 0x10000},
291 {"user-config", 0x1f50000, 0x40000},
292 {"qos-db", 0x1f90000, 0x40000},
293 {"usb-config", 0x1fd0000, 0x10000},
294 {"log", 0x1fe0000, 0x20000},
295 {NULL, 0, 0}
296 },
297
298 .first_sysupgrade_partition = "os-image",
299 .last_sysupgrade_partition = "file-system"
300 },
301
302 /** Firmware layout for the C25v1 */
303 {
304 .id = "ARCHER-C25-V1",
305 .support_list =
306 "SupportList:\n"
307 "{product_name:ArcherC25,product_ver:1.0.0,special_id:00000000}\n"
308 "{product_name:ArcherC25,product_ver:1.0.0,special_id:55530000}\n"
309 "{product_name:ArcherC25,product_ver:1.0.0,special_id:45550000}\n",
310 .support_trail = '\x00',
311 .soft_ver = "soft_ver:1.0.0\n",
312
313 /**
314 We use a bigger os-image partition than the stock images (and thus
315 smaller file-system), as our kernel doesn't fit in the stock firmware's
316 1MB os-image.
317 */
318 .partitions = {
319 {"factory-boot", 0x00000, 0x20000},
320 {"fs-uboot", 0x20000, 0x10000},
321 {"os-image", 0x30000, 0x180000}, /* Stock: base 0x30000 size 0x100000 */
322 {"file-system", 0x1b0000, 0x620000}, /* Stock: base 0x130000 size 0x6a0000 */
323 {"user-config", 0x7d0000, 0x04000},
324 {"default-mac", 0x7e0000, 0x00100},
325 {"device-id", 0x7e0100, 0x00100},
326 {"extra-para", 0x7e0200, 0x00100},
327 {"pin", 0x7e0300, 0x00100},
328 {"support-list", 0x7e0400, 0x00400},
329 {"soft-version", 0x7e0800, 0x00400},
330 {"product-info", 0x7e0c00, 0x01400},
331 {"partition-table", 0x7e2000, 0x01000},
332 {"profile", 0x7e3000, 0x01000},
333 {"default-config", 0x7e4000, 0x04000},
334 {"merge-config", 0x7ec000, 0x02000},
335 {"qos-db", 0x7ee000, 0x02000},
336 {"radio", 0x7f0000, 0x10000},
337 {NULL, 0, 0}
338 },
339
340 .first_sysupgrade_partition = "os-image",
341 .last_sysupgrade_partition = "file-system",
342 },
343
344 /** Firmware layout for the C58v1 */
345 {
346 .id = "ARCHER-C58-V1",
347 .vendor = "",
348 .support_list =
349 "SupportList:\r\n"
350 "{product_name:Archer C58,product_ver:1.0.0,special_id:00000000}\r\n"
351 "{product_name:Archer C58,product_ver:1.0.0,special_id:45550000}\r\n"
352 "{product_name:Archer C58,product_ver:1.0.0,special_id:55530000}\r\n",
353 .support_trail = '\x00',
354 .soft_ver = "soft_ver:1.0.0\n",
355
356 .partitions = {
357 {"fs-uboot", 0x00000, 0x10000},
358 {"default-mac", 0x10000, 0x00200},
359 {"pin", 0x10200, 0x00200},
360 {"product-info", 0x10400, 0x00100},
361 {"partition-table", 0x10500, 0x00800},
362 {"soft-version", 0x11300, 0x00200},
363 {"support-list", 0x11500, 0x00100},
364 {"device-id", 0x11600, 0x00100},
365 {"profile", 0x11700, 0x03900},
366 {"default-config", 0x15000, 0x04000},
367 {"user-config", 0x19000, 0x04000},
368 {"os-image", 0x20000, 0x150000},
369 {"file-system", 0x170000, 0x678000},
370 {"certyficate", 0x7e8000, 0x08000},
371 {"radio", 0x7f0000, 0x10000},
372 {NULL, 0, 0}
373 },
374
375 .first_sysupgrade_partition = "os-image",
376 .last_sysupgrade_partition = "file-system",
377 },
378
379 /** Firmware layout for the C59v1 */
380 {
381 .id = "ARCHER-C59-V1",
382 .vendor = "",
383 .support_list =
384 "SupportList:\r\n"
385 "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n"
386 "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n"
387 "{product_name:Archer C59,product_ver:1.0.0,special_id:52550000}\r\n"
388 "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n",
389 .support_trail = '\x00',
390 .soft_ver = "soft_ver:1.0.0\n",
391
392 .partitions = {
393 {"fs-uboot", 0x00000, 0x10000},
394 {"default-mac", 0x10000, 0x00200},
395 {"pin", 0x10200, 0x00200},
396 {"device-id", 0x10400, 0x00100},
397 {"product-info", 0x10500, 0x0fb00},
398 {"os-image", 0x20000, 0x180000},
399 {"file-system", 0x1a0000, 0xcb0000},
400 {"partition-table", 0xe50000, 0x10000},
401 {"soft-version", 0xe60000, 0x10000},
402 {"support-list", 0xe70000, 0x10000},
403 {"profile", 0xe80000, 0x10000},
404 {"default-config", 0xe90000, 0x10000},
405 {"user-config", 0xea0000, 0x40000},
406 {"usb-config", 0xee0000, 0x10000},
407 {"certificate", 0xef0000, 0x10000},
408 {"qos-db", 0xf00000, 0x40000},
409 {"log", 0xfe0000, 0x10000},
410 {"radio", 0xff0000, 0x10000},
411 {NULL, 0, 0}
412 },
413
414 .first_sysupgrade_partition = "os-image",
415 .last_sysupgrade_partition = "file-system",
416 },
417
418 /** Firmware layout for the C60v1 */
419 {
420 .id = "ARCHER-C60-V1",
421 .vendor = "",
422 .support_list =
423 "SupportList:\r\n"
424 "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n"
425 "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n"
426 "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n",
427 .support_trail = '\x00',
428 .soft_ver = "soft_ver:1.0.0\n",
429
430 .partitions = {
431 {"fs-uboot", 0x00000, 0x10000},
432 {"default-mac", 0x10000, 0x00200},
433 {"pin", 0x10200, 0x00200},
434 {"product-info", 0x10400, 0x00100},
435 {"partition-table", 0x10500, 0x00800},
436 {"soft-version", 0x11300, 0x00200},
437 {"support-list", 0x11500, 0x00100},
438 {"device-id", 0x11600, 0x00100},
439 {"profile", 0x11700, 0x03900},
440 {"default-config", 0x15000, 0x04000},
441 {"user-config", 0x19000, 0x04000},
442 {"os-image", 0x20000, 0x150000},
443 {"file-system", 0x170000, 0x678000},
444 {"certyficate", 0x7e8000, 0x08000},
445 {"radio", 0x7f0000, 0x10000},
446 {NULL, 0, 0}
447 },
448
449 .first_sysupgrade_partition = "os-image",
450 .last_sysupgrade_partition = "file-system",
451 },
452
453 /** Firmware layout for the C5 */
454 {
455 .id = "ARCHER-C5-V2",
456 .vendor = "",
457 .support_list =
458 "SupportList:\r\n"
459 "{product_name:ArcherC5,product_ver:2.0.0,special_id:00000000}\r\n"
460 "{product_name:ArcherC5,product_ver:2.0.0,special_id:55530000}\r\n"
461 "{product_name:ArcherC5,product_ver:2.0.0,special_id:4A500000}\r\n", /* JP version */
462 .support_trail = '\x00',
463 .soft_ver = NULL,
464
465 .partitions = {
466 {"fs-uboot", 0x00000, 0x40000},
467 {"os-image", 0x40000, 0x200000},
468 {"file-system", 0x240000, 0xc00000},
469 {"default-mac", 0xe40000, 0x00200},
470 {"pin", 0xe40200, 0x00200},
471 {"product-info", 0xe40400, 0x00200},
472 {"partition-table", 0xe50000, 0x10000},
473 {"soft-version", 0xe60000, 0x00200},
474 {"support-list", 0xe61000, 0x0f000},
475 {"profile", 0xe70000, 0x10000},
476 {"default-config", 0xe80000, 0x10000},
477 {"user-config", 0xe90000, 0x50000},
478 {"log", 0xee0000, 0x100000},
479 {"radio_bk", 0xfe0000, 0x10000},
480 {"radio", 0xff0000, 0x10000},
481 {NULL, 0, 0}
482 },
483
484 .first_sysupgrade_partition = "os-image",
485 .last_sysupgrade_partition = "file-system"
486 },
487
488 /** Firmware layout for the C9 */
489 {
490 .id = "ARCHERC9",
491 .vendor = "",
492 .support_list =
493 "SupportList:\n"
494 "{product_name:ArcherC9,"
495 "product_ver:1.0.0,"
496 "special_id:00000000}\n",
497 .support_trail = '\x00',
498 .soft_ver = NULL,
499
500 .partitions = {
501 {"fs-uboot", 0x00000, 0x40000},
502 {"os-image", 0x40000, 0x200000},
503 {"file-system", 0x240000, 0xc00000},
504 {"default-mac", 0xe40000, 0x00200},
505 {"pin", 0xe40200, 0x00200},
506 {"product-info", 0xe40400, 0x00200},
507 {"partition-table", 0xe50000, 0x10000},
508 {"soft-version", 0xe60000, 0x00200},
509 {"support-list", 0xe61000, 0x0f000},
510 {"profile", 0xe70000, 0x10000},
511 {"default-config", 0xe80000, 0x10000},
512 {"user-config", 0xe90000, 0x50000},
513 {"log", 0xee0000, 0x100000},
514 {"radio_bk", 0xfe0000, 0x10000},
515 {"radio", 0xff0000, 0x10000},
516 {NULL, 0, 0}
517 },
518
519 .first_sysupgrade_partition = "os-image",
520 .last_sysupgrade_partition = "file-system"
521 },
522
523 /** Firmware layout for the EAP120 */
524 {
525 .id = "EAP120",
526 .vendor = "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
527 .support_list =
528 "SupportList:\r\n"
529 "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
530 .support_trail = '\xff',
531 .soft_ver = NULL,
532
533 .partitions = {
534 {"fs-uboot", 0x00000, 0x20000},
535 {"partition-table", 0x20000, 0x02000},
536 {"default-mac", 0x30000, 0x00020},
537 {"support-list", 0x31000, 0x00100},
538 {"product-info", 0x31100, 0x00100},
539 {"soft-version", 0x32000, 0x00100},
540 {"os-image", 0x40000, 0x180000},
541 {"file-system", 0x1c0000, 0x600000},
542 {"user-config", 0x7c0000, 0x10000},
543 {"backup-config", 0x7d0000, 0x10000},
544 {"log", 0x7e0000, 0x10000},
545 {"radio", 0x7f0000, 0x10000},
546 {NULL, 0, 0}
547 },
548
549 .first_sysupgrade_partition = "os-image",
550 .last_sysupgrade_partition = "file-system"
551 },
552
553 /** Firmware layout for the TL-WA850RE v2 */
554 {
555 .id = "TLWA850REV2",
556 .vendor = "",
557 .support_list =
558 "SupportList:\n"
559 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55530000}\n"
560 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:00000000}\n"
561 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55534100}\n"
562 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:45550000}\n"
563 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4B520000}\n"
564 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:42520000}\n"
565 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4A500000}\n"
566 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:43410000}\n"
567 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:41550000}\n"
568 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:52550000}\n",
569 .support_trail = '\x00',
570 .soft_ver = NULL,
571
572 /**
573 576KB were moved from file-system to os-image
574 in comparison to the stock image
575 */
576 .partitions = {
577 {"fs-uboot", 0x00000, 0x20000},
578 {"os-image", 0x20000, 0x150000},
579 {"file-system", 0x170000, 0x240000},
580 {"partition-table", 0x3b0000, 0x02000},
581 {"default-mac", 0x3c0000, 0x00020},
582 {"pin", 0x3c0100, 0x00020},
583 {"product-info", 0x3c1000, 0x01000},
584 {"soft-version", 0x3c2000, 0x00100},
585 {"support-list", 0x3c3000, 0x01000},
586 {"profile", 0x3c4000, 0x08000},
587 {"user-config", 0x3d0000, 0x10000},
588 {"default-config", 0x3e0000, 0x10000},
589 {"radio", 0x3f0000, 0x10000},
590 {NULL, 0, 0}
591 },
592
593 .first_sysupgrade_partition = "os-image",
594 .last_sysupgrade_partition = "file-system"
595 },
596
597 /** Firmware layout for the TL-WA855RE v1 */
598 {
599 .id = "TLWA855REV1",
600 .vendor = "",
601 .support_list =
602 "SupportList:\n"
603 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:00000000}\n"
604 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:55530000}\n"
605 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:45550000}\n"
606 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4B520000}\n"
607 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:42520000}\n"
608 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4A500000}\n"
609 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:43410000}\n"
610 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:41550000}\n"
611 "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:52550000}\n",
612 .support_trail = '\x00',
613 .soft_ver = NULL,
614
615 .partitions = {
616 {"fs-uboot", 0x00000, 0x20000},
617 {"os-image", 0x20000, 0x150000},
618 {"file-system", 0x170000, 0x240000},
619 {"partition-table", 0x3b0000, 0x02000},
620 {"default-mac", 0x3c0000, 0x00020},
621 {"pin", 0x3c0100, 0x00020},
622 {"product-info", 0x3c1000, 0x01000},
623 {"soft-version", 0x3c2000, 0x00100},
624 {"support-list", 0x3c3000, 0x01000},
625 {"profile", 0x3c4000, 0x08000},
626 {"user-config", 0x3d0000, 0x10000},
627 {"default-config", 0x3e0000, 0x10000},
628 {"radio", 0x3f0000, 0x10000},
629 {NULL, 0, 0}
630 },
631
632 .first_sysupgrade_partition = "os-image",
633 .last_sysupgrade_partition = "file-system"
634 },
635
636 /** Firmware layout for the TL-WR1043 v4 */
637 {
638 .id = "TLWR1043NDV4",
639 .vendor = "",
640 .support_list =
641 "SupportList:\n"
642 "{product_name:TL-WR1043ND,product_ver:4.0.0,special_id:45550000}\n",
643 .support_trail = '\x00',
644 .soft_ver = NULL,
645
646 /**
647 We use a bigger os-image partition than the stock images (and thus
648 smaller file-system), as our kernel doesn't fit in the stock firmware's
649 1MB os-image.
650 */
651 .partitions = {
652 {"fs-uboot", 0x00000, 0x20000},
653 {"os-image", 0x20000, 0x180000},
654 {"file-system", 0x1a0000, 0xdb0000},
655 {"default-mac", 0xf50000, 0x00200},
656 {"pin", 0xf50200, 0x00200},
657 {"product-info", 0xf50400, 0x0fc00},
658 {"soft-version", 0xf60000, 0x0b000},
659 {"support-list", 0xf6b000, 0x04000},
660 {"profile", 0xf70000, 0x04000},
661 {"default-config", 0xf74000, 0x0b000},
662 {"user-config", 0xf80000, 0x40000},
663 {"partition-table", 0xfc0000, 0x10000},
664 {"log", 0xfd0000, 0x20000},
665 {"radio", 0xff0000, 0x10000},
666 {NULL, 0, 0}
667 },
668
669 .first_sysupgrade_partition = "os-image",
670 .last_sysupgrade_partition = "file-system"
671 },
672
673 /** Firmware layout for the TL-WR902AC v1 */
674 {
675 .id = "TL-WR902AC-V1",
676 .vendor = "",
677 .support_list =
678 "SupportList:\n"
679 "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:45550000}\n"
680 "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:55530000}\n",
681 .support_trail = '\x00',
682 .soft_ver = NULL,
683
684 /**
685 384KB were moved from file-system to os-image
686 in comparison to the stock image
687 */
688 .partitions = {
689 {"fs-uboot", 0x00000, 0x20000},
690 {"os-image", 0x20000, 0x160000},
691 {"file-system", 0x180000, 0x5d0000},
692 {"default-mac", 0x750000, 0x00200},
693 {"pin", 0x750200, 0x00200},
694 {"product-info", 0x750400, 0x0fc00},
695 {"soft-version", 0x760000, 0x0b000},
696 {"support-list", 0x76b000, 0x04000},
697 {"profile", 0x770000, 0x04000},
698 {"default-config", 0x774000, 0x0b000},
699 {"user-config", 0x780000, 0x40000},
700 {"partition-table", 0x7c0000, 0x10000},
701 {"log", 0x7d0000, 0x20000},
702 {"radio", 0x7f0000, 0x10000},
703 {NULL, 0, 0}
704 },
705
706 .first_sysupgrade_partition = "os-image",
707 .last_sysupgrade_partition = "file-system",
708 },
709
710 /** Firmware layout for the TL-WR942N V1 */
711 {
712 .id = "TLWR942NV1",
713 .vendor = "",
714 .support_list =
715 "SupportList:\r\n"
716 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:00000000}\r\n"
717 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:52550000}\r\n",
718 .support_trail = '\x00',
719 .soft_ver = NULL,
720
721 .partitions = {
722 {"fs-uboot", 0x00000, 0x20000},
723 {"os-image", 0x20000, 0x150000},
724 {"file-system", 0x170000, 0xcd0000},
725 {"default-mac", 0xe40000, 0x00200},
726 {"pin", 0xe40200, 0x00200},
727 {"product-info", 0xe40400, 0x0fc00},
728 {"partition-table", 0xe50000, 0x10000},
729 {"soft-version", 0xe60000, 0x10000},
730 {"support-list", 0xe70000, 0x10000},
731 {"profile", 0xe80000, 0x10000},
732 {"default-config", 0xe90000, 0x10000},
733 {"user-config", 0xea0000, 0x40000},
734 {"qos-db", 0xee0000, 0x40000},
735 {"certificate", 0xf20000, 0x10000},
736 {"usb-config", 0xfb0000, 0x10000},
737 {"log", 0xfc0000, 0x20000},
738 {"radio-bk", 0xfe0000, 0x10000},
739 {"radio", 0xff0000, 0x10000},
740 {NULL, 0, 0}
741 },
742
743 .first_sysupgrade_partition = "os-image",
744 .last_sysupgrade_partition = "file-system",
745 },
746
747 /** Firmware layout for the RE350 v1 */
748 {
749 .id = "RE350-V1",
750 .vendor = "",
751 .support_list =
752 "SupportList:\n"
753 "{product_name:RE350,product_ver:1.0.0,special_id:45550000}\n"
754 "{product_name:RE350,product_ver:1.0.0,special_id:00000000}\n"
755 "{product_name:RE350,product_ver:1.0.0,special_id:41550000}\n"
756 "{product_name:RE350,product_ver:1.0.0,special_id:55530000}\n"
757 "{product_name:RE350,product_ver:1.0.0,special_id:43410000}\n"
758 "{product_name:RE350,product_ver:1.0.0,special_id:4b520000}\n"
759 "{product_name:RE350,product_ver:1.0.0,special_id:4a500000}\n",
760 .support_trail = '\x00',
761 .soft_ver = NULL,
762
763 /**
764 The original os-image partition is too small,
765 so we enlarge it to 1.6M
766 */
767 .partitions = {
768 {"fs-uboot", 0x00000, 0x20000},
769 {"os-image", 0x20000, 0x1a0000},
770 {"file-system", 0x1c0000, 0x440000},
771 {"partition-table", 0x600000, 0x02000},
772 {"default-mac", 0x610000, 0x00020},
773 {"pin", 0x610100, 0x00020},
774 {"product-info", 0x611100, 0x01000},
775 {"soft-version", 0x620000, 0x01000},
776 {"support-list", 0x621000, 0x01000},
777 {"profile", 0x622000, 0x08000},
778 {"user-config", 0x630000, 0x10000},
779 {"default-config", 0x640000, 0x10000},
780 {"radio", 0x7f0000, 0x10000},
781 {NULL, 0, 0}
782 },
783
784 .first_sysupgrade_partition = "os-image",
785 .last_sysupgrade_partition = "file-system"
786 },
787
788 /** Firmware layout for the RE450 */
789 {
790 .id = "RE450",
791 .vendor = "",
792 .support_list =
793 "SupportList:\r\n"
794 "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n"
795 "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n"
796 "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n"
797 "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n"
798 "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n"
799 "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n"
800 "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n"
801 "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n",
802 .support_trail = '\x00',
803 .soft_ver = NULL,
804
805 /**
806 The flash partition table for RE450;
807 it is almost the same as the one used by the stock images,
808 576KB were moved from file-system to os-image.
809 */
810 .partitions = {
811 {"fs-uboot", 0x00000, 0x20000},
812 {"os-image", 0x20000, 0x150000},
813 {"file-system", 0x170000, 0x4a0000},
814 {"partition-table", 0x600000, 0x02000},
815 {"default-mac", 0x610000, 0x00020},
816 {"pin", 0x610100, 0x00020},
817 {"product-info", 0x611100, 0x01000},
818 {"soft-version", 0x620000, 0x01000},
819 {"support-list", 0x621000, 0x01000},
820 {"profile", 0x622000, 0x08000},
821 {"user-config", 0x630000, 0x10000},
822 {"default-config", 0x640000, 0x10000},
823 {"radio", 0x7f0000, 0x10000},
824 {NULL, 0, 0}
825 },
826
827 .first_sysupgrade_partition = "os-image",
828 .last_sysupgrade_partition = "file-system"
829 },
830
831 {}
832 };
833
834 #define error(_ret, _errno, _str, ...) \
835 do { \
836 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
837 strerror(_errno)); \
838 if (_ret) \
839 exit(_ret); \
840 } while (0)
841
842
843 /** Stores a uint32 as big endian */
844 static inline void put32(uint8_t *buf, uint32_t val) {
845 buf[0] = val >> 24;
846 buf[1] = val >> 16;
847 buf[2] = val >> 8;
848 buf[3] = val;
849 }
850
851 /** Allocates a new image partition */
852 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
853 struct image_partition_entry entry = {name, len, malloc(len)};
854 if (!entry.data)
855 error(1, errno, "malloc");
856
857 return entry;
858 }
859
860 /** Frees an image partition */
861 static void free_image_partition(struct image_partition_entry entry) {
862 free(entry.data);
863 }
864
865 static time_t source_date_epoch = -1;
866 static void set_source_date_epoch() {
867 char *env = getenv("SOURCE_DATE_EPOCH");
868 char *endptr = env;
869 errno = 0;
870 if (env && *env) {
871 source_date_epoch = strtoull(env, &endptr, 10);
872 if (errno || (endptr && *endptr != '\0')) {
873 fprintf(stderr, "Invalid SOURCE_DATE_EPOCH");
874 exit(1);
875 }
876 }
877 }
878
879 /** Generates the partition-table partition */
880 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
881 struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
882
883 char *s = (char *)entry.data, *end = (char *)(s+entry.size);
884
885 *(s++) = 0x00;
886 *(s++) = 0x04;
887 *(s++) = 0x00;
888 *(s++) = 0x00;
889
890 size_t i;
891 for (i = 0; p[i].name; i++) {
892 size_t len = end-s;
893 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
894
895 if (w > len-1)
896 error(1, 0, "flash partition table overflow?");
897
898 s += w;
899 }
900
901 s++;
902
903 memset(s, 0xff, end-s);
904
905 return entry;
906 }
907
908
909 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
910 static inline uint8_t bcd(uint8_t v) {
911 return 0x10 * (v/10) + v%10;
912 }
913
914
915 /** Generates the soft-version partition */
916 static struct image_partition_entry make_soft_version(uint32_t rev) {
917 struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
918 struct soft_version *s = (struct soft_version *)entry.data;
919
920 time_t t;
921
922 if (source_date_epoch != -1)
923 t = source_date_epoch;
924 else if (time(&t) == (time_t)(-1))
925 error(1, errno, "time");
926
927 struct tm *tm = localtime(&t);
928
929 s->magic = htonl(0x0000000c);
930 s->zero = 0;
931 s->pad1 = 0xff;
932
933 s->version_major = 0;
934 s->version_minor = 0;
935 s->version_patch = 0;
936
937 s->year_hi = bcd((1900+tm->tm_year)/100);
938 s->year_lo = bcd(tm->tm_year%100);
939 s->month = bcd(tm->tm_mon+1);
940 s->day = bcd(tm->tm_mday);
941 s->rev = htonl(rev);
942
943 s->pad2 = 0xff;
944
945 return entry;
946 }
947
948 static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) {
949 /** String length _including_ the terminating zero byte */
950 uint32_t ver_len = strlen(soft_ver) + 1;
951 /** Partition contains 64 bit header, the version string, and one additional null byte */
952 size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1;
953 struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len);
954
955 uint32_t *len = (uint32_t *)entry.data;
956 len[0] = htonl(ver_len);
957 len[1] = 0;
958 memcpy(&len[2], soft_ver, ver_len);
959
960 entry.data[partition_len - 1] = 0;
961
962 return entry;
963 }
964
965 /** Generates the support-list partition */
966 static struct image_partition_entry make_support_list(const struct device_info *info) {
967 size_t len = strlen(info->support_list);
968 struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
969
970 put32(entry.data, len);
971 memset(entry.data+4, 0, 4);
972 memcpy(entry.data+8, info->support_list, len);
973 entry.data[len+8] = info->support_trail;
974
975 return entry;
976 }
977
978 /** Creates a new image partition with an arbitrary name from a file */
979 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
980 struct stat statbuf;
981
982 if (stat(filename, &statbuf) < 0)
983 error(1, errno, "unable to stat file `%s'", filename);
984
985 size_t len = statbuf.st_size;
986
987 if (add_jffs2_eof)
988 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
989
990 struct image_partition_entry entry = alloc_image_partition(part_name, len);
991
992 FILE *file = fopen(filename, "rb");
993 if (!file)
994 error(1, errno, "unable to open file `%s'", filename);
995
996 if (fread(entry.data, statbuf.st_size, 1, file) != 1)
997 error(1, errno, "unable to read file `%s'", filename);
998
999 if (add_jffs2_eof) {
1000 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
1001
1002 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
1003 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
1004 }
1005
1006 fclose(file);
1007
1008 return entry;
1009 }
1010
1011 /** Creates a new image partition from arbitrary data */
1012 static struct image_partition_entry put_data(const char *part_name, const char *datain, size_t len) {
1013
1014 struct image_partition_entry entry = alloc_image_partition(part_name, len);
1015
1016 memcpy(entry.data, datain, len);
1017
1018 return entry;
1019 }
1020
1021 /**
1022 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
1023
1024 Example image partition table:
1025
1026 fwup-ptn partition-table base 0x00800 size 0x00800
1027 fwup-ptn os-image base 0x01000 size 0x113b45
1028 fwup-ptn file-system base 0x114b45 size 0x1d0004
1029 fwup-ptn support-list base 0x2e4b49 size 0x000d1
1030
1031 Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
1032 the end of the partition table is marked with a zero byte.
1033
1034 The firmware image must contain at least the partition-table and support-list partitions
1035 to be accepted. There aren't any alignment constraints for the image partitions.
1036
1037 The partition-table partition contains the actual flash layout; partitions
1038 from the image partition table are mapped to the corresponding flash partitions during
1039 the firmware upgrade. The support-list partition contains a list of devices supported by
1040 the firmware image.
1041
1042 The base offsets in the firmware partition table are relative to the end
1043 of the vendor information block, so the partition-table partition will
1044 actually start at offset 0x1814 of the image.
1045
1046 I think partition-table must be the first partition in the firmware image.
1047 */
1048 static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) {
1049 size_t i, j;
1050 char *image_pt = (char *)buffer, *end = image_pt + 0x800;
1051
1052 size_t base = 0x800;
1053 for (i = 0; parts[i].name; i++) {
1054 for (j = 0; flash_parts[j].name; j++) {
1055 if (!strcmp(flash_parts[j].name, parts[i].name)) {
1056 if (parts[i].size > flash_parts[j].size)
1057 error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size);
1058 break;
1059 }
1060 }
1061
1062 assert(flash_parts[j].name);
1063
1064 memcpy(buffer + base, parts[i].data, parts[i].size);
1065
1066 size_t len = end-image_pt;
1067 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);
1068
1069 if (w > len-1)
1070 error(1, 0, "image partition table overflow?");
1071
1072 image_pt += w;
1073
1074 base += parts[i].size;
1075 }
1076 }
1077
1078 /** Generates and writes the image MD5 checksum */
1079 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
1080 MD5_CTX ctx;
1081
1082 MD5_Init(&ctx);
1083 MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
1084 MD5_Update(&ctx, buffer, len);
1085 MD5_Final(md5, &ctx);
1086 }
1087
1088
1089 /**
1090 Generates the firmware image in factory format
1091
1092 Image format:
1093
1094 Bytes (hex) Usage
1095 ----------- -----
1096 0000-0003 Image size (4 bytes, big endian)
1097 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
1098 0014-0017 Vendor information length (without padding) (4 bytes, big endian)
1099 0018-1013 Vendor information (4092 bytes, padded with 0xff; there seem to be older
1100 (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
1101 1014-1813 Image partition table (2048 bytes, padded with 0xff)
1102 1814-xxxx Firmware partitions
1103 */
1104 static void * generate_factory_image(const struct device_info *info, const struct image_partition_entry *parts, size_t *len) {
1105 *len = 0x1814;
1106
1107 size_t i;
1108 for (i = 0; parts[i].name; i++)
1109 *len += parts[i].size;
1110
1111 uint8_t *image = malloc(*len);
1112 if (!image)
1113 error(1, errno, "malloc");
1114
1115 memset(image, 0xff, *len);
1116 put32(image, *len);
1117
1118 if (info->vendor) {
1119 size_t vendor_len = strlen(info->vendor);
1120 put32(image+0x14, vendor_len);
1121 memcpy(image+0x18, info->vendor, vendor_len);
1122 }
1123
1124 put_partitions(image + 0x1014, info->partitions, parts);
1125 put_md5(image+0x04, image+0x14, *len-0x14);
1126
1127 return image;
1128 }
1129
1130 /**
1131 Generates the firmware image in sysupgrade format
1132
1133 This makes some assumptions about the provided flash and image partition tables and
1134 should be generalized when TP-LINK starts building its safeloader into hardware with
1135 different flash layouts.
1136 */
1137 static void * generate_sysupgrade_image(const struct device_info *info, const struct image_partition_entry *image_parts, size_t *len) {
1138 size_t i, j;
1139 size_t flash_first_partition_index = 0;
1140 size_t flash_last_partition_index = 0;
1141 const struct flash_partition_entry *flash_first_partition = NULL;
1142 const struct flash_partition_entry *flash_last_partition = NULL;
1143 const struct image_partition_entry *image_last_partition = NULL;
1144
1145 /** Find first and last partitions */
1146 for (i = 0; info->partitions[i].name; i++) {
1147 if (!strcmp(info->partitions[i].name, info->first_sysupgrade_partition)) {
1148 flash_first_partition = &info->partitions[i];
1149 flash_first_partition_index = i;
1150 } else if (!strcmp(info->partitions[i].name, info->last_sysupgrade_partition)) {
1151 flash_last_partition = &info->partitions[i];
1152 flash_last_partition_index = i;
1153 }
1154 }
1155
1156 assert(flash_first_partition && flash_last_partition);
1157 assert(flash_first_partition_index < flash_last_partition_index);
1158
1159 /** Find last partition from image to calculate needed size */
1160 for (i = 0; image_parts[i].name; i++) {
1161 if (!strcmp(image_parts[i].name, info->last_sysupgrade_partition)) {
1162 image_last_partition = &image_parts[i];
1163 break;
1164 }
1165 }
1166
1167 assert(image_last_partition);
1168
1169 *len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size;
1170
1171 uint8_t *image = malloc(*len);
1172 if (!image)
1173 error(1, errno, "malloc");
1174
1175 memset(image, 0xff, *len);
1176
1177 for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) {
1178 for (j = 0; image_parts[j].name; j++) {
1179 if (!strcmp(info->partitions[i].name, image_parts[j].name)) {
1180 if (image_parts[j].size > info->partitions[i].size)
1181 error(1, 0, "%s partition too big (more than %u bytes)", info->partitions[i].name, (unsigned)info->partitions[i].size);
1182 memcpy(image + info->partitions[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size);
1183 break;
1184 }
1185
1186 assert(image_parts[j].name);
1187 }
1188 }
1189
1190 return image;
1191 }
1192
1193 /** Generates an image according to a given layout and writes it to a file */
1194 static void build_image(const char *output,
1195 const char *kernel_image,
1196 const char *rootfs_image,
1197 uint32_t rev,
1198 bool add_jffs2_eof,
1199 bool sysupgrade,
1200 const struct device_info *info) {
1201
1202 struct image_partition_entry parts[7] = {};
1203
1204 parts[0] = make_partition_table(info->partitions);
1205 if (info->soft_ver)
1206 parts[1] = make_soft_version_from_string(info->soft_ver);
1207 else
1208 parts[1] = make_soft_version(rev);
1209
1210 parts[2] = make_support_list(info);
1211 parts[3] = read_file("os-image", kernel_image, false);
1212 parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
1213
1214 if (strcasecmp(info->id, "ARCHER-C25-V1") == 0) {
1215 const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
1216 parts[5] = put_data("extra-para", mdat, 11);
1217 }
1218
1219 size_t len;
1220 void *image;
1221 if (sysupgrade)
1222 image = generate_sysupgrade_image(info, parts, &len);
1223 else
1224 image = generate_factory_image(info, parts, &len);
1225
1226 FILE *file = fopen(output, "wb");
1227 if (!file)
1228 error(1, errno, "unable to open output file");
1229
1230 if (fwrite(image, len, 1, file) != 1)
1231 error(1, 0, "unable to write output file");
1232
1233 fclose(file);
1234
1235 free(image);
1236
1237 size_t i;
1238 for (i = 0; parts[i].name; i++)
1239 free_image_partition(parts[i]);
1240 }
1241
1242 /** Usage output */
1243 static void usage(const char *argv0) {
1244 fprintf(stderr,
1245 "Usage: %s [OPTIONS...]\n"
1246 "\n"
1247 "Options:\n"
1248 " -B <board> create image for the board specified with <board>\n"
1249 " -k <file> read kernel image from the file <file>\n"
1250 " -r <file> read rootfs image from the file <file>\n"
1251 " -o <file> write output to the file <file>\n"
1252 " -V <rev> sets the revision number to <rev>\n"
1253 " -j add jffs2 end-of-filesystem markers\n"
1254 " -S create sysupgrade instead of factory image\n"
1255 " -h show this help\n",
1256 argv0
1257 );
1258 };
1259
1260
1261 static const struct device_info *find_board(const char *id)
1262 {
1263 struct device_info *board = NULL;
1264
1265 for (board = boards; board->id != NULL; board++)
1266 if (strcasecmp(id, board->id) == 0)
1267 return board;
1268
1269 return NULL;
1270 }
1271
1272 int main(int argc, char *argv[]) {
1273 const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
1274 bool add_jffs2_eof = false, sysupgrade = false;
1275 unsigned rev = 0;
1276 const struct device_info *info;
1277 set_source_date_epoch();
1278
1279 while (true) {
1280 int c;
1281
1282 c = getopt(argc, argv, "B:k:r:o:V:jSh");
1283 if (c == -1)
1284 break;
1285
1286 switch (c) {
1287 case 'B':
1288 board = optarg;
1289 break;
1290
1291 case 'k':
1292 kernel_image = optarg;
1293 break;
1294
1295 case 'r':
1296 rootfs_image = optarg;
1297 break;
1298
1299 case 'o':
1300 output = optarg;
1301 break;
1302
1303 case 'V':
1304 sscanf(optarg, "r%u", &rev);
1305 break;
1306
1307 case 'j':
1308 add_jffs2_eof = true;
1309 break;
1310
1311 case 'S':
1312 sysupgrade = true;
1313 break;
1314
1315 case 'h':
1316 usage(argv[0]);
1317 return 0;
1318
1319 default:
1320 usage(argv[0]);
1321 return 1;
1322 }
1323 }
1324
1325 if (!board)
1326 error(1, 0, "no board has been specified");
1327 if (!kernel_image)
1328 error(1, 0, "no kernel image has been specified");
1329 if (!rootfs_image)
1330 error(1, 0, "no rootfs image has been specified");
1331 if (!output)
1332 error(1, 0, "no output filename has been specified");
1333
1334 info = find_board(board);
1335
1336 if (info == NULL)
1337 error(1, 0, "unsupported board %s", board);
1338
1339 build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
1340
1341 return 0;
1342 }