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