9c9efc369c23e068687d37122aa8b4879a49d096
[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 C59v1 */
303 {
304 .id = "ARCHER-C59-V1",
305 .vendor = "",
306 .support_list =
307 "SupportList:\r\n"
308 "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n"
309 "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n"
310 "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n",
311 .support_trail = '\x00',
312 .soft_ver = "soft_ver:1.0.0\n",
313
314 .partitions = {
315 {"fs-uboot", 0x00000, 0x10000},
316 {"default-mac", 0x10000, 0x00200},
317 {"pin", 0x10200, 0x00200},
318 {"device-id", 0x10400, 0x00100},
319 {"product-info", 0x10500, 0x0fb00},
320 {"os-image", 0x20000, 0x180000},
321 {"file-system", 0x1a0000, 0xcb0000},
322 {"partition-table", 0xe50000, 0x10000},
323 {"soft-version", 0xe60000, 0x10000},
324 {"support-list", 0xe70000, 0x10000},
325 {"profile", 0xe80000, 0x10000},
326 {"default-config", 0xe90000, 0x10000},
327 {"user-config", 0xea0000, 0x40000},
328 {"usb-config", 0xee0000, 0x10000},
329 {"certificate", 0xef0000, 0x10000},
330 {"qos-db", 0xf00000, 0x40000},
331 {"log", 0xfe0000, 0x10000},
332 {"radio", 0xff0000, 0x10000},
333 {NULL, 0, 0}
334 },
335
336 .first_sysupgrade_partition = "os-image",
337 .last_sysupgrade_partition = "file-system",
338 },
339
340 /** Firmware layout for the C60v1 */
341 {
342 .id = "ARCHER-C60-V1",
343 .vendor = "",
344 .support_list =
345 "SupportList:\r\n"
346 "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n"
347 "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n"
348 "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n",
349 .support_trail = '\x00',
350 .soft_ver = "soft_ver:1.0.0\n",
351
352 .partitions = {
353 {"fs-uboot", 0x00000, 0x10000},
354 {"default-mac", 0x10000, 0x00200},
355 {"pin", 0x10200, 0x00200},
356 {"product-info", 0x10400, 0x00100},
357 {"partition-table", 0x10500, 0x00800},
358 {"soft-version", 0x11300, 0x00200},
359 {"support-list", 0x11500, 0x00100},
360 {"device-id", 0x11600, 0x00100},
361 {"profile", 0x11700, 0x03900},
362 {"default-config", 0x15000, 0x04000},
363 {"user-config", 0x19000, 0x04000},
364 {"os-image", 0x20000, 0x150000},
365 {"file-system", 0x170000, 0x678000},
366 {"certyficate", 0x7e8000, 0x08000},
367 {"radio", 0x7f0000, 0x10000},
368 {NULL, 0, 0}
369 },
370
371 .first_sysupgrade_partition = "os-image",
372 .last_sysupgrade_partition = "file-system",
373 },
374
375 /** Firmware layout for the C5 */
376 {
377 .id = "ARCHER-C5-V2",
378 .vendor = "",
379 .support_list =
380 "SupportList:\r\n"
381 "{product_name:ArcherC5,"
382 "product_ver:2.0.0,"
383 "special_id:00000000}\r\n",
384 .support_trail = '\x00',
385 .soft_ver = NULL,
386
387 .partitions = {
388 {"fs-uboot", 0x00000, 0x40000},
389 {"os-image", 0x40000, 0x200000},
390 {"file-system", 0x240000, 0xc00000},
391 {"default-mac", 0xe40000, 0x00200},
392 {"pin", 0xe40200, 0x00200},
393 {"product-info", 0xe40400, 0x00200},
394 {"partition-table", 0xe50000, 0x10000},
395 {"soft-version", 0xe60000, 0x00200},
396 {"support-list", 0xe61000, 0x0f000},
397 {"profile", 0xe70000, 0x10000},
398 {"default-config", 0xe80000, 0x10000},
399 {"user-config", 0xe90000, 0x50000},
400 {"log", 0xee0000, 0x100000},
401 {"radio_bk", 0xfe0000, 0x10000},
402 {"radio", 0xff0000, 0x10000},
403 {NULL, 0, 0}
404 },
405
406 .first_sysupgrade_partition = "os-image",
407 .last_sysupgrade_partition = "file-system"
408 },
409
410 /** Firmware layout for the C9 */
411 {
412 .id = "ARCHERC9",
413 .vendor = "",
414 .support_list =
415 "SupportList:\n"
416 "{product_name:ArcherC9,"
417 "product_ver:1.0.0,"
418 "special_id:00000000}\n",
419 .support_trail = '\x00',
420 .soft_ver = NULL,
421
422 .partitions = {
423 {"fs-uboot", 0x00000, 0x40000},
424 {"os-image", 0x40000, 0x200000},
425 {"file-system", 0x240000, 0xc00000},
426 {"default-mac", 0xe40000, 0x00200},
427 {"pin", 0xe40200, 0x00200},
428 {"product-info", 0xe40400, 0x00200},
429 {"partition-table", 0xe50000, 0x10000},
430 {"soft-version", 0xe60000, 0x00200},
431 {"support-list", 0xe61000, 0x0f000},
432 {"profile", 0xe70000, 0x10000},
433 {"default-config", 0xe80000, 0x10000},
434 {"user-config", 0xe90000, 0x50000},
435 {"log", 0xee0000, 0x100000},
436 {"radio_bk", 0xfe0000, 0x10000},
437 {"radio", 0xff0000, 0x10000},
438 {NULL, 0, 0}
439 },
440
441 .first_sysupgrade_partition = "os-image",
442 .last_sysupgrade_partition = "file-system"
443 },
444
445 /** Firmware layout for the EAP120 */
446 {
447 .id = "EAP120",
448 .vendor = "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
449 .support_list =
450 "SupportList:\r\n"
451 "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
452 .support_trail = '\xff',
453 .soft_ver = NULL,
454
455 .partitions = {
456 {"fs-uboot", 0x00000, 0x20000},
457 {"partition-table", 0x20000, 0x02000},
458 {"default-mac", 0x30000, 0x00020},
459 {"support-list", 0x31000, 0x00100},
460 {"product-info", 0x31100, 0x00100},
461 {"soft-version", 0x32000, 0x00100},
462 {"os-image", 0x40000, 0x180000},
463 {"file-system", 0x1c0000, 0x600000},
464 {"user-config", 0x7c0000, 0x10000},
465 {"backup-config", 0x7d0000, 0x10000},
466 {"log", 0x7e0000, 0x10000},
467 {"radio", 0x7f0000, 0x10000},
468 {NULL, 0, 0}
469 },
470
471 .first_sysupgrade_partition = "os-image",
472 .last_sysupgrade_partition = "file-system"
473 },
474
475 /** Firmware layout for the TL-WA850RE v2 */
476 {
477 .id = "TLWA850REV2",
478 .vendor = "",
479 .support_list =
480 "SupportList:\n"
481 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55530000}\n"
482 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:00000000}\n"
483 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55534100}\n"
484 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:45550000}\n"
485 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4B520000}\n"
486 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:42520000}\n"
487 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4A500000}\n"
488 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:43410000}\n"
489 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:41550000}\n"
490 "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:52550000}\n",
491 .support_trail = '\x00',
492 .soft_ver = NULL,
493
494 /**
495 576KB were moved from file-system to os-image
496 in comparison to the stock image
497 */
498 .partitions = {
499 {"fs-uboot", 0x00000, 0x20000},
500 {"os-image", 0x20000, 0x150000},
501 {"file-system", 0x170000, 0x240000},
502 {"partition-table", 0x3b0000, 0x02000},
503 {"default-mac", 0x3c0000, 0x00020},
504 {"pin", 0x3c0100, 0x00020},
505 {"product-info", 0x3c1000, 0x01000},
506 {"soft-version", 0x3c2000, 0x00100},
507 {"support-list", 0x3c3000, 0x01000},
508 {"profile", 0x3c4000, 0x08000},
509 {"user-config", 0x3d0000, 0x10000},
510 {"default-config", 0x3e0000, 0x10000},
511 {"radio", 0x3f0000, 0x10000},
512 {NULL, 0, 0}
513 },
514
515 .first_sysupgrade_partition = "os-image",
516 .last_sysupgrade_partition = "file-system"
517 },
518
519 /** Firmware layout for the TL-WR1043 v4 */
520 {
521 .id = "TLWR1043NDV4",
522 .vendor = "",
523 .support_list =
524 "SupportList:\n"
525 "{product_name:TL-WR1043ND,product_ver:4.0.0,special_id:45550000}\n",
526 .support_trail = '\x00',
527 .soft_ver = NULL,
528
529 /**
530 We use a bigger os-image partition than the stock images (and thus
531 smaller file-system), as our kernel doesn't fit in the stock firmware's
532 1MB os-image.
533 */
534 .partitions = {
535 {"fs-uboot", 0x00000, 0x20000},
536 {"os-image", 0x20000, 0x180000},
537 {"file-system", 0x1a0000, 0xdb0000},
538 {"default-mac", 0xf50000, 0x00200},
539 {"pin", 0xf50200, 0x00200},
540 {"product-info", 0xf50400, 0x0fc00},
541 {"soft-version", 0xf60000, 0x0b000},
542 {"support-list", 0xf6b000, 0x04000},
543 {"profile", 0xf70000, 0x04000},
544 {"default-config", 0xf74000, 0x0b000},
545 {"user-config", 0xf80000, 0x40000},
546 {"partition-table", 0xfc0000, 0x10000},
547 {"log", 0xfd0000, 0x20000},
548 {"radio", 0xff0000, 0x10000},
549 {NULL, 0, 0}
550 },
551
552 .first_sysupgrade_partition = "os-image",
553 .last_sysupgrade_partition = "file-system"
554 },
555
556 /** Firmware layout for the RE450 */
557 {
558 .id = "RE450",
559 .vendor = "",
560 .support_list =
561 "SupportList:\r\n"
562 "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n"
563 "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n"
564 "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n"
565 "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n"
566 "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n"
567 "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n"
568 "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n"
569 "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n",
570 .support_trail = '\x00',
571 .soft_ver = NULL,
572
573 /**
574 The flash partition table for RE450;
575 it is almost the same as the one used by the stock images,
576 576KB were moved from file-system to os-image.
577 */
578 .partitions = {
579 {"fs-uboot", 0x00000, 0x20000},
580 {"os-image", 0x20000, 0x150000},
581 {"file-system", 0x170000, 0x4a0000},
582 {"partition-table", 0x600000, 0x02000},
583 {"default-mac", 0x610000, 0x00020},
584 {"pin", 0x610100, 0x00020},
585 {"product-info", 0x611100, 0x01000},
586 {"soft-version", 0x620000, 0x01000},
587 {"support-list", 0x621000, 0x01000},
588 {"profile", 0x622000, 0x08000},
589 {"user-config", 0x630000, 0x10000},
590 {"default-config", 0x640000, 0x10000},
591 {"radio", 0x7f0000, 0x10000},
592 {NULL, 0, 0}
593 },
594
595 .first_sysupgrade_partition = "os-image",
596 .last_sysupgrade_partition = "file-system"
597 },
598
599 {}
600 };
601
602 #define error(_ret, _errno, _str, ...) \
603 do { \
604 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
605 strerror(_errno)); \
606 if (_ret) \
607 exit(_ret); \
608 } while (0)
609
610
611 /** Stores a uint32 as big endian */
612 static inline void put32(uint8_t *buf, uint32_t val) {
613 buf[0] = val >> 24;
614 buf[1] = val >> 16;
615 buf[2] = val >> 8;
616 buf[3] = val;
617 }
618
619 /** Allocates a new image partition */
620 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
621 struct image_partition_entry entry = {name, len, malloc(len)};
622 if (!entry.data)
623 error(1, errno, "malloc");
624
625 return entry;
626 }
627
628 /** Frees an image partition */
629 static void free_image_partition(struct image_partition_entry entry) {
630 free(entry.data);
631 }
632
633 /** Generates the partition-table partition */
634 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
635 struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
636
637 char *s = (char *)entry.data, *end = (char *)(s+entry.size);
638
639 *(s++) = 0x00;
640 *(s++) = 0x04;
641 *(s++) = 0x00;
642 *(s++) = 0x00;
643
644 size_t i;
645 for (i = 0; p[i].name; i++) {
646 size_t len = end-s;
647 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
648
649 if (w > len-1)
650 error(1, 0, "flash partition table overflow?");
651
652 s += w;
653 }
654
655 s++;
656
657 memset(s, 0xff, end-s);
658
659 return entry;
660 }
661
662
663 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
664 static inline uint8_t bcd(uint8_t v) {
665 return 0x10 * (v/10) + v%10;
666 }
667
668
669 /** Generates the soft-version partition */
670 static struct image_partition_entry make_soft_version(uint32_t rev) {
671 struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
672 struct soft_version *s = (struct soft_version *)entry.data;
673
674 time_t t;
675
676 if (time(&t) == (time_t)(-1))
677 error(1, errno, "time");
678
679 struct tm *tm = localtime(&t);
680
681 s->magic = htonl(0x0000000c);
682 s->zero = 0;
683 s->pad1 = 0xff;
684
685 s->version_major = 0;
686 s->version_minor = 0;
687 s->version_patch = 0;
688
689 s->year_hi = bcd((1900+tm->tm_year)/100);
690 s->year_lo = bcd(tm->tm_year%100);
691 s->month = bcd(tm->tm_mon+1);
692 s->day = bcd(tm->tm_mday);
693 s->rev = htonl(rev);
694
695 s->pad2 = 0xff;
696
697 return entry;
698 }
699
700 static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) {
701 /** String length _including_ the terminating zero byte */
702 uint32_t ver_len = strlen(soft_ver) + 1;
703 /** Partition contains 64 bit header, the version string, and one additional null byte */
704 size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1;
705 struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len);
706
707 uint32_t *len = (uint32_t *)entry.data;
708 len[0] = htonl(ver_len);
709 len[1] = 0;
710 memcpy(&len[2], soft_ver, ver_len);
711
712 entry.data[partition_len - 1] = 0;
713
714 return entry;
715 }
716
717 /** Generates the support-list partition */
718 static struct image_partition_entry make_support_list(const struct device_info *info) {
719 size_t len = strlen(info->support_list);
720 struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
721
722 put32(entry.data, len);
723 memset(entry.data+4, 0, 4);
724 memcpy(entry.data+8, info->support_list, len);
725 entry.data[len+8] = info->support_trail;
726
727 return entry;
728 }
729
730 /** Creates a new image partition with an arbitrary name from a file */
731 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
732 struct stat statbuf;
733
734 if (stat(filename, &statbuf) < 0)
735 error(1, errno, "unable to stat file `%s'", filename);
736
737 size_t len = statbuf.st_size;
738
739 if (add_jffs2_eof)
740 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
741
742 struct image_partition_entry entry = alloc_image_partition(part_name, len);
743
744 FILE *file = fopen(filename, "rb");
745 if (!file)
746 error(1, errno, "unable to open file `%s'", filename);
747
748 if (fread(entry.data, statbuf.st_size, 1, file) != 1)
749 error(1, errno, "unable to read file `%s'", filename);
750
751 if (add_jffs2_eof) {
752 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
753
754 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
755 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
756 }
757
758 fclose(file);
759
760 return entry;
761 }
762
763
764 /**
765 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
766
767 Example image partition table:
768
769 fwup-ptn partition-table base 0x00800 size 0x00800
770 fwup-ptn os-image base 0x01000 size 0x113b45
771 fwup-ptn file-system base 0x114b45 size 0x1d0004
772 fwup-ptn support-list base 0x2e4b49 size 0x000d1
773
774 Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
775 the end of the partition table is marked with a zero byte.
776
777 The firmware image must contain at least the partition-table and support-list partitions
778 to be accepted. There aren't any alignment constraints for the image partitions.
779
780 The partition-table partition contains the actual flash layout; partitions
781 from the image partition table are mapped to the corresponding flash partitions during
782 the firmware upgrade. The support-list partition contains a list of devices supported by
783 the firmware image.
784
785 The base offsets in the firmware partition table are relative to the end
786 of the vendor information block, so the partition-table partition will
787 actually start at offset 0x1814 of the image.
788
789 I think partition-table must be the first partition in the firmware image.
790 */
791 static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) {
792 size_t i, j;
793 char *image_pt = (char *)buffer, *end = image_pt + 0x800;
794
795 size_t base = 0x800;
796 for (i = 0; parts[i].name; i++) {
797 for (j = 0; flash_parts[j].name; j++) {
798 if (!strcmp(flash_parts[j].name, parts[i].name)) {
799 if (parts[i].size > flash_parts[j].size)
800 error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size);
801 break;
802 }
803 }
804
805 assert(flash_parts[j].name);
806
807 memcpy(buffer + base, parts[i].data, parts[i].size);
808
809 size_t len = end-image_pt;
810 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);
811
812 if (w > len-1)
813 error(1, 0, "image partition table overflow?");
814
815 image_pt += w;
816
817 base += parts[i].size;
818 }
819 }
820
821 /** Generates and writes the image MD5 checksum */
822 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
823 MD5_CTX ctx;
824
825 MD5_Init(&ctx);
826 MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
827 MD5_Update(&ctx, buffer, len);
828 MD5_Final(md5, &ctx);
829 }
830
831
832 /**
833 Generates the firmware image in factory format
834
835 Image format:
836
837 Bytes (hex) Usage
838 ----------- -----
839 0000-0003 Image size (4 bytes, big endian)
840 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
841 0014-0017 Vendor information length (without padding) (4 bytes, big endian)
842 0018-1013 Vendor information (4092 bytes, padded with 0xff; there seem to be older
843 (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
844 1014-1813 Image partition table (2048 bytes, padded with 0xff)
845 1814-xxxx Firmware partitions
846 */
847 static void * generate_factory_image(const struct device_info *info, const struct image_partition_entry *parts, size_t *len) {
848 *len = 0x1814;
849
850 size_t i;
851 for (i = 0; parts[i].name; i++)
852 *len += parts[i].size;
853
854 uint8_t *image = malloc(*len);
855 if (!image)
856 error(1, errno, "malloc");
857
858 memset(image, 0xff, *len);
859 put32(image, *len);
860
861 if (info->vendor) {
862 size_t vendor_len = strlen(info->vendor);
863 put32(image+0x14, vendor_len);
864 memcpy(image+0x18, info->vendor, vendor_len);
865 }
866
867 put_partitions(image + 0x1014, info->partitions, parts);
868 put_md5(image+0x04, image+0x14, *len-0x14);
869
870 return image;
871 }
872
873 /**
874 Generates the firmware image in sysupgrade format
875
876 This makes some assumptions about the provided flash and image partition tables and
877 should be generalized when TP-LINK starts building its safeloader into hardware with
878 different flash layouts.
879 */
880 static void * generate_sysupgrade_image(const struct device_info *info, const struct image_partition_entry *image_parts, size_t *len) {
881 size_t i, j;
882 size_t flash_first_partition_index = 0;
883 size_t flash_last_partition_index = 0;
884 const struct flash_partition_entry *flash_first_partition = NULL;
885 const struct flash_partition_entry *flash_last_partition = NULL;
886 const struct image_partition_entry *image_last_partition = NULL;
887
888 /** Find first and last partitions */
889 for (i = 0; info->partitions[i].name; i++) {
890 if (!strcmp(info->partitions[i].name, info->first_sysupgrade_partition)) {
891 flash_first_partition = &info->partitions[i];
892 flash_first_partition_index = i;
893 } else if (!strcmp(info->partitions[i].name, info->last_sysupgrade_partition)) {
894 flash_last_partition = &info->partitions[i];
895 flash_last_partition_index = i;
896 }
897 }
898
899 assert(flash_first_partition && flash_last_partition);
900 assert(flash_first_partition_index < flash_last_partition_index);
901
902 /** Find last partition from image to calculate needed size */
903 for (i = 0; image_parts[i].name; i++) {
904 if (!strcmp(image_parts[i].name, info->last_sysupgrade_partition)) {
905 image_last_partition = &image_parts[i];
906 break;
907 }
908 }
909
910 assert(image_last_partition);
911
912 *len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size;
913
914 uint8_t *image = malloc(*len);
915 if (!image)
916 error(1, errno, "malloc");
917
918 memset(image, 0xff, *len);
919
920 for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) {
921 for (j = 0; image_parts[j].name; j++) {
922 if (!strcmp(info->partitions[i].name, image_parts[j].name)) {
923 if (image_parts[j].size > info->partitions[i].size)
924 error(1, 0, "%s partition too big (more than %u bytes)", info->partitions[i].name, (unsigned)info->partitions[i].size);
925 memcpy(image + info->partitions[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size);
926 break;
927 }
928
929 assert(image_parts[j].name);
930 }
931 }
932
933 return image;
934 }
935
936 /** Generates an image according to a given layout and writes it to a file */
937 static void build_image(const char *output,
938 const char *kernel_image,
939 const char *rootfs_image,
940 uint32_t rev,
941 bool add_jffs2_eof,
942 bool sysupgrade,
943 const struct device_info *info) {
944 struct image_partition_entry parts[6] = {};
945
946 parts[0] = make_partition_table(info->partitions);
947 if (info->soft_ver)
948 parts[1] = make_soft_version_from_string(info->soft_ver);
949 else
950 parts[1] = make_soft_version(rev);
951
952 parts[2] = make_support_list(info);
953 parts[3] = read_file("os-image", kernel_image, false);
954 parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
955
956 size_t len;
957 void *image;
958 if (sysupgrade)
959 image = generate_sysupgrade_image(info, parts, &len);
960 else
961 image = generate_factory_image(info, parts, &len);
962
963 FILE *file = fopen(output, "wb");
964 if (!file)
965 error(1, errno, "unable to open output file");
966
967 if (fwrite(image, len, 1, file) != 1)
968 error(1, 0, "unable to write output file");
969
970 fclose(file);
971
972 free(image);
973
974 size_t i;
975 for (i = 0; parts[i].name; i++)
976 free_image_partition(parts[i]);
977 }
978
979 /** Usage output */
980 static void usage(const char *argv0) {
981 fprintf(stderr,
982 "Usage: %s [OPTIONS...]\n"
983 "\n"
984 "Options:\n"
985 " -B <board> create image for the board specified with <board>\n"
986 " -k <file> read kernel image from the file <file>\n"
987 " -r <file> read rootfs image from the file <file>\n"
988 " -o <file> write output to the file <file>\n"
989 " -V <rev> sets the revision number to <rev>\n"
990 " -j add jffs2 end-of-filesystem markers\n"
991 " -S create sysupgrade instead of factory image\n"
992 " -h show this help\n",
993 argv0
994 );
995 };
996
997
998 static const struct device_info *find_board(const char *id)
999 {
1000 struct device_info *board = NULL;
1001
1002 for (board = boards; board->id != NULL; board++)
1003 if (strcasecmp(id, board->id) == 0)
1004 return board;
1005
1006 return NULL;
1007 }
1008
1009 int main(int argc, char *argv[]) {
1010 const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
1011 bool add_jffs2_eof = false, sysupgrade = false;
1012 unsigned rev = 0;
1013 const struct device_info *info;
1014
1015 while (true) {
1016 int c;
1017
1018 c = getopt(argc, argv, "B:k:r:o:V:jSh");
1019 if (c == -1)
1020 break;
1021
1022 switch (c) {
1023 case 'B':
1024 board = optarg;
1025 break;
1026
1027 case 'k':
1028 kernel_image = optarg;
1029 break;
1030
1031 case 'r':
1032 rootfs_image = optarg;
1033 break;
1034
1035 case 'o':
1036 output = optarg;
1037 break;
1038
1039 case 'V':
1040 sscanf(optarg, "r%u", &rev);
1041 break;
1042
1043 case 'j':
1044 add_jffs2_eof = true;
1045 break;
1046
1047 case 'S':
1048 sysupgrade = true;
1049 break;
1050
1051 case 'h':
1052 usage(argv[0]);
1053 return 0;
1054
1055 default:
1056 usage(argv[0]);
1057 return 1;
1058 }
1059 }
1060
1061 if (!board)
1062 error(1, 0, "no board has been specified");
1063 if (!kernel_image)
1064 error(1, 0, "no kernel image has been specified");
1065 if (!rootfs_image)
1066 error(1, 0, "no rootfs image has been specified");
1067 if (!output)
1068 error(1, 0, "no output filename has been specified");
1069
1070 info = find_board(board);
1071
1072 if (info == NULL)
1073 error(1, 0, "unsupported board %s", board);
1074
1075 build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
1076
1077 return 0;
1078 }