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