firmware-utils/mktplinkfw: add ability to put jffs2 eof marker into the image
[openwrt.git] / tools / firmware-utils / src / mktplinkfw.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 <sys/stat.h>
24
25 #include <arpa/inet.h>
26 #include <netinet/in.h>
27
28 #include "md5.h"
29
30 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
31
32 #define HEADER_VERSION_V1       0x01000000
33 #define HWID_TL_MR3220_V1       0x32200001
34 #define HWID_TL_MR3420_V1       0x34200001
35 #define HWID_TL_WA901ND_V1      0x09010001
36 #define HWID_TL_WA901ND_V2      0x09010002
37 #define HWID_TL_WR703N_V1       0x07030101
38 #define HWID_TL_WR741ND_V1      0x07410001
39 #define HWID_TL_WR741ND_V4      0x07410004
40 #define HWID_TL_WR740N_V1       0x07400001
41 #define HWID_TL_WR740N_V3       0x07400003
42 #define HWID_TL_WR743ND_V1      0x07430001
43 #define HWID_TL_WR841N_V1_5     0x08410002
44 #define HWID_TL_WR841ND_V3      0x08410003
45 #define HWID_TL_WR841ND_V5      0x08410005
46 #define HWID_TL_WR841ND_V7      0x08410007
47 #define HWID_TL_WR941ND_V2      0x09410002
48 #define HWID_TL_WR941ND_V4      0x09410004
49 #define HWID_TL_WR1043ND_V1     0x10430001
50
51 #define MD5SUM_LEN      16
52
53 struct file_info {
54         char            *file_name;     /* name of the file */
55         uint32_t        file_size;      /* length of the file */
56 };
57
58 struct fw_header {
59         uint32_t        version;        /* header version */
60         char            vendor_name[24];
61         char            fw_version[36];
62         uint32_t        hw_id;          /* hardware id */
63         uint32_t        hw_rev;         /* hardware revision */
64         uint32_t        unk1;
65         uint8_t         md5sum1[MD5SUM_LEN];
66         uint32_t        unk2;
67         uint8_t         md5sum2[MD5SUM_LEN];
68         uint32_t        unk3;
69         uint32_t        kernel_la;      /* kernel load address */
70         uint32_t        kernel_ep;      /* kernel entry point */
71         uint32_t        fw_length;      /* total length of the firmware */
72         uint32_t        kernel_ofs;     /* kernel data offset */
73         uint32_t        kernel_len;     /* kernel data length */
74         uint32_t        rootfs_ofs;     /* rootfs data offset */
75         uint32_t        rootfs_len;     /* rootfs data length */
76         uint32_t        boot_ofs;       /* bootloader data offset */
77         uint32_t        boot_len;       /* bootloader data length */
78         uint8_t         pad[360];
79 } __attribute__ ((packed));
80
81 struct flash_layout {
82         char            *id;
83         uint32_t        fw_max_len;
84         uint32_t        kernel_la;
85         uint32_t        kernel_ep;
86         uint32_t        rootfs_ofs;
87 };
88
89 struct board_info {
90         char            *id;
91         uint32_t        hw_id;
92         uint32_t        hw_rev;
93         char            *layout_id;
94 };
95
96 /*
97  * Globals
98  */
99 static char *ofname;
100 static char *progname;
101 static char *vendor = "TP-LINK Technologies";
102 static char *version = "ver. 1.0";
103
104 static char *board_id;
105 static struct board_info *board;
106 static char *layout_id;
107 static struct flash_layout *layout;
108 static char *opt_hw_id;
109 static uint32_t hw_id;
110 static char *opt_hw_rev;
111 static uint32_t hw_rev;
112 static struct file_info kernel_info;
113 static uint32_t kernel_la = 0;
114 static uint32_t kernel_ep = 0;
115 static uint32_t kernel_len = 0;
116 static struct file_info rootfs_info;
117 static uint32_t rootfs_ofs = 0;
118 static uint32_t rootfs_align;
119 static struct file_info boot_info;
120 static int combined;
121 static int strip_padding;
122 static int add_jffs2_eof;
123 static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
124
125 static struct file_info inspect_info;
126 static int extract = 0;
127
128 char md5salt_normal[MD5SUM_LEN] = {
129         0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb,
130         0xdd, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x38,
131 };
132
133 char md5salt_boot[MD5SUM_LEN] = {
134         0x8c, 0xef, 0x33, 0x5b, 0xd5, 0xc5, 0xce, 0xfa,
135         0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42,
136 };
137
138 static struct flash_layout layouts[] = {
139         {
140                 .id             = "4M",
141                 .fw_max_len     = 0x3c0000,
142                 .kernel_la      = 0x80060000,
143                 .kernel_ep      = 0x80060000,
144                 .rootfs_ofs     = 0x140000,
145         }, {
146                 .id             = "4Mlzma",
147                 .fw_max_len     = 0x3c0000,
148                 .kernel_la      = 0x80060000,
149                 .kernel_ep      = 0x80060000,
150                 .rootfs_ofs     = 0x100000,
151         }, {
152                 .id             = "8M",
153                 .fw_max_len     = 0x7c0000,
154                 .kernel_la      = 0x80060000,
155                 .kernel_ep      = 0x80060000,
156                 .rootfs_ofs     = 0x140000,
157         }, {
158                 /* terminating entry */
159         }
160 };
161
162 static struct board_info boards[] = {
163         {
164                 .id             = "TL-MR3220v1",
165                 .hw_id          = HWID_TL_MR3220_V1,
166                 .hw_rev         = 1,
167                 .layout_id      = "4M",
168         }, {
169                 .id             = "TL-MR3420v1",
170                 .hw_id          = HWID_TL_MR3420_V1,
171                 .hw_rev         = 1,
172                 .layout_id      = "4M",
173         }, {
174                 .id             = "TL-WA901NDv1",
175                 .hw_id          = HWID_TL_WA901ND_V1,
176                 .hw_rev         = 1,
177                 .layout_id      = "4M",
178         }, {
179                 .id             = "TL-WA901NDv2",
180                 .hw_id          = HWID_TL_WA901ND_V2,
181                 .hw_rev         = 1,
182                 .layout_id      = "4M",
183         }, {
184                 .id             = "TL-WR741NDv1",
185                 .hw_id          = HWID_TL_WR741ND_V1,
186                 .hw_rev         = 1,
187                 .layout_id      = "4M",
188         }, {
189                 .id             = "TL-WR741NDv4",
190                 .hw_id          = HWID_TL_WR741ND_V4,
191                 .hw_rev         = 1,
192                 .layout_id      = "4Mlzma",
193         }, {
194                 .id             = "TL-WR740Nv1",
195                 .hw_id          = HWID_TL_WR740N_V1,
196                 .hw_rev         = 1,
197                 .layout_id      = "4M",
198         }, {
199                 .id             = "TL-WR740Nv3",
200                 .hw_id          = HWID_TL_WR740N_V3,
201                 .hw_rev         = 1,
202                 .layout_id      = "4M",
203         }, {
204                 .id             = "TL-WR743NDv1",
205                 .hw_id          = HWID_TL_WR743ND_V1,
206                 .hw_rev         = 1,
207                 .layout_id      = "4M",
208         }, {
209                 .id             = "TL-WR841Nv1.5",
210                 .hw_id          = HWID_TL_WR841N_V1_5,
211                 .hw_rev         = 2,
212                 .layout_id      = "4M",
213         }, {
214                 .id             = "TL-WR841NDv3",
215                 .hw_id          = HWID_TL_WR841ND_V3,
216                 .hw_rev         = 3,
217                 .layout_id      = "4M",
218         }, {
219                 .id             = "TL-WR841NDv5",
220                 .hw_id          = HWID_TL_WR841ND_V5,
221                 .hw_rev         = 1,
222                 .layout_id      = "4M",
223         }, {
224                 .id             = "TL-WR841NDv7",
225                 .hw_id          = HWID_TL_WR841ND_V7,
226                 .hw_rev         = 1,
227                 .layout_id      = "4M",
228         }, {
229                 .id             = "TL-WR941NDv2",
230                 .hw_id          = HWID_TL_WR941ND_V2,
231                 .hw_rev         = 2,
232                 .layout_id      = "4M",
233         }, {
234                 .id             = "TL-WR941NDv4",
235                 .hw_id          = HWID_TL_WR941ND_V4,
236                 .hw_rev         = 1,
237                 .layout_id      = "4M",
238         }, {
239                 .id             = "TL-WR1043NDv1",
240                 .hw_id          = HWID_TL_WR1043ND_V1,
241                 .hw_rev         = 1,
242                 .layout_id      = "8M",
243         }, {
244                 .id             = "TL-WR703Nv1",
245                 .hw_id          = HWID_TL_WR703N_V1,
246                 .hw_rev         = 1,
247                 .layout_id      = "4Mlzma",
248         }, {
249                 /* terminating entry */
250         }
251 };
252
253 /*
254  * Message macros
255  */
256 #define ERR(fmt, ...) do { \
257         fflush(0); \
258         fprintf(stderr, "[%s] *** error: " fmt "\n", \
259                         progname, ## __VA_ARGS__ ); \
260 } while (0)
261
262 #define ERRS(fmt, ...) do { \
263         int save = errno; \
264         fflush(0); \
265         fprintf(stderr, "[%s] *** error: " fmt "\n", \
266                         progname, ## __VA_ARGS__, strerror(save)); \
267 } while (0)
268
269 #define DBG(fmt, ...) do { \
270         fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
271 } while (0)
272
273 static struct board_info *find_board(char *id)
274 {
275         struct board_info *ret;
276         struct board_info *board;
277
278         ret = NULL;
279         for (board = boards; board->id != NULL; board++){
280                 if (strcasecmp(id, board->id) == 0) {
281                         ret = board;
282                         break;
283                 }
284         };
285
286         return ret;
287 }
288
289 static struct board_info *find_board_by_hwid(uint32_t hw_id)
290 {
291         struct board_info *board;
292
293         for (board = boards; board->id != NULL; board++) {
294                 if (hw_id == board->hw_id)
295                         return board;
296         };
297
298         return NULL;
299 }
300
301 static struct flash_layout *find_layout(char *id)
302 {
303         struct flash_layout *ret;
304         struct flash_layout *l;
305
306         ret = NULL;
307         for (l = layouts; l->id != NULL; l++){
308                 if (strcasecmp(id, l->id) == 0) {
309                         ret = l;
310                         break;
311                 }
312         };
313
314         return ret;
315 }
316
317 static void usage(int status)
318 {
319         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
320         struct board_info *board;
321
322         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
323         fprintf(stream,
324 "\n"
325 "Options:\n"
326 "  -B <board>      create image for the board specified with <board>\n"
327 "  -c              use combined kernel image\n"
328 "  -E <ep>         overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n"
329 "  -L <la>         overwrite kernel load address with <la> (hexval prefixed with 0x)\n"
330 "  -H <hwid>       use hardware id specified with <hwid>\n"
331 "  -F <id>         use flash layout specified with <id>\n"
332 "  -k <file>       read kernel image from the file <file>\n"
333 "  -r <file>       read rootfs image from the file <file>\n"
334 "  -a <align>      align the rootfs start on an <align> bytes boundary\n"
335 "  -R <offset>     overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
336 "  -o <file>       write output to the file <file>\n"
337 "  -s              strip padding from the end of the image\n"
338 "  -j              add jffs2 end-of-filesystem markers\n"
339 "  -N <vendor>     set image vendor to <vendor>\n"
340 "  -V <version>    set image version to <version>\n"
341 "  -i <file>       inspect given firmware file <file>\n"
342 "  -x              extract kernel and rootfs while inspecting (requires -i)\n"
343 "  -h              show this screen\n"
344         );
345
346         exit(status);
347 }
348
349 static int get_md5(char *data, int size, char *md5)
350 {
351         MD5_CTX ctx;
352
353         MD5_Init(&ctx);
354         MD5_Update(&ctx, data, size);
355         MD5_Final(md5, &ctx);
356 }
357
358 static int get_file_stat(struct file_info *fdata)
359 {
360         struct stat st;
361         int res;
362
363         if (fdata->file_name == NULL)
364                 return 0;
365
366         res = stat(fdata->file_name, &st);
367         if (res){
368                 ERRS("stat failed on %s", fdata->file_name);
369                 return res;
370         }
371
372         fdata->file_size = st.st_size;
373         return 0;
374 }
375
376 static int read_to_buf(struct file_info *fdata, char *buf)
377 {
378         FILE *f;
379         int ret = EXIT_FAILURE;
380
381         f = fopen(fdata->file_name, "r");
382         if (f == NULL) {
383                 ERRS("could not open \"%s\" for reading", fdata->file_name);
384                 goto out;
385         }
386
387         errno = 0;
388         fread(buf, fdata->file_size, 1, f);
389         if (errno != 0) {
390                 ERRS("unable to read from file \"%s\"", fdata->file_name);
391                 goto out_close;
392         }
393
394         ret = EXIT_SUCCESS;
395
396  out_close:
397         fclose(f);
398  out:
399         return ret;
400 }
401
402 static int check_options(void)
403 {
404         int ret;
405
406         if (inspect_info.file_name) {
407                 ret = get_file_stat(&inspect_info);
408                 if (ret)
409                         return ret;
410
411                 return 0;
412         } else if (extract) {
413                 ERR("no firmware for inspection specified");
414                 return -1;
415         }
416
417         if (board_id == NULL && opt_hw_id == NULL) {
418                 ERR("either board or hardware id must be specified");
419                 return -1;
420         }
421
422         if (board_id) {
423                 board = find_board(board_id);
424                 if (board == NULL) {
425                         ERR("unknown/unsupported board id \"%s\"", board_id);
426                         return -1;
427                 }
428                 if (layout_id == NULL)
429                         layout_id = board->layout_id;
430
431                 hw_id = board->hw_id;
432                 hw_rev = board->hw_rev;
433         } else {
434                 if (layout_id == NULL) {
435                         ERR("flash layout is not specified");
436                         return -1;
437                 }
438                 hw_id = strtoul(opt_hw_id, NULL, 0);
439
440                 if (opt_hw_rev)
441                         hw_rev = strtoul(opt_hw_rev, NULL, 0);
442                 else
443                         hw_rev = 1;
444         }
445
446         layout = find_layout(layout_id);
447         if (layout == NULL) {
448                 ERR("unknown flash layout \"%s\"", layout_id);
449                 return -1;
450         }
451
452         if (!kernel_la)
453                 kernel_la = layout->kernel_la;
454         if (!kernel_ep)
455                 kernel_ep = layout->kernel_ep;
456         if (!rootfs_ofs)
457                 rootfs_ofs = layout->rootfs_ofs;
458
459         if (kernel_info.file_name == NULL) {
460                 ERR("no kernel image specified");
461                 return -1;
462         }
463
464         ret = get_file_stat(&kernel_info);
465         if (ret)
466                 return ret;
467
468         kernel_len = kernel_info.file_size;
469
470         if (combined) {
471                 if (kernel_info.file_size >
472                     layout->fw_max_len - sizeof(struct fw_header)) {
473                         ERR("kernel image is too big");
474                         return -1;
475                 }
476         } else {
477                 if (rootfs_info.file_name == NULL) {
478                         ERR("no rootfs image specified");
479                         return -1;
480                 }
481
482                 ret = get_file_stat(&rootfs_info);
483                 if (ret)
484                         return ret;
485
486                 if (rootfs_align) {
487                         kernel_len += sizeof(struct fw_header);
488                         kernel_len = ALIGN(kernel_len, rootfs_align);
489                         kernel_len -= sizeof(struct fw_header);
490
491                         DBG("kernel length aligned to %u", kernel_len);
492
493                         if (kernel_len + rootfs_info.file_size >
494                             layout->fw_max_len - sizeof(struct fw_header)) {
495                                 ERR("images are too big");
496                                 return -1;
497                         }
498                 } else {
499                         if (kernel_info.file_size >
500                             rootfs_ofs - sizeof(struct fw_header)) {
501                                 ERR("kernel image is too big");
502                                 return -1;
503                         }
504
505                         if (rootfs_info.file_size >
506                             (layout->fw_max_len - rootfs_ofs)) {
507                                 ERR("rootfs image is too big");
508                                 return -1;
509                         }
510                 }
511         }
512
513         if (ofname == NULL) {
514                 ERR("no output file specified");
515                 return -1;
516         }
517
518         return 0;
519 }
520
521 static void fill_header(char *buf, int len)
522 {
523         struct fw_header *hdr = (struct fw_header *)buf;
524
525         memset(hdr, 0, sizeof(struct fw_header));
526
527         hdr->version = htonl(HEADER_VERSION_V1);
528         strncpy(hdr->vendor_name, vendor, sizeof(hdr->vendor_name));
529         strncpy(hdr->fw_version, version, sizeof(hdr->fw_version));
530         hdr->hw_id = htonl(hw_id);
531         hdr->hw_rev = htonl(hw_rev);
532
533         if (boot_info.file_size == 0)
534                 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1));
535         else
536                 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
537
538         hdr->kernel_la = htonl(kernel_la);
539         hdr->kernel_ep = htonl(kernel_ep);
540         hdr->fw_length = htonl(layout->fw_max_len);
541         hdr->kernel_ofs = htonl(sizeof(struct fw_header));
542         hdr->kernel_len = htonl(kernel_len);
543         if (!combined) {
544                 hdr->rootfs_ofs = htonl(rootfs_ofs);
545                 hdr->rootfs_len = htonl(rootfs_info.file_size);
546         }
547
548         get_md5(buf, len, hdr->md5sum1);
549 }
550
551 static int pad_jffs2(char *buf, int currlen)
552 {
553         int len;
554         uint32_t pad_mask;
555
556         len = currlen;
557         pad_mask = (64 * 1024);
558         while ((len < layout->fw_max_len) && (pad_mask != 0)) {
559                 uint32_t mask;
560                 int i;
561
562                 for (i = 10; i < 32; i++) {
563                         mask = 1 << i;
564                         if (pad_mask & mask)
565                                 break;
566                 }
567
568                 len = ALIGN(len, mask);
569
570                 for (i = 10; i < 32; i++) {
571                         mask = 1 << i;
572                         if ((len & (mask - 1)) == 0)
573                                 pad_mask &= ~mask;
574                 }
575
576                 for (i = 0; i < sizeof(jffs2_eof_mark); i++)
577                         buf[len + i] = jffs2_eof_mark[i];
578
579                 len += sizeof(jffs2_eof_mark);
580         }
581
582         return len;
583 }
584
585 static int write_fw(char *data, int len)
586 {
587         FILE *f;
588         int ret = EXIT_FAILURE;
589
590         f = fopen(ofname, "w");
591         if (f == NULL) {
592                 ERRS("could not open \"%s\" for writing", ofname);
593                 goto out;
594         }
595
596         errno = 0;
597         fwrite(data, len, 1, f);
598         if (errno) {
599                 ERRS("unable to write output file");
600                 goto out_flush;
601         }
602
603         DBG("firmware file \"%s\" completed", ofname);
604
605         ret = EXIT_SUCCESS;
606
607  out_flush:
608         fflush(f);
609         fclose(f);
610         if (ret != EXIT_SUCCESS) {
611                 unlink(ofname);
612         }
613  out:
614         return ret;
615 }
616
617 static int build_fw(void)
618 {
619         int buflen;
620         char *buf;
621         char *p;
622         int ret = EXIT_FAILURE;
623         int writelen = 0;
624
625         buflen = layout->fw_max_len;
626
627         buf = malloc(buflen);
628         if (!buf) {
629                 ERR("no memory for buffer\n");
630                 goto out;
631         }
632
633         memset(buf, 0xff, buflen);
634         p = buf + sizeof(struct fw_header);
635         ret = read_to_buf(&kernel_info, p);
636         if (ret)
637                 goto out_free_buf;
638
639         writelen = sizeof(struct fw_header) + kernel_len;
640
641         if (!combined) {
642                 if (rootfs_align)
643                         p = buf + writelen;
644                 else
645                         p = buf + rootfs_ofs;
646
647                 ret = read_to_buf(&rootfs_info, p);
648                 if (ret)
649                         goto out_free_buf;
650
651                 if (rootfs_align)
652                         writelen += rootfs_info.file_size;
653                 else
654                         writelen = rootfs_ofs + rootfs_info.file_size;
655
656                 if (add_jffs2_eof)
657                         writelen = pad_jffs2(buf, writelen);
658         }
659
660         if (!strip_padding)
661                 writelen = buflen;
662
663         fill_header(buf, writelen);
664         ret = write_fw(buf, writelen);
665         if (ret)
666                 goto out_free_buf;
667
668         ret = EXIT_SUCCESS;
669
670  out_free_buf:
671         free(buf);
672  out:
673         return ret;
674 }
675
676 /* Helper functions to inspect_fw() representing different output formats */
677 static inline void inspect_fw_pstr(char *label, char *str)
678 {
679         printf("%-23s: %s\n", label, str);
680 }
681
682 static inline void inspect_fw_phex(char *label, uint32_t val)
683 {
684         printf("%-23s: 0x%08x\n", label, val);
685 }
686
687 static inline void inspect_fw_phexpost(char *label,
688                                        uint32_t val, char *post)
689 {
690         printf("%-23s: 0x%08x (%s)\n", label, val, post);
691 }
692
693 static inline void inspect_fw_phexdef(char *label,
694                                       uint32_t val, uint32_t defval)
695 {
696         printf("%-23s: 0x%08x                  ", label, val);
697
698         if (val == defval)
699                 printf("(== OpenWrt default)\n");
700         else
701                 printf("(OpenWrt default: 0x%08x)\n", defval);
702 }
703
704 static inline void inspect_fw_phexexp(char *label,
705                                       uint32_t val, uint32_t expval)
706 {
707         printf("%-23s: 0x%08x ", label, val);
708
709         if (val == expval)
710                 printf("(ok)\n");
711         else
712                 printf("(expected: 0x%08x)\n", expval);
713 }
714
715 static inline void inspect_fw_phexdec(char *label, uint32_t val)
716 {
717         printf("%-23s: 0x%08x / %8u bytes\n", label, val, val);
718 }
719
720 static inline void inspect_fw_phexdecdef(char *label,
721                                          uint32_t val, uint32_t defval)
722 {
723         printf("%-23s: 0x%08x / %8u bytes ", label, val, val);
724
725         if (val == defval)
726                 printf("(== OpenWrt default)\n");
727         else
728                 printf("(OpenWrt default: 0x%08x)\n", defval);
729 }
730
731 static inline void inspect_fw_pmd5sum(char *label, uint8_t *val, char *text)
732 {
733         int i;
734
735         printf("%-23s:", label);
736         for (i=0; i<MD5SUM_LEN; i++)
737                 printf(" %02x", val[i]);
738         printf(" %s\n", text);
739 }
740
741 static int inspect_fw(void)
742 {
743         char *buf;
744         struct fw_header *hdr;
745         uint8_t md5sum[MD5SUM_LEN];
746         struct board_info *board;
747         int ret = EXIT_FAILURE;
748
749         buf = malloc(inspect_info.file_size);
750         if (!buf) {
751                 ERR("no memory for buffer!\n");
752                 goto out;
753         }
754
755         ret = read_to_buf(&inspect_info, buf);
756         if (ret)
757                 goto out_free_buf;
758         hdr = (struct fw_header *)buf;
759
760         inspect_fw_pstr("File name", inspect_info.file_name);
761         inspect_fw_phexdec("File size", inspect_info.file_size);
762
763         if (ntohl(hdr->version) != HEADER_VERSION_V1) {
764                 ERR("file does not seem to have V1 header!\n");
765                 goto out_free_buf;
766         }
767
768         inspect_fw_phexdec("Version 1 Header size", sizeof(struct fw_header));
769
770         if (ntohl(hdr->unk1) != 0)
771                 inspect_fw_phexdec("Unknown value 1", hdr->unk1);
772
773         memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
774         if (ntohl(hdr->boot_len) == 0)
775                 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
776         else
777                 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
778         get_md5(buf, inspect_info.file_size, hdr->md5sum1);
779
780         if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
781                 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)");
782                 inspect_fw_pmd5sum("          --> expected", hdr->md5sum1, "");
783         } else {
784                 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)");
785         }
786         if (ntohl(hdr->unk2) != 0)
787                 inspect_fw_phexdec("Unknown value 2", hdr->unk2);
788         inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2,
789                            "(purpose yet unknown, unchecked here)");
790         if (ntohl(hdr->unk3) != 0)
791                 inspect_fw_phexdec("Unknown value 3", hdr->unk3);
792
793         printf("\n");
794
795         inspect_fw_pstr("Vendor name", hdr->vendor_name);
796         inspect_fw_pstr("Firmware version", hdr->fw_version);
797         board = find_board_by_hwid(ntohl(hdr->hw_id));
798         if (board) {
799                 layout = find_layout(board->layout_id);
800                 inspect_fw_phexpost("Hardware ID",
801                                     ntohl(hdr->hw_id), board->id);
802                 inspect_fw_phexexp("Hardware Revision",
803                                    ntohl(hdr->hw_rev), board->hw_rev);
804         } else {
805                 inspect_fw_phexpost("Hardware ID",
806                                     ntohl(hdr->hw_id), "unknown");
807                 inspect_fw_phex("Hardware Revision",
808                                 ntohl(hdr->hw_rev));
809         }
810
811         printf("\n");
812
813         inspect_fw_phexdec("Kernel data offset",
814                            ntohl(hdr->kernel_ofs));
815         inspect_fw_phexdec("Kernel data length",
816                            ntohl(hdr->kernel_len));
817         if (board) {
818                 inspect_fw_phexdef("Kernel load address",
819                                    ntohl(hdr->kernel_la),
820                                    layout ? layout->kernel_la : 0xffffffff);
821                 inspect_fw_phexdef("Kernel entry point",
822                                    ntohl(hdr->kernel_ep),
823                                    layout ? layout->kernel_ep : 0xffffffff);
824                 inspect_fw_phexdecdef("Rootfs data offset",
825                                       ntohl(hdr->rootfs_ofs),
826                                       layout ? layout->rootfs_ofs : 0xffffffff);
827         } else {
828                 inspect_fw_phex("Kernel load address",
829                                 ntohl(hdr->kernel_la));
830                 inspect_fw_phex("Kernel entry point",
831                                 ntohl(hdr->kernel_ep));
832                 inspect_fw_phexdec("Rootfs data offset",
833                                    ntohl(hdr->rootfs_ofs));
834         }
835         inspect_fw_phexdec("Rootfs data length",
836                            ntohl(hdr->rootfs_len));
837         inspect_fw_phexdec("Boot loader data offset",
838                            ntohl(hdr->boot_ofs));
839         inspect_fw_phexdec("Boot loader data length",
840                            ntohl(hdr->boot_len));
841         inspect_fw_phexdec("Total firmware length",
842                            ntohl(hdr->fw_length));
843
844         if (extract) {
845                 FILE *fp;
846                 char *filename;
847
848                 printf("\n");
849
850                 filename = malloc(strlen(inspect_info.file_name) + 8);
851                 sprintf(filename, "%s-kernel", inspect_info.file_name);
852                 printf("Extracting kernel to \"%s\"...\n", filename);
853                 fp = fopen(filename, "w");
854                 if (fp) {
855                         if (!fwrite(buf + ntohl(hdr->kernel_ofs),
856                                     ntohl(hdr->kernel_len), 1, fp)) {
857                                 ERR("error in fwrite(): %s", strerror(errno));
858                         }
859                         fclose(fp);
860                 } else {
861                         ERR("error in fopen(): %s", strerror(errno));
862                 }
863                 free(filename);
864
865                 filename = malloc(strlen(inspect_info.file_name) + 8);
866                 sprintf(filename, "%s-rootfs", inspect_info.file_name);
867                 printf("Extracting rootfs to \"%s\"...\n", filename);
868                 fp = fopen(filename, "w");
869                 if (fp) {
870                         if (!fwrite(buf + ntohl(hdr->rootfs_ofs),
871                                     ntohl(hdr->rootfs_len), 1, fp)) {
872                                 ERR("error in fwrite(): %s", strerror(errno));
873                         }
874                         fclose(fp);
875                 } else {
876                         ERR("error in fopen(): %s", strerror(errno));
877                 }
878                 free(filename);
879         }
880
881  out_free_buf:
882         free(buf);
883  out:
884         return ret;
885 }
886
887 int main(int argc, char *argv[])
888 {
889         int ret = EXIT_FAILURE;
890         int err;
891
892         FILE *outfile;
893
894         progname = basename(argv[0]);
895
896         while ( 1 ) {
897                 int c;
898
899                 c = getopt(argc, argv, "a:B:H:E:F:L:V:N:W:ci:k:r:R:o:xhsj");
900                 if (c == -1)
901                         break;
902
903                 switch (c) {
904                 case 'a':
905                         sscanf(optarg, "0x%x", &rootfs_align);
906                         break;
907                 case 'B':
908                         board_id = optarg;
909                         break;
910                 case 'H':
911                         opt_hw_id = optarg;
912                         break;
913                 case 'E':
914                         sscanf(optarg, "0x%x", &kernel_ep);
915                         break;
916                 case 'F':
917                         layout_id = optarg;
918                         break;
919                 case 'W':
920                         opt_hw_rev = optarg;
921                         break;
922                 case 'L':
923                         sscanf(optarg, "0x%x", &kernel_la);
924                         break;
925                 case 'V':
926                         version = optarg;
927                         break;
928                 case 'N':
929                         vendor = optarg;
930                         break;
931                 case 'c':
932                         combined++;
933                         break;
934                 case 'k':
935                         kernel_info.file_name = optarg;
936                         break;
937                 case 'r':
938                         rootfs_info.file_name = optarg;
939                         break;
940                 case 'R':
941                         sscanf(optarg, "0x%x", &rootfs_ofs);
942                         break;
943                 case 'o':
944                         ofname = optarg;
945                         break;
946                 case 's':
947                         strip_padding = 1;
948                         break;
949                 case 'i':
950                         inspect_info.file_name = optarg;
951                         break;
952                 case 'j':
953                         add_jffs2_eof = 1;
954                         break;
955                 case 'x':
956                         extract = 1;
957                         break;
958                 case 'h':
959                         usage(EXIT_SUCCESS);
960                         break;
961                 default:
962                         usage(EXIT_FAILURE);
963                         break;
964                 }
965         }
966
967         ret = check_options();
968         if (ret)
969                 goto out;
970
971         if (!inspect_info.file_name)
972                 ret = build_fw();
973         else
974                 ret = inspect_fw();
975
976  out:
977         return ret;
978 }
979