ar71xx: add support for TP-Link TL-WR942N v1
[openwrt/staging/wigyori.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 TL-WR942N V1 */
557 {
558 .id = "TLWR942NV1",
559 .vendor = "",
560 .support_list =
561 "SupportList:\r\n"
562 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:00000000}\r\n"
563 "{product_name:TL-WR942N,product_ver:1.0.0,special_id:52550000}\r\n",
564 .support_trail = '\x00',
565 .soft_ver = NULL,
566
567 .partitions = {
568 {"fs-uboot", 0x00000, 0x20000},
569 {"os-image", 0x20000, 0x150000},
570 {"file-system", 0x170000, 0xcd0000},
571 {"default-mac", 0xe40000, 0x00200},
572 {"pin", 0xe40200, 0x00200},
573 {"product-info", 0xe40400, 0x0fc00},
574 {"partition-table", 0xe50000, 0x10000},
575 {"soft-version", 0xe60000, 0x10000},
576 {"support-list", 0xe70000, 0x10000},
577 {"profile", 0xe80000, 0x10000},
578 {"default-config", 0xe90000, 0x10000},
579 {"user-config", 0xea0000, 0x40000},
580 {"qos-db", 0xee0000, 0x40000},
581 {"certificate", 0xf20000, 0x10000},
582 {"usb-config", 0xfb0000, 0x10000},
583 {"log", 0xfc0000, 0x20000},
584 {"radio-bk", 0xfe0000, 0x10000},
585 {"radio", 0xff0000, 0x10000},
586 {NULL, 0, 0}
587 },
588
589 .first_sysupgrade_partition = "os-image",
590 .last_sysupgrade_partition = "file-system",
591 },
592
593 /** Firmware layout for the RE450 */
594 {
595 .id = "RE450",
596 .vendor = "",
597 .support_list =
598 "SupportList:\r\n"
599 "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n"
600 "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n"
601 "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n"
602 "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n"
603 "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n"
604 "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n"
605 "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n"
606 "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n",
607 .support_trail = '\x00',
608 .soft_ver = NULL,
609
610 /**
611 The flash partition table for RE450;
612 it is almost the same as the one used by the stock images,
613 576KB were moved from file-system to os-image.
614 */
615 .partitions = {
616 {"fs-uboot", 0x00000, 0x20000},
617 {"os-image", 0x20000, 0x150000},
618 {"file-system", 0x170000, 0x4a0000},
619 {"partition-table", 0x600000, 0x02000},
620 {"default-mac", 0x610000, 0x00020},
621 {"pin", 0x610100, 0x00020},
622 {"product-info", 0x611100, 0x01000},
623 {"soft-version", 0x620000, 0x01000},
624 {"support-list", 0x621000, 0x01000},
625 {"profile", 0x622000, 0x08000},
626 {"user-config", 0x630000, 0x10000},
627 {"default-config", 0x640000, 0x10000},
628 {"radio", 0x7f0000, 0x10000},
629 {NULL, 0, 0}
630 },
631
632 .first_sysupgrade_partition = "os-image",
633 .last_sysupgrade_partition = "file-system"
634 },
635
636 {}
637 };
638
639 #define error(_ret, _errno, _str, ...) \
640 do { \
641 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
642 strerror(_errno)); \
643 if (_ret) \
644 exit(_ret); \
645 } while (0)
646
647
648 /** Stores a uint32 as big endian */
649 static inline void put32(uint8_t *buf, uint32_t val) {
650 buf[0] = val >> 24;
651 buf[1] = val >> 16;
652 buf[2] = val >> 8;
653 buf[3] = val;
654 }
655
656 /** Allocates a new image partition */
657 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
658 struct image_partition_entry entry = {name, len, malloc(len)};
659 if (!entry.data)
660 error(1, errno, "malloc");
661
662 return entry;
663 }
664
665 /** Frees an image partition */
666 static void free_image_partition(struct image_partition_entry entry) {
667 free(entry.data);
668 }
669
670 /** Generates the partition-table partition */
671 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
672 struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
673
674 char *s = (char *)entry.data, *end = (char *)(s+entry.size);
675
676 *(s++) = 0x00;
677 *(s++) = 0x04;
678 *(s++) = 0x00;
679 *(s++) = 0x00;
680
681 size_t i;
682 for (i = 0; p[i].name; i++) {
683 size_t len = end-s;
684 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
685
686 if (w > len-1)
687 error(1, 0, "flash partition table overflow?");
688
689 s += w;
690 }
691
692 s++;
693
694 memset(s, 0xff, end-s);
695
696 return entry;
697 }
698
699
700 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
701 static inline uint8_t bcd(uint8_t v) {
702 return 0x10 * (v/10) + v%10;
703 }
704
705
706 /** Generates the soft-version partition */
707 static struct image_partition_entry make_soft_version(uint32_t rev) {
708 struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
709 struct soft_version *s = (struct soft_version *)entry.data;
710
711 time_t t;
712
713 if (time(&t) == (time_t)(-1))
714 error(1, errno, "time");
715
716 struct tm *tm = localtime(&t);
717
718 s->magic = htonl(0x0000000c);
719 s->zero = 0;
720 s->pad1 = 0xff;
721
722 s->version_major = 0;
723 s->version_minor = 0;
724 s->version_patch = 0;
725
726 s->year_hi = bcd((1900+tm->tm_year)/100);
727 s->year_lo = bcd(tm->tm_year%100);
728 s->month = bcd(tm->tm_mon+1);
729 s->day = bcd(tm->tm_mday);
730 s->rev = htonl(rev);
731
732 s->pad2 = 0xff;
733
734 return entry;
735 }
736
737 static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) {
738 /** String length _including_ the terminating zero byte */
739 uint32_t ver_len = strlen(soft_ver) + 1;
740 /** Partition contains 64 bit header, the version string, and one additional null byte */
741 size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1;
742 struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len);
743
744 uint32_t *len = (uint32_t *)entry.data;
745 len[0] = htonl(ver_len);
746 len[1] = 0;
747 memcpy(&len[2], soft_ver, ver_len);
748
749 entry.data[partition_len - 1] = 0;
750
751 return entry;
752 }
753
754 /** Generates the support-list partition */
755 static struct image_partition_entry make_support_list(const struct device_info *info) {
756 size_t len = strlen(info->support_list);
757 struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
758
759 put32(entry.data, len);
760 memset(entry.data+4, 0, 4);
761 memcpy(entry.data+8, info->support_list, len);
762 entry.data[len+8] = info->support_trail;
763
764 return entry;
765 }
766
767 /** Creates a new image partition with an arbitrary name from a file */
768 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
769 struct stat statbuf;
770
771 if (stat(filename, &statbuf) < 0)
772 error(1, errno, "unable to stat file `%s'", filename);
773
774 size_t len = statbuf.st_size;
775
776 if (add_jffs2_eof)
777 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
778
779 struct image_partition_entry entry = alloc_image_partition(part_name, len);
780
781 FILE *file = fopen(filename, "rb");
782 if (!file)
783 error(1, errno, "unable to open file `%s'", filename);
784
785 if (fread(entry.data, statbuf.st_size, 1, file) != 1)
786 error(1, errno, "unable to read file `%s'", filename);
787
788 if (add_jffs2_eof) {
789 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
790
791 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
792 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
793 }
794
795 fclose(file);
796
797 return entry;
798 }
799
800
801 /**
802 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
803
804 Example image partition table:
805
806 fwup-ptn partition-table base 0x00800 size 0x00800
807 fwup-ptn os-image base 0x01000 size 0x113b45
808 fwup-ptn file-system base 0x114b45 size 0x1d0004
809 fwup-ptn support-list base 0x2e4b49 size 0x000d1
810
811 Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
812 the end of the partition table is marked with a zero byte.
813
814 The firmware image must contain at least the partition-table and support-list partitions
815 to be accepted. There aren't any alignment constraints for the image partitions.
816
817 The partition-table partition contains the actual flash layout; partitions
818 from the image partition table are mapped to the corresponding flash partitions during
819 the firmware upgrade. The support-list partition contains a list of devices supported by
820 the firmware image.
821
822 The base offsets in the firmware partition table are relative to the end
823 of the vendor information block, so the partition-table partition will
824 actually start at offset 0x1814 of the image.
825
826 I think partition-table must be the first partition in the firmware image.
827 */
828 static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) {
829 size_t i, j;
830 char *image_pt = (char *)buffer, *end = image_pt + 0x800;
831
832 size_t base = 0x800;
833 for (i = 0; parts[i].name; i++) {
834 for (j = 0; flash_parts[j].name; j++) {
835 if (!strcmp(flash_parts[j].name, parts[i].name)) {
836 if (parts[i].size > flash_parts[j].size)
837 error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size);
838 break;
839 }
840 }
841
842 assert(flash_parts[j].name);
843
844 memcpy(buffer + base, parts[i].data, parts[i].size);
845
846 size_t len = end-image_pt;
847 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);
848
849 if (w > len-1)
850 error(1, 0, "image partition table overflow?");
851
852 image_pt += w;
853
854 base += parts[i].size;
855 }
856 }
857
858 /** Generates and writes the image MD5 checksum */
859 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
860 MD5_CTX ctx;
861
862 MD5_Init(&ctx);
863 MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
864 MD5_Update(&ctx, buffer, len);
865 MD5_Final(md5, &ctx);
866 }
867
868
869 /**
870 Generates the firmware image in factory format
871
872 Image format:
873
874 Bytes (hex) Usage
875 ----------- -----
876 0000-0003 Image size (4 bytes, big endian)
877 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
878 0014-0017 Vendor information length (without padding) (4 bytes, big endian)
879 0018-1013 Vendor information (4092 bytes, padded with 0xff; there seem to be older
880 (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
881 1014-1813 Image partition table (2048 bytes, padded with 0xff)
882 1814-xxxx Firmware partitions
883 */
884 static void * generate_factory_image(const struct device_info *info, const struct image_partition_entry *parts, size_t *len) {
885 *len = 0x1814;
886
887 size_t i;
888 for (i = 0; parts[i].name; i++)
889 *len += parts[i].size;
890
891 uint8_t *image = malloc(*len);
892 if (!image)
893 error(1, errno, "malloc");
894
895 memset(image, 0xff, *len);
896 put32(image, *len);
897
898 if (info->vendor) {
899 size_t vendor_len = strlen(info->vendor);
900 put32(image+0x14, vendor_len);
901 memcpy(image+0x18, info->vendor, vendor_len);
902 }
903
904 put_partitions(image + 0x1014, info->partitions, parts);
905 put_md5(image+0x04, image+0x14, *len-0x14);
906
907 return image;
908 }
909
910 /**
911 Generates the firmware image in sysupgrade format
912
913 This makes some assumptions about the provided flash and image partition tables and
914 should be generalized when TP-LINK starts building its safeloader into hardware with
915 different flash layouts.
916 */
917 static void * generate_sysupgrade_image(const struct device_info *info, const struct image_partition_entry *image_parts, size_t *len) {
918 size_t i, j;
919 size_t flash_first_partition_index = 0;
920 size_t flash_last_partition_index = 0;
921 const struct flash_partition_entry *flash_first_partition = NULL;
922 const struct flash_partition_entry *flash_last_partition = NULL;
923 const struct image_partition_entry *image_last_partition = NULL;
924
925 /** Find first and last partitions */
926 for (i = 0; info->partitions[i].name; i++) {
927 if (!strcmp(info->partitions[i].name, info->first_sysupgrade_partition)) {
928 flash_first_partition = &info->partitions[i];
929 flash_first_partition_index = i;
930 } else if (!strcmp(info->partitions[i].name, info->last_sysupgrade_partition)) {
931 flash_last_partition = &info->partitions[i];
932 flash_last_partition_index = i;
933 }
934 }
935
936 assert(flash_first_partition && flash_last_partition);
937 assert(flash_first_partition_index < flash_last_partition_index);
938
939 /** Find last partition from image to calculate needed size */
940 for (i = 0; image_parts[i].name; i++) {
941 if (!strcmp(image_parts[i].name, info->last_sysupgrade_partition)) {
942 image_last_partition = &image_parts[i];
943 break;
944 }
945 }
946
947 assert(image_last_partition);
948
949 *len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size;
950
951 uint8_t *image = malloc(*len);
952 if (!image)
953 error(1, errno, "malloc");
954
955 memset(image, 0xff, *len);
956
957 for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) {
958 for (j = 0; image_parts[j].name; j++) {
959 if (!strcmp(info->partitions[i].name, image_parts[j].name)) {
960 if (image_parts[j].size > info->partitions[i].size)
961 error(1, 0, "%s partition too big (more than %u bytes)", info->partitions[i].name, (unsigned)info->partitions[i].size);
962 memcpy(image + info->partitions[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size);
963 break;
964 }
965
966 assert(image_parts[j].name);
967 }
968 }
969
970 return image;
971 }
972
973 /** Generates an image according to a given layout and writes it to a file */
974 static void build_image(const char *output,
975 const char *kernel_image,
976 const char *rootfs_image,
977 uint32_t rev,
978 bool add_jffs2_eof,
979 bool sysupgrade,
980 const struct device_info *info) {
981 struct image_partition_entry parts[6] = {};
982
983 parts[0] = make_partition_table(info->partitions);
984 if (info->soft_ver)
985 parts[1] = make_soft_version_from_string(info->soft_ver);
986 else
987 parts[1] = make_soft_version(rev);
988
989 parts[2] = make_support_list(info);
990 parts[3] = read_file("os-image", kernel_image, false);
991 parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
992
993 size_t len;
994 void *image;
995 if (sysupgrade)
996 image = generate_sysupgrade_image(info, parts, &len);
997 else
998 image = generate_factory_image(info, parts, &len);
999
1000 FILE *file = fopen(output, "wb");
1001 if (!file)
1002 error(1, errno, "unable to open output file");
1003
1004 if (fwrite(image, len, 1, file) != 1)
1005 error(1, 0, "unable to write output file");
1006
1007 fclose(file);
1008
1009 free(image);
1010
1011 size_t i;
1012 for (i = 0; parts[i].name; i++)
1013 free_image_partition(parts[i]);
1014 }
1015
1016 /** Usage output */
1017 static void usage(const char *argv0) {
1018 fprintf(stderr,
1019 "Usage: %s [OPTIONS...]\n"
1020 "\n"
1021 "Options:\n"
1022 " -B <board> create image for the board specified with <board>\n"
1023 " -k <file> read kernel image from the file <file>\n"
1024 " -r <file> read rootfs image from the file <file>\n"
1025 " -o <file> write output to the file <file>\n"
1026 " -V <rev> sets the revision number to <rev>\n"
1027 " -j add jffs2 end-of-filesystem markers\n"
1028 " -S create sysupgrade instead of factory image\n"
1029 " -h show this help\n",
1030 argv0
1031 );
1032 };
1033
1034
1035 static const struct device_info *find_board(const char *id)
1036 {
1037 struct device_info *board = NULL;
1038
1039 for (board = boards; board->id != NULL; board++)
1040 if (strcasecmp(id, board->id) == 0)
1041 return board;
1042
1043 return NULL;
1044 }
1045
1046 int main(int argc, char *argv[]) {
1047 const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
1048 bool add_jffs2_eof = false, sysupgrade = false;
1049 unsigned rev = 0;
1050 const struct device_info *info;
1051
1052 while (true) {
1053 int c;
1054
1055 c = getopt(argc, argv, "B:k:r:o:V:jSh");
1056 if (c == -1)
1057 break;
1058
1059 switch (c) {
1060 case 'B':
1061 board = optarg;
1062 break;
1063
1064 case 'k':
1065 kernel_image = optarg;
1066 break;
1067
1068 case 'r':
1069 rootfs_image = optarg;
1070 break;
1071
1072 case 'o':
1073 output = optarg;
1074 break;
1075
1076 case 'V':
1077 sscanf(optarg, "r%u", &rev);
1078 break;
1079
1080 case 'j':
1081 add_jffs2_eof = true;
1082 break;
1083
1084 case 'S':
1085 sysupgrade = true;
1086 break;
1087
1088 case 'h':
1089 usage(argv[0]);
1090 return 0;
1091
1092 default:
1093 usage(argv[0]);
1094 return 1;
1095 }
1096 }
1097
1098 if (!board)
1099 error(1, 0, "no board has been specified");
1100 if (!kernel_image)
1101 error(1, 0, "no kernel image has been specified");
1102 if (!rootfs_image)
1103 error(1, 0, "no rootfs image has been specified");
1104 if (!output)
1105 error(1, 0, "no output filename has been specified");
1106
1107 info = find_board(board);
1108
1109 if (info == NULL)
1110 error(1, 0, "unsupported board %s", board);
1111
1112 build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
1113
1114 return 0;
1115 }