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