Upstream refresh for v1.5.0-rc1 : Upstream 19.07 : 4fb6b8c553f692eeb5bcb203e0f8ee8df0...
[librecmc/librecmc.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