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