include/package.mk: Add support for src-checkout/ folder
[openwrt/staging/blogic.git] / tools / firmware-utils / src / mktplinkfw2.c
1 /*
2 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
3 *
4 * This tool was based on:
5 * TP-Link WR941 V2 firmware checksum fixing tool.
6 * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 *
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <string.h>
18 #include <unistd.h> /* for unlink() */
19 #include <libgen.h>
20 #include <getopt.h> /* for getopt() */
21 #include <stdarg.h>
22 #include <errno.h>
23 #include <stdbool.h>
24 #include <endian.h>
25 #include <sys/stat.h>
26
27 #include <arpa/inet.h>
28 #include <netinet/in.h>
29
30 #include "md5.h"
31 #include "mktplinkfw-lib.h"
32
33 struct fw_header {
34 uint32_t version; /* 0x00: header version */
35 char fw_version[48]; /* 0x04: fw version string */
36 uint32_t hw_id; /* 0x34: hardware id */
37 uint32_t hw_rev; /* 0x38: FIXME: hardware revision? */
38 uint32_t hw_ver_add; /* 0x3c: additional hardware version */
39 uint8_t md5sum1[MD5SUM_LEN]; /* 0x40 */
40 uint32_t unk2; /* 0x50: 0x00000000 */
41 uint8_t md5sum2[MD5SUM_LEN]; /* 0x54 */
42 uint32_t unk3; /* 0x64: 0xffffffff */
43
44 uint32_t kernel_la; /* 0x68: kernel load address */
45 uint32_t kernel_ep; /* 0x6c: kernel entry point */
46 uint32_t fw_length; /* 0x70: total length of the image */
47 uint32_t kernel_ofs; /* 0x74: kernel data offset */
48 uint32_t kernel_len; /* 0x78: kernel data length */
49 uint32_t rootfs_ofs; /* 0x7c: rootfs data offset */
50 uint32_t rootfs_len; /* 0x80: rootfs data length */
51 uint32_t boot_ofs; /* 0x84: bootloader offset */
52 uint32_t boot_len; /* 0x88: bootloader length */
53 uint16_t unk4; /* 0x8c: 0x55aa */
54 uint8_t sver_hi; /* 0x8e */
55 uint8_t sver_lo; /* 0x8f */
56 uint8_t unk5; /* 0x90: magic: 0xa5 */
57 uint8_t ver_hi; /* 0x91 */
58 uint8_t ver_mid; /* 0x92 */
59 uint8_t ver_lo; /* 0x93 */
60 uint8_t pad[364];
61 } __attribute__ ((packed));
62
63 #define FLAG_LE_KERNEL_LA_EP 0x00000001 /* Little-endian used for kernel load address & entry point */
64
65 struct board_info {
66 char *id;
67 uint32_t hw_id;
68 uint32_t hw_rev;
69 uint32_t hw_ver_add;
70 char *layout_id;
71 uint32_t hdr_ver;
72 uint32_t flags;
73 };
74
75 /*
76 * Globals
77 */
78 char *ofname;
79 char *progname;
80 static char *vendor = "TP-LINK Technologies";
81 static char *version = "ver. 1.0";
82 static char *fw_ver = "0.0.0";
83 static char *sver = "1.0";
84 static uint32_t hdr_ver = 2;
85
86 static struct board_info custom_board;
87
88 static struct board_info *board;
89 static char *layout_id;
90 struct flash_layout *layout;
91 static char *opt_hw_id;
92 static char *opt_hw_rev;
93 static char *opt_hw_ver_add;
94 static int fw_ver_lo;
95 static int fw_ver_mid;
96 static int fw_ver_hi;
97 static int sver_lo;
98 static int sver_hi;
99 struct file_info kernel_info;
100 static uint32_t kernel_la = 0;
101 static uint32_t kernel_ep = 0;
102 uint32_t kernel_len = 0;
103 struct file_info rootfs_info;
104 uint32_t rootfs_ofs = 0;
105 uint32_t rootfs_align;
106 static struct file_info boot_info;
107 int combined;
108 int strip_padding;
109 int add_jffs2_eof;
110
111 static struct file_info inspect_info;
112 static int extract = 0;
113
114 char md5salt_normal[MD5SUM_LEN] = {
115 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb,
116 0xdc, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x37,
117 };
118
119 char md5salt_boot[MD5SUM_LEN] = {
120 0x8c, 0xef, 0x33, 0x5f, 0xd5, 0xc5, 0xce, 0xfa,
121 0xac, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42,
122 };
123
124 static struct flash_layout layouts[] = {
125 {
126 .id = "4Mmtk",
127 .fw_max_len = 0x3d0000,
128 .kernel_la = 0x80000000,
129 .kernel_ep = 0x80000000,
130 .rootfs_ofs = 0x140000,
131 }, {
132 .id = "8Mltq",
133 .fw_max_len = 0x7a0000,
134 .kernel_la = 0x80002000,
135 .kernel_ep = 0x80002000,
136 .rootfs_ofs = 0x140000,
137 }, {
138 .id = "16Mltq",
139 .fw_max_len = 0xf90000,
140 .kernel_la = 0x80002000,
141 .kernel_ep = 0x800061b0,
142 .rootfs_ofs = 0x140000,
143 }, {
144 .id = "8Mmtk",
145 .fw_max_len = 0x7a0000,
146 .kernel_la = 0x80000000,
147 .kernel_ep = 0x80000000,
148 .rootfs_ofs = 0x140000,
149 }, {
150 .id = "8MSUmtk", /* Split U-Boot OS */
151 .fw_max_len = 0x770000,
152 .kernel_la = 0x80000000,
153 .kernel_ep = 0x80000000,
154 .rootfs_ofs = 0x140000,
155 }, {
156 .id = "8MLmtk",
157 .fw_max_len = 0x7b0000,
158 .kernel_la = 0x80000000,
159 .kernel_ep = 0x80000000,
160 .rootfs_ofs = 0x140000,
161 }, {
162 .id = "8Mqca",
163 .fw_max_len = 0x7a0000,
164 .kernel_la = 0x80060000,
165 .kernel_ep = 0x80060000,
166 .rootfs_ofs = 0x140000,
167 }, {
168 .id = "16Mqca",
169 .fw_max_len = 0xf90000,
170 .kernel_la = 0x80060000,
171 .kernel_ep = 0x80060000,
172 .rootfs_ofs = 0x140000,
173 }, {
174 /* terminating entry */
175 }
176 };
177
178 static void usage(int status)
179 {
180 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
181 struct board_info *board;
182
183 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
184 fprintf(stream,
185 "\n"
186 "Options:\n"
187 " -c use combined kernel image\n"
188 " -e swap endianness in kernel load address and entry point\n"
189 " -E <ep> overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n"
190 " -L <la> overwrite kernel load address with <la> (hexval prefixed with 0x)\n"
191 " -H <hwid> use hardware id specified with <hwid>\n"
192 " -W <hwrev> use hardware revision specified with <hwrev>\n"
193 " -w <hwveradd> use additional hardware version specified with <hwveradd>\n"
194 " -F <id> use flash layout specified with <id>\n"
195 " -k <file> read kernel image from the file <file>\n"
196 " -r <file> read rootfs image from the file <file>\n"
197 " -a <align> align the rootfs start on an <align> bytes boundary\n"
198 " -R <offset> overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
199 " -o <file> write output to the file <file>\n"
200 " -s strip padding from the end of the image\n"
201 " -j add jffs2 end-of-filesystem markers\n"
202 " -N <vendor> set image vendor to <vendor>\n"
203 " -T <version> set header version to <version>\n"
204 " -V <version> set image version to <version>\n"
205 " -v <version> set firmware version to <version>\n"
206 " -y <version> set secondary version to <version>\n"
207 " -i <file> inspect given firmware file <file>\n"
208 " -x extract kernel and rootfs while inspecting (requires -i)\n"
209 " -h show this screen\n"
210 );
211
212 exit(status);
213 }
214
215 static int check_options(void)
216 {
217 int ret;
218
219 if (inspect_info.file_name) {
220 ret = get_file_stat(&inspect_info);
221 if (ret)
222 return ret;
223
224 return 0;
225 } else if (extract) {
226 ERR("no firmware for inspection specified");
227 return -1;
228 }
229
230 if (opt_hw_id == NULL) {
231 ERR("hardware id must be specified");
232 return -1;
233 }
234
235 board = &custom_board;
236
237 if (layout_id == NULL) {
238 ERR("flash layout is not specified");
239 return -1;
240 }
241
242 board->hw_id = strtoul(opt_hw_id, NULL, 0);
243
244 board->hw_rev = 1;
245 board->hw_ver_add = 0;
246
247 if (opt_hw_rev)
248 board->hw_rev = strtoul(opt_hw_rev, NULL, 0);
249 if (opt_hw_ver_add)
250 board->hw_ver_add = strtoul(opt_hw_ver_add, NULL, 0);
251
252 layout = find_layout(layouts, layout_id);
253 if (layout == NULL) {
254 ERR("unknown flash layout \"%s\"", layout_id);
255 return -1;
256 }
257
258 if (!kernel_la)
259 kernel_la = layout->kernel_la;
260 if (!kernel_ep)
261 kernel_ep = layout->kernel_ep;
262 if (!rootfs_ofs)
263 rootfs_ofs = layout->rootfs_ofs;
264
265 if (kernel_info.file_name == NULL) {
266 ERR("no kernel image specified");
267 return -1;
268 }
269
270 ret = get_file_stat(&kernel_info);
271 if (ret)
272 return ret;
273
274 kernel_len = kernel_info.file_size;
275
276 if (combined) {
277 if (kernel_info.file_size >
278 layout->fw_max_len - sizeof(struct fw_header)) {
279 ERR("kernel image is too big");
280 return -1;
281 }
282 } else {
283 if (rootfs_info.file_name == NULL) {
284 ERR("no rootfs image specified");
285 return -1;
286 }
287
288 ret = get_file_stat(&rootfs_info);
289 if (ret)
290 return ret;
291
292 if (rootfs_align) {
293 kernel_len += sizeof(struct fw_header);
294 rootfs_ofs = ALIGN(kernel_len, rootfs_align);
295 kernel_len -= sizeof(struct fw_header);
296
297 DBG("rootfs offset aligned to 0x%u", rootfs_ofs);
298
299 if (kernel_len + rootfs_info.file_size >
300 layout->fw_max_len - sizeof(struct fw_header)) {
301 ERR("images are too big");
302 return -1;
303 }
304 } else {
305 if (kernel_info.file_size >
306 rootfs_ofs - sizeof(struct fw_header)) {
307 ERR("kernel image is too big");
308 return -1;
309 }
310
311 if (rootfs_info.file_size >
312 (layout->fw_max_len - rootfs_ofs)) {
313 ERR("rootfs image is too big");
314 return -1;
315 }
316 }
317 }
318
319 if (ofname == NULL) {
320 ERR("no output file specified");
321 return -1;
322 }
323
324 ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo);
325 if (ret != 3) {
326 ERR("invalid firmware version '%s'", fw_ver);
327 return -1;
328 }
329
330 ret = sscanf(sver, "%d.%d", &sver_hi, &sver_lo);
331 if (ret != 2) {
332 ERR("invalid secondary version '%s'", sver);
333 return -1;
334 }
335
336 return 0;
337 }
338
339 void fill_header(char *buf, int len)
340 {
341 struct fw_header *hdr = (struct fw_header *)buf;
342 unsigned ver_len;
343
344 memset(hdr, '\xff', sizeof(struct fw_header));
345
346 hdr->version = htonl(bswap_32(hdr_ver));
347 ver_len = strlen(version);
348 if (ver_len > (sizeof(hdr->fw_version) - 1))
349 ver_len = sizeof(hdr->fw_version) - 1;
350
351 memcpy(hdr->fw_version, version, ver_len);
352 hdr->fw_version[ver_len] = 0;
353
354 hdr->hw_id = htonl(board->hw_id);
355 hdr->hw_rev = htonl(board->hw_rev);
356 hdr->hw_ver_add = htonl(board->hw_ver_add);
357
358 if (boot_info.file_size == 0) {
359 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1));
360 hdr->boot_ofs = htonl(0);
361 hdr->boot_len = htonl(0);
362 } else {
363 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
364 hdr->boot_ofs = htonl(rootfs_ofs + rootfs_info.file_size);
365 hdr->boot_len = htonl(rootfs_info.file_size);
366 }
367
368 hdr->kernel_la = htonl(kernel_la);
369 hdr->kernel_ep = htonl(kernel_ep);
370 hdr->fw_length = htonl(layout->fw_max_len);
371 hdr->kernel_ofs = htonl(sizeof(struct fw_header));
372 hdr->kernel_len = htonl(kernel_len);
373 if (!combined) {
374 hdr->rootfs_ofs = htonl(rootfs_ofs);
375 hdr->rootfs_len = htonl(rootfs_info.file_size);
376 }
377
378 hdr->boot_ofs = htonl(0);
379 hdr->boot_len = htonl(boot_info.file_size);
380
381 hdr->unk2 = htonl(0);
382 hdr->unk3 = htonl(0xffffffff);
383 hdr->unk4 = htons(0x55aa);
384 hdr->unk5 = 0xa5;
385
386 hdr->sver_hi = sver_hi;
387 hdr->sver_lo = sver_lo;
388
389 hdr->ver_hi = fw_ver_hi;
390 hdr->ver_mid = fw_ver_mid;
391 hdr->ver_lo = fw_ver_lo;
392
393 if (board->flags & FLAG_LE_KERNEL_LA_EP) {
394 hdr->kernel_la = bswap_32(hdr->kernel_la);
395 hdr->kernel_ep = bswap_32(hdr->kernel_ep);
396 }
397
398 get_md5(buf, len, hdr->md5sum1);
399 }
400
401 static int inspect_fw(void)
402 {
403 char *buf;
404 struct fw_header *hdr;
405 uint8_t md5sum[MD5SUM_LEN];
406 struct board_info *board;
407 int ret = EXIT_FAILURE;
408
409 buf = malloc(inspect_info.file_size);
410 if (!buf) {
411 ERR("no memory for buffer!\n");
412 goto out;
413 }
414
415 ret = read_to_buf(&inspect_info, buf);
416 if (ret)
417 goto out_free_buf;
418 hdr = (struct fw_header *)buf;
419
420 board = &custom_board;
421
422 if (board->flags & FLAG_LE_KERNEL_LA_EP) {
423 hdr->kernel_la = bswap_32(hdr->kernel_la);
424 hdr->kernel_ep = bswap_32(hdr->kernel_ep);
425 }
426
427 inspect_fw_pstr("File name", inspect_info.file_name);
428 inspect_fw_phexdec("File size", inspect_info.file_size);
429
430 switch(bswap_32(ntohl(hdr->version))) {
431 case 2:
432 case 3:
433 break;
434 default:
435 ERR("file does not seem to have V2/V3 header!\n");
436 goto out_free_buf;
437 }
438
439 inspect_fw_phexdec("Version 2 Header size", sizeof(struct fw_header));
440
441 memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
442 if (ntohl(hdr->boot_len) == 0)
443 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
444 else
445 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
446 get_md5(buf, inspect_info.file_size, hdr->md5sum1);
447
448 if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
449 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)");
450 inspect_fw_pmd5sum(" --> expected", hdr->md5sum1, "");
451 } else {
452 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)");
453 }
454 if (ntohl(hdr->unk2) != 0)
455 inspect_fw_phexdec("Unknown value 2", hdr->unk2);
456 inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2,
457 "(purpose yet unknown, unchecked here)");
458
459 if (ntohl(hdr->unk3) != 0xffffffff)
460 inspect_fw_phexdec("Unknown value 3", hdr->unk3);
461
462 if (ntohs(hdr->unk4) != 0x55aa)
463 inspect_fw_phexdec("Unknown value 4", hdr->unk4);
464
465 if (hdr->unk5 != 0xa5)
466 inspect_fw_phexdec("Unknown value 5", hdr->unk5);
467
468 printf("\n");
469
470 inspect_fw_pstr("Firmware version", hdr->fw_version);
471 inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id));
472 inspect_fw_phex("Hardware Revision",
473 ntohl(hdr->hw_rev));
474 inspect_fw_phex("Additional HW Version",
475 ntohl(hdr->hw_ver_add));
476
477 printf("%-23s: %d.%d.%d-%d.%d\n", "Software version",
478 hdr->ver_hi, hdr->ver_mid, hdr->ver_lo,
479 hdr->sver_hi, hdr->sver_lo);
480
481 printf("\n");
482
483 inspect_fw_phexdec("Kernel data offset",
484 ntohl(hdr->kernel_ofs));
485 inspect_fw_phexdec("Kernel data length",
486 ntohl(hdr->kernel_len));
487 inspect_fw_phex("Kernel load address",
488 ntohl(hdr->kernel_la));
489 inspect_fw_phex("Kernel entry point",
490 ntohl(hdr->kernel_ep));
491 inspect_fw_phexdec("Rootfs data offset",
492 ntohl(hdr->rootfs_ofs));
493 inspect_fw_phexdec("Rootfs data length",
494 ntohl(hdr->rootfs_len));
495 inspect_fw_phexdec("Boot loader data offset",
496 ntohl(hdr->boot_ofs));
497 inspect_fw_phexdec("Boot loader data length",
498 ntohl(hdr->boot_len));
499 inspect_fw_phexdec("Total firmware length",
500 ntohl(hdr->fw_length));
501
502 if (extract) {
503 FILE *fp;
504 char *filename;
505
506 printf("\n");
507
508 filename = malloc(strlen(inspect_info.file_name) + 8);
509 sprintf(filename, "%s-kernel", inspect_info.file_name);
510 printf("Extracting kernel to \"%s\"...\n", filename);
511 fp = fopen(filename, "w");
512 if (fp) {
513 if (!fwrite(buf + ntohl(hdr->kernel_ofs),
514 ntohl(hdr->kernel_len), 1, fp)) {
515 ERR("error in fwrite(): %s", strerror(errno));
516 }
517 fclose(fp);
518 } else {
519 ERR("error in fopen(): %s", strerror(errno));
520 }
521 free(filename);
522
523 filename = malloc(strlen(inspect_info.file_name) + 8);
524 sprintf(filename, "%s-rootfs", inspect_info.file_name);
525 printf("Extracting rootfs to \"%s\"...\n", filename);
526 fp = fopen(filename, "w");
527 if (fp) {
528 if (!fwrite(buf + ntohl(hdr->rootfs_ofs),
529 ntohl(hdr->rootfs_len), 1, fp)) {
530 ERR("error in fwrite(): %s", strerror(errno));
531 }
532 fclose(fp);
533 } else {
534 ERR("error in fopen(): %s", strerror(errno));
535 }
536 free(filename);
537 }
538
539 out_free_buf:
540 free(buf);
541 out:
542 return ret;
543 }
544
545 int main(int argc, char *argv[])
546 {
547 int ret = EXIT_FAILURE;
548
549 progname = basename(argv[0]);
550
551 while ( 1 ) {
552 int c;
553
554 c = getopt(argc, argv, "a:H:E:F:L:V:N:W:w:ci:k:r:R:o:xhsjv:y:T:e");
555 if (c == -1)
556 break;
557
558 switch (c) {
559 case 'a':
560 sscanf(optarg, "0x%x", &rootfs_align);
561 break;
562 case 'H':
563 opt_hw_id = optarg;
564 break;
565 case 'E':
566 sscanf(optarg, "0x%x", &kernel_ep);
567 break;
568 case 'F':
569 layout_id = optarg;
570 break;
571 case 'W':
572 opt_hw_rev = optarg;
573 break;
574 case 'w':
575 opt_hw_ver_add = optarg;
576 break;
577 case 'L':
578 sscanf(optarg, "0x%x", &kernel_la);
579 break;
580 case 'V':
581 version = optarg;
582 break;
583 case 'v':
584 fw_ver = optarg;
585 break;
586 case 'y':
587 sver = optarg;
588 break;
589 case 'N':
590 vendor = optarg;
591 break;
592 case 'c':
593 combined++;
594 break;
595 case 'k':
596 kernel_info.file_name = optarg;
597 break;
598 case 'r':
599 rootfs_info.file_name = optarg;
600 break;
601 case 'R':
602 sscanf(optarg, "0x%x", &rootfs_ofs);
603 break;
604 case 'o':
605 ofname = optarg;
606 break;
607 case 's':
608 strip_padding = 1;
609 break;
610 case 'i':
611 inspect_info.file_name = optarg;
612 break;
613 case 'j':
614 add_jffs2_eof = 1;
615 break;
616 case 'x':
617 extract = 1;
618 break;
619 case 'T':
620 hdr_ver = atoi(optarg);
621 break;
622 case 'e':
623 custom_board.flags = FLAG_LE_KERNEL_LA_EP;
624 break;
625 case 'h':
626 usage(EXIT_SUCCESS);
627 break;
628 default:
629 usage(EXIT_FAILURE);
630 break;
631 }
632 }
633
634 ret = check_options();
635 if (ret)
636 goto out;
637
638 if (!inspect_info.file_name)
639 ret = build_fw(sizeof(struct fw_header));
640 else
641 ret = inspect_fw();
642
643 out:
644 return ret;
645 }
646