3cb5ba0835ff9ca431717fcd869753a478af1e49
[oweals/openwrt.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     = 0x3c0000,
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             = "8MLmtk",
151                 .fw_max_len     = 0x7b0000,
152                 .kernel_la      = 0x80000000,
153                 .kernel_ep      = 0x80000000,
154                 .rootfs_ofs     = 0x140000,
155         }, {
156                 /* terminating entry */
157         }
158 };
159
160 static void usage(int status)
161 {
162         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
163         struct board_info *board;
164
165         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
166         fprintf(stream,
167 "\n"
168 "Options:\n"
169 "  -c              use combined kernel image\n"
170 "  -e              swap endianness in kernel load address and entry point\n"
171 "  -E <ep>         overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n"
172 "  -L <la>         overwrite kernel load address with <la> (hexval prefixed with 0x)\n"
173 "  -H <hwid>       use hardware id specified with <hwid>\n"
174 "  -W <hwrev>      use hardware revision specified with <hwrev>\n"
175 "  -w <hwveradd>   use additional hardware version specified with <hwveradd>\n"
176 "  -F <id>         use flash layout specified with <id>\n"
177 "  -k <file>       read kernel image from the file <file>\n"
178 "  -r <file>       read rootfs image from the file <file>\n"
179 "  -a <align>      align the rootfs start on an <align> bytes boundary\n"
180 "  -R <offset>     overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
181 "  -o <file>       write output to the file <file>\n"
182 "  -s              strip padding from the end of the image\n"
183 "  -j              add jffs2 end-of-filesystem markers\n"
184 "  -N <vendor>     set image vendor to <vendor>\n"
185 "  -T <version>    set header version to <version>\n"
186 "  -V <version>    set image version to <version>\n"
187 "  -v <version>    set firmware version to <version>\n"
188 "  -y <version>    set secondary version to <version>\n"
189 "  -i <file>       inspect given firmware file <file>\n"
190 "  -x              extract kernel and rootfs while inspecting (requires -i)\n"
191 "  -h              show this screen\n"
192         );
193
194         exit(status);
195 }
196
197 static int check_options(void)
198 {
199         int ret;
200
201         if (inspect_info.file_name) {
202                 ret = get_file_stat(&inspect_info);
203                 if (ret)
204                         return ret;
205
206                 return 0;
207         } else if (extract) {
208                 ERR("no firmware for inspection specified");
209                 return -1;
210         }
211
212         if (opt_hw_id == NULL) {
213                 ERR("hardware id must be specified");
214                 return -1;
215         }
216
217         board = &custom_board;
218
219         if (layout_id == NULL) {
220                 ERR("flash layout is not specified");
221                 return -1;
222         }
223
224         board->hw_id = strtoul(opt_hw_id, NULL, 0);
225
226         board->hw_rev = 1;
227         board->hw_ver_add = 0;
228
229         if (opt_hw_rev)
230                 board->hw_rev = strtoul(opt_hw_rev, NULL, 0);
231         if (opt_hw_ver_add)
232                 board->hw_ver_add = strtoul(opt_hw_ver_add, NULL, 0);
233
234         layout = find_layout(layouts, layout_id);
235         if (layout == NULL) {
236                 ERR("unknown flash layout \"%s\"", layout_id);
237                 return -1;
238         }
239
240         if (!kernel_la)
241                 kernel_la = layout->kernel_la;
242         if (!kernel_ep)
243                 kernel_ep = layout->kernel_ep;
244         if (!rootfs_ofs)
245                 rootfs_ofs = layout->rootfs_ofs;
246
247         if (kernel_info.file_name == NULL) {
248                 ERR("no kernel image specified");
249                 return -1;
250         }
251
252         ret = get_file_stat(&kernel_info);
253         if (ret)
254                 return ret;
255
256         kernel_len = kernel_info.file_size;
257
258         if (combined) {
259                 if (kernel_info.file_size >
260                     layout->fw_max_len - sizeof(struct fw_header)) {
261                         ERR("kernel image is too big");
262                         return -1;
263                 }
264         } else {
265                 if (rootfs_info.file_name == NULL) {
266                         ERR("no rootfs image specified");
267                         return -1;
268                 }
269
270                 ret = get_file_stat(&rootfs_info);
271                 if (ret)
272                         return ret;
273
274                 if (rootfs_align) {
275                         kernel_len += sizeof(struct fw_header);
276                         rootfs_ofs = ALIGN(kernel_len, rootfs_align);
277                         kernel_len -= sizeof(struct fw_header);
278
279                         DBG("rootfs offset aligned to 0x%u", rootfs_ofs);
280
281                         if (kernel_len + rootfs_info.file_size >
282                             layout->fw_max_len - sizeof(struct fw_header)) {
283                                 ERR("images are too big");
284                                 return -1;
285                         }
286                 } else {
287                         if (kernel_info.file_size >
288                             rootfs_ofs - sizeof(struct fw_header)) {
289                                 ERR("kernel image is too big");
290                                 return -1;
291                         }
292
293                         if (rootfs_info.file_size >
294                             (layout->fw_max_len - rootfs_ofs)) {
295                                 ERR("rootfs image is too big");
296                                 return -1;
297                         }
298                 }
299         }
300
301         if (ofname == NULL) {
302                 ERR("no output file specified");
303                 return -1;
304         }
305
306         ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo);
307         if (ret != 3) {
308                 ERR("invalid firmware version '%s'", fw_ver);
309                 return -1;
310         }
311
312         ret = sscanf(sver, "%d.%d", &sver_hi, &sver_lo);
313         if (ret != 2) {
314                 ERR("invalid secondary version '%s'", sver);
315                 return -1;
316         }
317
318         return 0;
319 }
320
321 void fill_header(char *buf, int len)
322 {
323         struct fw_header *hdr = (struct fw_header *)buf;
324         unsigned ver_len;
325
326         memset(hdr, '\xff', sizeof(struct fw_header));
327
328         hdr->version = htonl(bswap_32(hdr_ver));
329         ver_len = strlen(version);
330         if (ver_len > (sizeof(hdr->fw_version) - 1))
331                 ver_len = sizeof(hdr->fw_version) - 1;
332
333         memcpy(hdr->fw_version, version, ver_len);
334         hdr->fw_version[ver_len] = 0;
335
336         hdr->hw_id = htonl(board->hw_id);
337         hdr->hw_rev = htonl(board->hw_rev);
338         hdr->hw_ver_add = htonl(board->hw_ver_add);
339
340         if (boot_info.file_size == 0) {
341                 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1));
342                 hdr->boot_ofs = htonl(0);
343                 hdr->boot_len = htonl(0);
344         } else {
345                 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
346                 hdr->boot_ofs = htonl(rootfs_ofs + rootfs_info.file_size);
347                 hdr->boot_len = htonl(rootfs_info.file_size);
348         }
349
350         hdr->kernel_la = htonl(kernel_la);
351         hdr->kernel_ep = htonl(kernel_ep);
352         hdr->fw_length = htonl(layout->fw_max_len);
353         hdr->kernel_ofs = htonl(sizeof(struct fw_header));
354         hdr->kernel_len = htonl(kernel_len);
355         if (!combined) {
356                 hdr->rootfs_ofs = htonl(rootfs_ofs);
357                 hdr->rootfs_len = htonl(rootfs_info.file_size);
358         }
359
360         hdr->boot_ofs = htonl(0);
361         hdr->boot_len = htonl(boot_info.file_size);
362
363         hdr->unk2 = htonl(0);
364         hdr->unk3 = htonl(0xffffffff);
365         hdr->unk4 = htons(0x55aa);
366         hdr->unk5 = 0xa5;
367
368         hdr->sver_hi = sver_hi;
369         hdr->sver_lo = sver_lo;
370
371         hdr->ver_hi = fw_ver_hi;
372         hdr->ver_mid = fw_ver_mid;
373         hdr->ver_lo = fw_ver_lo;
374
375         if (board->flags & FLAG_LE_KERNEL_LA_EP) {
376                 hdr->kernel_la = bswap_32(hdr->kernel_la);
377                 hdr->kernel_ep = bswap_32(hdr->kernel_ep);
378         }
379
380         get_md5(buf, len, hdr->md5sum1);
381 }
382
383 static int inspect_fw(void)
384 {
385         char *buf;
386         struct fw_header *hdr;
387         uint8_t md5sum[MD5SUM_LEN];
388         struct board_info *board;
389         int ret = EXIT_FAILURE;
390
391         buf = malloc(inspect_info.file_size);
392         if (!buf) {
393                 ERR("no memory for buffer!\n");
394                 goto out;
395         }
396
397         ret = read_to_buf(&inspect_info, buf);
398         if (ret)
399                 goto out_free_buf;
400         hdr = (struct fw_header *)buf;
401
402         board = &custom_board;
403
404         if (board->flags & FLAG_LE_KERNEL_LA_EP) {
405                 hdr->kernel_la = bswap_32(hdr->kernel_la);
406                 hdr->kernel_ep = bswap_32(hdr->kernel_ep);
407         }
408
409         inspect_fw_pstr("File name", inspect_info.file_name);
410         inspect_fw_phexdec("File size", inspect_info.file_size);
411
412         switch(bswap_32(ntohl(hdr->version))) {
413         case 2:
414         case 3:
415                 break;
416         default:
417                 ERR("file does not seem to have V2/V3 header!\n");
418                 goto out_free_buf;
419         }
420
421         inspect_fw_phexdec("Version 2 Header size", sizeof(struct fw_header));
422
423         memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
424         if (ntohl(hdr->boot_len) == 0)
425                 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
426         else
427                 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
428         get_md5(buf, inspect_info.file_size, hdr->md5sum1);
429
430         if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
431                 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)");
432                 inspect_fw_pmd5sum("          --> expected", hdr->md5sum1, "");
433         } else {
434                 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)");
435         }
436         if (ntohl(hdr->unk2) != 0)
437                 inspect_fw_phexdec("Unknown value 2", hdr->unk2);
438         inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2,
439                            "(purpose yet unknown, unchecked here)");
440
441         if (ntohl(hdr->unk3) != 0xffffffff)
442                 inspect_fw_phexdec("Unknown value 3", hdr->unk3);
443
444         if (ntohs(hdr->unk4) != 0x55aa)
445                 inspect_fw_phexdec("Unknown value 4", hdr->unk4);
446
447         if (hdr->unk5 != 0xa5)
448                 inspect_fw_phexdec("Unknown value 5", hdr->unk5);
449
450         printf("\n");
451
452         inspect_fw_pstr("Firmware version", hdr->fw_version);
453         inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id));
454         inspect_fw_phex("Hardware Revision",
455                         ntohl(hdr->hw_rev));
456         inspect_fw_phex("Additional HW Version",
457                         ntohl(hdr->hw_ver_add));
458
459         printf("%-23s: %d.%d.%d-%d.%d\n", "Software version",
460                hdr->ver_hi, hdr->ver_mid, hdr->ver_lo,
461                hdr->sver_hi, hdr->sver_lo);
462
463         printf("\n");
464
465         inspect_fw_phexdec("Kernel data offset",
466                            ntohl(hdr->kernel_ofs));
467         inspect_fw_phexdec("Kernel data length",
468                            ntohl(hdr->kernel_len));
469         inspect_fw_phex("Kernel load address",
470                         ntohl(hdr->kernel_la));
471         inspect_fw_phex("Kernel entry point",
472                         ntohl(hdr->kernel_ep));
473         inspect_fw_phexdec("Rootfs data offset",
474                            ntohl(hdr->rootfs_ofs));
475         inspect_fw_phexdec("Rootfs data length",
476                            ntohl(hdr->rootfs_len));
477         inspect_fw_phexdec("Boot loader data offset",
478                            ntohl(hdr->boot_ofs));
479         inspect_fw_phexdec("Boot loader data length",
480                            ntohl(hdr->boot_len));
481         inspect_fw_phexdec("Total firmware length",
482                            ntohl(hdr->fw_length));
483
484         if (extract) {
485                 FILE *fp;
486                 char *filename;
487
488                 printf("\n");
489
490                 filename = malloc(strlen(inspect_info.file_name) + 8);
491                 sprintf(filename, "%s-kernel", inspect_info.file_name);
492                 printf("Extracting kernel to \"%s\"...\n", filename);
493                 fp = fopen(filename, "w");
494                 if (fp) {
495                         if (!fwrite(buf + ntohl(hdr->kernel_ofs),
496                                     ntohl(hdr->kernel_len), 1, fp)) {
497                                 ERR("error in fwrite(): %s", strerror(errno));
498                         }
499                         fclose(fp);
500                 } else {
501                         ERR("error in fopen(): %s", strerror(errno));
502                 }
503                 free(filename);
504
505                 filename = malloc(strlen(inspect_info.file_name) + 8);
506                 sprintf(filename, "%s-rootfs", inspect_info.file_name);
507                 printf("Extracting rootfs to \"%s\"...\n", filename);
508                 fp = fopen(filename, "w");
509                 if (fp) {
510                         if (!fwrite(buf + ntohl(hdr->rootfs_ofs),
511                                     ntohl(hdr->rootfs_len), 1, fp)) {
512                                 ERR("error in fwrite(): %s", strerror(errno));
513                         }
514                         fclose(fp);
515                 } else {
516                         ERR("error in fopen(): %s", strerror(errno));
517                 }
518                 free(filename);
519         }
520
521  out_free_buf:
522         free(buf);
523  out:
524         return ret;
525 }
526
527 int main(int argc, char *argv[])
528 {
529         int ret = EXIT_FAILURE;
530
531         progname = basename(argv[0]);
532
533         while ( 1 ) {
534                 int c;
535
536                 c = getopt(argc, argv, "a:H:E:F:L:V:N:W:w:ci:k:r:R:o:xhsjv:y:T:e");
537                 if (c == -1)
538                         break;
539
540                 switch (c) {
541                 case 'a':
542                         sscanf(optarg, "0x%x", &rootfs_align);
543                         break;
544                 case 'H':
545                         opt_hw_id = optarg;
546                         break;
547                 case 'E':
548                         sscanf(optarg, "0x%x", &kernel_ep);
549                         break;
550                 case 'F':
551                         layout_id = optarg;
552                         break;
553                 case 'W':
554                         opt_hw_rev = optarg;
555                         break;
556                 case 'w':
557                         opt_hw_ver_add = optarg;
558                         break;
559                 case 'L':
560                         sscanf(optarg, "0x%x", &kernel_la);
561                         break;
562                 case 'V':
563                         version = optarg;
564                         break;
565                 case 'v':
566                         fw_ver = optarg;
567                         break;
568                 case 'y':
569                         sver = optarg;
570                         break;
571                 case 'N':
572                         vendor = optarg;
573                         break;
574                 case 'c':
575                         combined++;
576                         break;
577                 case 'k':
578                         kernel_info.file_name = optarg;
579                         break;
580                 case 'r':
581                         rootfs_info.file_name = optarg;
582                         break;
583                 case 'R':
584                         sscanf(optarg, "0x%x", &rootfs_ofs);
585                         break;
586                 case 'o':
587                         ofname = optarg;
588                         break;
589                 case 's':
590                         strip_padding = 1;
591                         break;
592                 case 'i':
593                         inspect_info.file_name = optarg;
594                         break;
595                 case 'j':
596                         add_jffs2_eof = 1;
597                         break;
598                 case 'x':
599                         extract = 1;
600                         break;
601                 case 'T':
602                         hdr_ver = atoi(optarg);
603                         break;
604                 case 'e':
605                         custom_board.flags = FLAG_LE_KERNEL_LA_EP;
606                         break;
607                 case 'h':
608                         usage(EXIT_SUCCESS);
609                         break;
610                 default:
611                         usage(EXIT_FAILURE);
612                         break;
613                 }
614         }
615
616         ret = check_options();
617         if (ret)
618                 goto out;
619
620         if (!inspect_info.file_name)
621                 ret = build_fw(sizeof(struct fw_header));
622         else
623                 ret = inspect_fw();
624
625  out:
626         return ret;
627 }
628