X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=src%2Fmktplinkfw.c;h=ed785b48e562a02cb09fa6baf6d6bd2c68692a49;hb=d3633fda3462a734ea20dd3046778baefe7418e8;hp=7127932d71ba89ef620aed29390a6ef794012dee;hpb=46e5f621b91b6cda60ff95fc815ef7882afe423c;p=project%2Ffirmware-utils.git diff --git a/src/mktplinkfw.c b/src/mktplinkfw.c index 7127932..ed785b4 100644 --- a/src/mktplinkfw.c +++ b/src/mktplinkfw.c @@ -19,6 +19,8 @@ #include #include /* for getopt() */ #include +#include +#include #include #include @@ -26,27 +28,18 @@ #include #include "md5.h" - -#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) -#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#include "mktplinkfw-lib.h" #define HEADER_VERSION_V1 0x01000000 #define HEADER_VERSION_V2 0x02000000 -#define MD5SUM_LEN 16 - -struct file_info { - char *file_name; /* name of the file */ - uint32_t file_size; /* length of the file */ -}; - struct fw_header { uint32_t version; /* header version */ char vendor_name[24]; char fw_version[36]; uint32_t hw_id; /* hardware id */ uint32_t hw_rev; /* hardware revision */ - uint32_t region; /* region code */ + uint32_t region_code; /* region code */ uint8_t md5sum1[MD5SUM_LEN]; uint32_t unk2; uint8_t md5sum2[MD5SUM_LEN]; @@ -63,57 +56,58 @@ struct fw_header { uint16_t ver_hi; uint16_t ver_mid; uint16_t ver_lo; - uint8_t pad[354]; + uint8_t pad[130]; + char region_str1[32]; + char region_str2[32]; + uint8_t pad2[160]; } __attribute__ ((packed)); -struct flash_layout { - char *id; - uint32_t fw_max_len; - uint32_t kernel_la; - uint32_t kernel_ep; - uint32_t rootfs_ofs; +struct fw_region { + char name[4]; + uint32_t code; }; + /* * Globals */ -static char *ofname; -static char *progname; +char *ofname; +char *progname; static char *vendor = "TP-LINK Technologies"; static char *version = "ver. 1.0"; static char *fw_ver = "0.0.0"; static uint32_t hdr_ver = HEADER_VERSION_V1; static char *layout_id; -static struct flash_layout *layout; +struct flash_layout *layout; static char *opt_hw_id; static uint32_t hw_id; static char *opt_hw_rev; static uint32_t hw_rev; static uint32_t opt_hdr_ver = 1; static char *country; -static uint32_t region; +static const struct fw_region *region; static int fw_ver_lo; static int fw_ver_mid; static int fw_ver_hi; -static struct file_info kernel_info; +struct file_info kernel_info; static uint32_t kernel_la = 0; static uint32_t kernel_ep = 0; -static uint32_t kernel_len = 0; -static struct file_info rootfs_info; -static uint32_t rootfs_ofs = 0; -static uint32_t rootfs_align; +uint32_t kernel_len = 0; +struct file_info rootfs_info; +uint32_t rootfs_ofs = 0; +uint32_t rootfs_align; static struct file_info boot_info; -static int combined; -static int strip_padding; -static int ignore_size; -static int add_jffs2_eof; -static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde}; +int combined; +int strip_padding; +int add_jffs2_eof; static uint32_t fw_max_len; static uint32_t reserved_space; static struct file_info inspect_info; static int extract = 0; +static bool endian_swap = false; +static bool rootfs_ofs_calc = false; static const char md5salt_normal[MD5SUM_LEN] = { 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb, @@ -150,6 +144,12 @@ static struct flash_layout layouts[] = { .kernel_la = 0x80060000, .kernel_ep = 0x80060000, .rootfs_ofs = 0x100000, + }, { + .id = "8Mmtk", + .fw_max_len = 0x7c0000, + .kernel_la = 0x80000000, + .kernel_ep = 0x8000c310, + .rootfs_ofs = 0x100000, }, { .id = "16M", .fw_max_len = 0xf80000, @@ -173,63 +173,22 @@ static struct flash_layout layouts[] = { } }; -static const char *const regions[] = { - "UN", /* universal */ - "US", +static const struct fw_region regions[] = { + /* Default region (universal) uses code 0 as well */ + {"US", 1}, + {"EU", 0}, + {"BR", 0}, }; -/* - * Message macros - */ -#define ERR(fmt, ...) do { \ - fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ - progname, ## __VA_ARGS__ ); \ -} while (0) - -#define ERRS(fmt, ...) do { \ - int save = errno; \ - fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ - progname, ## __VA_ARGS__, strerror(save)); \ -} while (0) - -#define DBG(fmt, ...) do { \ - fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ -} while (0) - -static struct flash_layout *find_layout(const char *id) -{ - struct flash_layout *ret; - struct flash_layout *l; - - ret = NULL; - for (l = layouts; l->id != NULL; l++){ - if (strcasecmp(id, l->id) == 0) { - ret = l; - break; - } - }; - - return ret; -} - -static uint32_t find_region(const char *country) { - uint32_t i; +static const struct fw_region * find_region(const char *country) { + size_t i; for (i = 0; i < ARRAY_SIZE(regions); i++) { - if (strcasecmp(regions[i], country) == 0) - return i; + if (strcasecmp(regions[i].name, country) == 0) + return ®ions[i]; } - return -1; -} - -static const char * get_region_country(uint32_t region) { - if (region < ARRAY_SIZE(regions)) - return regions[region]; - else - return "unknown"; + return NULL; } static void usage(int status) @@ -239,6 +198,7 @@ static void usage(int status) "\n" "Options:\n" " -c use combined kernel image\n" +" -e swap endianness in kernel load address and entry point\n" " -E overwrite kernel entry point with (hexval prefixed with 0x)\n" " -L overwrite kernel load address with (hexval prefixed with 0x)\n" " -H use hardware id specified with \n" @@ -249,9 +209,9 @@ static void usage(int status) " -r read rootfs image from the file \n" " -a align the rootfs start on an bytes boundary\n" " -R overwrite rootfs offset with (hexval prefixed with 0x)\n" +" -O calculate rootfs offset for combined images\n" " -o write output to the file \n" " -s strip padding from the end of the image\n" -" -S ignore firmware size limit (only for combined images)\n" " -j add jffs2 end-of-filesystem markers\n" " -N set image vendor to \n" " -V set image version to \n" @@ -266,59 +226,6 @@ static void usage(int status) exit(status); } -static void get_md5(const char *data, int size, uint8_t *md5) -{ - MD5_CTX ctx; - - MD5_Init(&ctx); - MD5_Update(&ctx, data, size); - MD5_Final(md5, &ctx); -} - -static int get_file_stat(struct file_info *fdata) -{ - struct stat st; - int res; - - if (fdata->file_name == NULL) - return 0; - - res = stat(fdata->file_name, &st); - if (res){ - ERRS("stat failed on %s", fdata->file_name); - return res; - } - - fdata->file_size = st.st_size; - return 0; -} - -static int read_to_buf(const struct file_info *fdata, char *buf) -{ - FILE *f; - int ret = EXIT_FAILURE; - - f = fopen(fdata->file_name, "r"); - if (f == NULL) { - ERRS("could not open \"%s\" for reading", fdata->file_name); - goto out; - } - - errno = 0; - fread(buf, fdata->file_size, 1, f); - if (errno != 0) { - ERRS("unable to read from file \"%s\"", fdata->file_name); - goto out_close; - } - - ret = EXIT_SUCCESS; - - out_close: - fclose(f); - out: - return ret; -} - static int check_options(void) { int ret; @@ -341,7 +248,7 @@ static int check_options(void) } hw_id = strtoul(opt_hw_id, NULL, 0); - if (layout_id == NULL) { + if (!combined && layout_id == NULL) { ERR("flash layout is not specified"); return -1; } @@ -353,36 +260,37 @@ static int check_options(void) if (country) { region = find_region(country); - if (region == (uint32_t)-1) { - char *end; - region = strtoul(country, &end, 0); - if (*end) { - ERR("unknown region code \"%s\"", country); - return -1; - } + if (!region) { + ERR("unknown region code \"%s\"", country); + return -1; } } - layout = find_layout(layout_id); - if (layout == NULL) { - ERR("unknown flash layout \"%s\"", layout_id); - return -1; - } + if (combined) { + if (!kernel_la || !kernel_ep) { + ERR("kernel loading address and entry point must be specified for combined image"); + return -1; + } + } else { + layout = find_layout(layouts, layout_id); + if (layout == NULL) { + ERR("unknown flash layout \"%s\"", layout_id); + return -1; + } - if (!kernel_la) - kernel_la = layout->kernel_la; - if (!kernel_ep) - kernel_ep = layout->kernel_ep; - if (!rootfs_ofs) - rootfs_ofs = layout->rootfs_ofs; + if (!kernel_la) + kernel_la = layout->kernel_la; + if (!kernel_ep) + kernel_ep = layout->kernel_ep; + if (!rootfs_ofs) + rootfs_ofs = layout->rootfs_ofs; - if (reserved_space > layout->fw_max_len) { - ERR("reserved space is not valid"); - return -1; + if (reserved_space > layout->fw_max_len) { + ERR("reserved space is not valid"); + return -1; + } } - fw_max_len = layout->fw_max_len - reserved_space; - if (kernel_info.file_name == NULL) { ERR("no kernel image specified"); return -1; @@ -394,18 +302,9 @@ static int check_options(void) kernel_len = kernel_info.file_size; - if (combined) { - exceed_bytes = kernel_info.file_size - (fw_max_len - sizeof(struct fw_header)); - if (exceed_bytes > 0) { - if (!ignore_size) { - ERR("kernel image is too big by %i bytes", exceed_bytes); - return -1; - } - layout->fw_max_len = sizeof(struct fw_header) + - kernel_info.file_size + - reserved_space; - } - } else { + if (!combined) { + fw_max_len = layout->fw_max_len - reserved_space; + if (rootfs_info.file_name == NULL) { ERR("no rootfs image specified"); return -1; @@ -417,10 +316,10 @@ static int check_options(void) if (rootfs_align) { kernel_len += sizeof(struct fw_header); - kernel_len = ALIGN(kernel_len, rootfs_align); + rootfs_ofs = ALIGN(kernel_len, rootfs_align); kernel_len -= sizeof(struct fw_header); - DBG("kernel length aligned to %u", kernel_len); + DBG("rootfs offset aligned to 0x%u", rootfs_ofs); exceed_bytes = kernel_len + rootfs_info.file_size - (fw_max_len - sizeof(struct fw_header)); if (exceed_bytes > 0) { @@ -465,7 +364,7 @@ static int check_options(void) return 0; } -static void fill_header(char *buf, int len) +void fill_header(char *buf, int len) { struct fw_header *hdr = (struct fw_header *)buf; @@ -476,184 +375,50 @@ static void fill_header(char *buf, int len) strncpy(hdr->fw_version, version, sizeof(hdr->fw_version)); hdr->hw_id = htonl(hw_id); hdr->hw_rev = htonl(hw_rev); - hdr->region = htonl(region); - - if (boot_info.file_size == 0) - memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); - else - memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); hdr->kernel_la = htonl(kernel_la); hdr->kernel_ep = htonl(kernel_ep); - hdr->fw_length = htonl(layout->fw_max_len); hdr->kernel_ofs = htonl(sizeof(struct fw_header)); hdr->kernel_len = htonl(kernel_len); + if (!combined) { + if (boot_info.file_size == 0) + memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); + else + memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); + + hdr->fw_length = htonl(layout->fw_max_len); hdr->rootfs_ofs = htonl(rootfs_ofs); hdr->rootfs_len = htonl(rootfs_info.file_size); } + if (combined && rootfs_ofs_calc) { + hdr->rootfs_ofs = htonl(sizeof(struct fw_header) + kernel_len); + } + hdr->ver_hi = htons(fw_ver_hi); hdr->ver_mid = htons(fw_ver_mid); hdr->ver_lo = htons(fw_ver_lo); - get_md5(buf, len, hdr->md5sum1); -} - -static int pad_jffs2(char *buf, int currlen) -{ - int len; - uint32_t pad_mask; - - len = currlen; - pad_mask = (64 * 1024); - while ((len < layout->fw_max_len) && (pad_mask != 0)) { - uint32_t mask; - int i; - - for (i = 10; i < 32; i++) { - mask = 1 << i; - if (pad_mask & mask) - break; - } - - len = ALIGN(len, mask); - - for (i = 10; i < 32; i++) { - mask = 1 << i; - if ((len & (mask - 1)) == 0) - pad_mask &= ~mask; - } - - for (i = 0; i < sizeof(jffs2_eof_mark); i++) - buf[len + i] = jffs2_eof_mark[i]; - - len += sizeof(jffs2_eof_mark); + if (region) { + hdr->region_code = htonl(region->code); + snprintf( + hdr->region_str1, sizeof(hdr->region_str1), "00000000;%02X%02X%02X%02X;", + region->name[0], region->name[1], region->name[2], region->name[3] + ); + snprintf( + hdr->region_str2, sizeof(hdr->region_str2), "%02X%02X%02X%02X", + region->name[0], region->name[1], region->name[2], region->name[3] + ); } - return len; -} - -static int write_fw(const char *data, int len) -{ - FILE *f; - int ret = EXIT_FAILURE; - - f = fopen(ofname, "w"); - if (f == NULL) { - ERRS("could not open \"%s\" for writing", ofname); - goto out; - } - - errno = 0; - fwrite(data, len, 1, f); - if (errno) { - ERRS("unable to write output file"); - goto out_flush; + if (endian_swap) { + hdr->kernel_la = bswap_32(hdr->kernel_la); + hdr->kernel_ep = bswap_32(hdr->kernel_ep); } - DBG("firmware file \"%s\" completed", ofname); - - ret = EXIT_SUCCESS; - - out_flush: - fflush(f); - fclose(f); - if (ret != EXIT_SUCCESS) { - unlink(ofname); - } - out: - return ret; -} - -static int build_fw(void) -{ - int buflen; - char *buf; - char *p; - int ret = EXIT_FAILURE; - int writelen = 0; - - buflen = layout->fw_max_len; - - buf = malloc(buflen); - if (!buf) { - ERR("no memory for buffer\n"); - goto out; - } - - memset(buf, 0xff, buflen); - p = buf + sizeof(struct fw_header); - ret = read_to_buf(&kernel_info, p); - if (ret) - goto out_free_buf; - - writelen = sizeof(struct fw_header) + kernel_len; - - if (!combined) { - if (rootfs_align) - p = buf + writelen; - else - p = buf + rootfs_ofs; - - ret = read_to_buf(&rootfs_info, p); - if (ret) - goto out_free_buf; - - if (rootfs_align) - writelen += rootfs_info.file_size; - else - writelen = rootfs_ofs + rootfs_info.file_size; - - if (add_jffs2_eof) - writelen = pad_jffs2(buf, writelen); - } - - if (!strip_padding) - writelen = buflen; - - fill_header(buf, writelen); - ret = write_fw(buf, writelen); - if (ret) - goto out_free_buf; - - ret = EXIT_SUCCESS; - - out_free_buf: - free(buf); - out: - return ret; -} - -/* Helper functions to inspect_fw() representing different output formats */ -static inline void inspect_fw_pstr(const char *label, const char *str) -{ - printf("%-23s: %s\n", label, str); -} - -static inline void inspect_fw_phex(const char *label, uint32_t val) -{ - printf("%-23s: 0x%08x\n", label, val); -} - -static inline void inspect_fw_phexpost(const char *label, uint32_t val, const char *post) -{ - printf("%-23s: 0x%08x (%s)\n", label, val, post); -} - -static inline void inspect_fw_phexdec(const char *label, uint32_t val) -{ - printf("%-23s: 0x%08x / %8u bytes\n", label, val, val); -} - -static inline void inspect_fw_pmd5sum(const char *label, const uint8_t *val, const char *text) -{ - int i; - - printf("%-23s:", label); - for (i=0; imd5sum1); } static int inspect_fw(void) @@ -711,7 +476,7 @@ static int inspect_fw(void) inspect_fw_pstr("Firmware version", hdr->fw_version); inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id)); inspect_fw_phex("Hardware Revision", ntohl(hdr->hw_rev)); - inspect_fw_phexpost("Region code", ntohl(hdr->region), get_region_country(ntohl(hdr->region))); + inspect_fw_phex("Region code", ntohl(hdr->region_code)); printf("\n"); @@ -786,7 +551,7 @@ int main(int argc, char *argv[]) while ( 1 ) { int c; - c = getopt(argc, argv, "a:H:E:F:L:m:V:N:W:C:ci:k:r:R:o:xX:hsSjv:"); + c = getopt(argc, argv, "a:H:E:F:L:m:V:N:W:C:ci:k:r:R:o:OxX:ehsjv:"); if (c == -1) break; @@ -839,12 +604,12 @@ int main(int argc, char *argv[]) case 'o': ofname = optarg; break; + case 'O': + rootfs_ofs_calc = 1; + break; case 's': strip_padding = 1; break; - case 'S': - ignore_size = 1; - break; case 'i': inspect_info.file_name = optarg; break; @@ -854,6 +619,9 @@ int main(int argc, char *argv[]) case 'x': extract = 1; break; + case 'e': + endian_swap = true; + break; case 'h': usage(EXIT_SUCCESS); break; @@ -871,7 +639,7 @@ int main(int argc, char *argv[]) goto out; if (!inspect_info.file_name) - ret = build_fw(); + ret = build_fw(sizeof(struct fw_header)); else ret = inspect_fw();