From 58e27b9fa1e9ea058eb5161f140e0909c4821c92 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 15 Jun 2013 22:13:08 +0200 Subject: mkimxboot: refactor code, no functional change The mkimxboot was becoming messy with juge functions and redundant code. Factor reading/writing/loading code into smaller functions. Introduce the concept of rockbox firmware to simplify support of other formats than scramble. Change-Id: I3a61295ca5abca1a0eee8c9e8709c6b8dfa256a6 --- rbutil/mkimxboot/mkimxboot.c | 471 +++++++++++++++++++++++-------------------- rbutil/mkimxboot/mkimxboot.h | 3 +- 2 files changed, 252 insertions(+), 222 deletions(-) diff --git a/rbutil/mkimxboot/mkimxboot.c b/rbutil/mkimxboot/mkimxboot.c index d420afce5d..da1e49876d 100644 --- a/rbutil/mkimxboot/mkimxboot.c +++ b/rbutil/mkimxboot/mkimxboot.c @@ -28,6 +28,14 @@ #include "dualboot.h" #include "md5.h" +/* abstract structure to represent a Rockbox firmware. It can be a scrambled file + * or an ELF file or whatever. */ +struct rb_fw_t +{ + void *boot; + size_t boot_sz; +}; + struct imx_fw_variant_desc_t { /* Offset within file */ @@ -160,7 +168,7 @@ static const struct imx_model_desc_t imx_models[] = #define MAGIC_NORMAL 0xcafebabe static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, - enum imx_output_type_t type, struct sb_file_t *sb_file, void *boot, size_t boot_sz) + enum imx_output_type_t type, struct sb_file_t *sb_file, struct rb_fw_t boot_fw) { /* We assume the file has three boot sections: ____, host, play and one * resource section rsrc. @@ -229,8 +237,8 @@ static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, rock_sec.insts = xmalloc(2 * sizeof(struct sb_inst_t)); memset(rock_sec.insts, 0, 2 * sizeof(struct sb_inst_t)); rock_sec.insts[0].inst = SB_INST_LOAD; - rock_sec.insts[0].size = boot_sz; - rock_sec.insts[0].data = memdup(boot, boot_sz); + rock_sec.insts[0].size = boot_fw.boot_sz; + rock_sec.insts[0].data = memdup(boot_fw.boot, boot_fw.boot_sz); rock_sec.insts[0].addr = imx_models[model].bootloader_addr; rock_sec.insts[1].inst = SB_INST_JUMP; rock_sec.insts[1].addr = imx_models[model].bootloader_addr; @@ -253,13 +261,13 @@ static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, sb_free_instruction(sec->insts[i]); memset(new_insts + jump_idx, 0, 2 * sizeof(struct sb_inst_t)); new_insts[jump_idx + 0].inst = SB_INST_LOAD; - new_insts[jump_idx + 0].size = boot_sz; - new_insts[jump_idx + 0].data = memdup(boot, boot_sz); + new_insts[jump_idx + 0].size = boot_fw.boot_sz; + new_insts[jump_idx + 0].data = memdup(boot_fw.boot, boot_fw.boot_sz); new_insts[jump_idx + 0].addr = imx_models[model].bootloader_addr; new_insts[jump_idx + 1].inst = SB_INST_JUMP; new_insts[jump_idx + 1].addr = imx_models[model].bootloader_addr; new_insts[jump_idx + 1].argument = recovery ? MAGIC_RECOVERY : MAGIC_NORMAL; - + free(sec->insts); sec->insts = new_insts; sec->nr_insts = jump_idx + 2; @@ -327,7 +335,7 @@ static enum imx_error_t parse_version(const char *s, struct sb_version_t *ver) static enum imx_error_t patch_firmware(enum imx_model_t model, enum imx_firmware_variant_t variant, enum imx_output_type_t type, - struct sb_file_t *sb_file, void *boot, size_t boot_sz, + struct sb_file_t *sb_file, struct rb_fw_t boot_fw, const char *force_version) { if(force_version) @@ -342,11 +350,11 @@ static enum imx_error_t patch_firmware(enum imx_model_t model, case MODEL_FUZEPLUS: /* The Fuze+ uses the standard ____, host, play sections, patch after third * call in ____ section */ - return patch_std_zero_host_play(3, model, type, sb_file, boot, boot_sz); + return patch_std_zero_host_play(3, model, type, sb_file, boot_fw); case MODEL_ZENXFI3: /* The ZEN X-Fi3 uses the standard ____, hSst, pSay sections, patch after third * call in ____ section. Although sections names use the S variant, they are standard. */ - return patch_std_zero_host_play(3, model, type, sb_file, boot, boot_sz); + return patch_std_zero_host_play(3, model, type, sb_file, boot_fw); case MODEL_ZENXFI2: /* The ZEN X-Fi2 has two types of firmware: recovery and normal. * Normal uses the standard ___, host, play sections and recovery only ____ */ @@ -355,7 +363,7 @@ static enum imx_error_t patch_firmware(enum imx_model_t model, case VARIANT_ZENXFI2_RECOVERY: case VARIANT_ZENXFI2_NAND: case VARIANT_ZENXFI2_SD: - return patch_std_zero_host_play(1, model, type, sb_file, boot, boot_sz); + return patch_std_zero_host_play(1, model, type, sb_file, boot_fw); default: return IMX_DONT_KNOW_HOW_TO_PATCH; } @@ -413,165 +421,240 @@ void dump_imx_dev_info(const char *prefix) } } -enum imx_error_t mkimxboot(const char *infile, const char *bootfile, - const char *outfile, struct imx_option_t opt) +/* find an entry into imx_sums which matches the MD5 sum of a file */ +static enum imx_error_t find_model_by_md5sum(uint8_t file_md5sum[16], int *md5_idx) { - /* Dump tables */ - if(opt.fw_variant > VARIANT_COUNT) { - return IMX_ERROR; - } - dump_imx_dev_info("[INFO] "); - /* compute MD5 sum of the file */ - uint8_t file_md5sum[16]; - do + int i = 0; + while(i < NR_IMX_SUMS) { - FILE *f = fopen(infile, "rb"); - if(f == NULL) + uint8_t md5[20]; + if(strlen(imx_sums[i].md5sum) != 32) { - printf("[ERR] Cannot open input file\n"); - return IMX_OPEN_ERROR; + printf("[INFO] Invalid MD5 sum in imx_sums\n"); + return IMX_ERROR; } - fseek(f, 0, SEEK_END); - size_t sz = ftell(f); - fseek(f, 0, SEEK_SET); - void *buf = xmalloc(sz); - if(fread(buf, sz, 1, f) != 1) + for(int j = 0; j < 16; j++) { - fclose(f); - free(buf); - printf("[ERR] Cannot read file\n"); - return IMX_READ_ERROR; + byte a, b; + if(convxdigit(imx_sums[i].md5sum[2 * j], &a) || convxdigit(imx_sums[i].md5sum[2 * j + 1], &b)) + { + printf("[ERR][INTERNAL] Bad checksum format: %s\n", imx_sums[i].md5sum); + return IMX_ERROR; + } + md5[j] = (a << 4) | b; } + if(memcmp(file_md5sum, md5, 16) == 0) + break; + i++; + } + if(i == NR_IMX_SUMS) + { + printf("[ERR] MD5 sum doesn't match any known file\n"); + return IMX_NO_MATCH; + } + *md5_idx = i; + return IMX_SUCCESS; +} + +/* read a file to a buffer */ +static enum imx_error_t read_file(const char *file, void **buffer, size_t *size) +{ + FILE *f = fopen(file, "rb"); + if(f == NULL) + { + printf("[ERR] Cannot open file '%s' for reading: %m\n", file); + return IMX_OPEN_ERROR; + } + fseek(f, 0, SEEK_END); + *size = ftell(f); + fseek(f, 0, SEEK_SET); + *buffer = xmalloc(*size); + if(fread(*buffer, *size, 1, f) != 1) + { + free(*buffer); + fclose(f); + printf("[ERR] Cannot read file '%s': %m\n", file); + return IMX_READ_ERROR; + } + fclose(f); + return IMX_SUCCESS; +} + +/* write a file from a buffer */ +static enum imx_error_t write_file(const char *file, void *buffer, size_t size) +{ + FILE *f = fopen(file, "wb"); + if(f == NULL) + { + printf("[ERR] Cannot open file '%s' for writing: %m\n", file); + return IMX_OPEN_ERROR; + } + if(fwrite(buffer, size, 1, f) != 1) + { + fclose(f); + printf("[ERR] Cannot write file '%s': %m\n", file); + return IMX_WRITE_ERROR; + } + fclose(f); + return IMX_SUCCESS; +} + +/* compute MD5 sum of a buffer */ +static enum imx_error_t compute_md5sum_buf(void *buf, size_t sz, uint8_t file_md5sum[16]) +{ + md5_context ctx; + md5_starts(&ctx); + md5_update(&ctx, buf, sz); + md5_finish(&ctx, file_md5sum); + return IMX_SUCCESS; +} + +/* compute MD5 of a file */ +static enum imx_error_t compute_md5sum(const char *file, uint8_t file_md5sum[16]) +{ + FILE *f = fopen(file, "rb"); + if(f == NULL) + { + printf("[ERR] Cannot open input file\n"); + return IMX_OPEN_ERROR; + } + fseek(f, 0, SEEK_END); + size_t sz = ftell(f); + fseek(f, 0, SEEK_SET); + void *buf = xmalloc(sz); + if(fread(buf, sz, 1, f) != 1) + { fclose(f); - md5_context ctx; - md5_starts(&ctx); - md5_update(&ctx, buf, sz); - md5_finish(&ctx, file_md5sum); free(buf); - }while(0); + printf("[ERR] Cannot read file\n"); + return IMX_READ_ERROR; + } + fclose(f); + compute_md5sum_buf(buf, sz, file_md5sum); + free(buf); + return IMX_SUCCESS; +} + +static enum imx_error_t load_sb_file(const char *file, int md5_idx, + struct imx_option_t opt, struct sb_file_t **sb_file) +{ + if(imx_sums[md5_idx].fw_variants[opt.fw_variant].size == 0) + { + printf("[ERR] Input file does not contain variant '%s'\n", imx_fw_variant[opt.fw_variant]); + return IMX_VARIANT_MISMATCH; + } + enum imx_model_t model = imx_sums[md5_idx].model; + enum sb_error_t err; + g_debug = opt.debug; + clear_keys(); + add_keys(imx_models[model].keys, imx_models[model].nr_keys); + *sb_file = sb_read_file_ex(file, imx_sums[md5_idx].fw_variants[opt.fw_variant].offset, + imx_sums[md5_idx].fw_variants[opt.fw_variant].size, false, NULL, &imx_printf, &err); + if(*sb_file == NULL) + { + clear_keys(); + return IMX_FIRST_SB_ERROR + err; + } + return IMX_SUCCESS; +} + +/* Load a rockbox firwmare from a buffer. Data is copied. */ +static enum imx_error_t rb_fw_load_buf(struct rb_fw_t *fw, uint8_t *buf, size_t sz, + enum imx_model_t model) +{ + if(sz < 8) + { + printf("[ERR] Bootloader file is too small to be valid\n"); + return IMX_BOOT_INVALID; + } + /* check model name */ + uint8_t *name = buf + 4; + if(memcmp(name, imx_models[model].rb_model_name, 4) != 0) + { + printf("[ERR] Bootloader model doesn't match found model for input file\n"); + return IMX_BOOT_MISMATCH; + } + /* check checksum */ + uint32_t sum = imx_models[model].rb_model_num; + for(int i = 8; i < sz; i++) + sum += buf[i]; + if(sum != get_uint32be(buf)) + { + printf("[ERR] Bootloader checksum mismatch\n"); + return IMX_BOOT_CHECKSUM_ERROR; + } + fw->boot = memdup(buf + 8, sz - 8); + fw->boot_sz = sz - 8; + return IMX_SUCCESS; +} + +/* load a rockbox firmware from a file. */ +static enum imx_error_t rb_fw_load(struct rb_fw_t *fw, const char *file, + enum imx_model_t model) +{ + void *buf; + size_t sz; + int ret = read_file(file, &buf, &sz); + if(ret == IMX_SUCCESS) + { + ret = rb_fw_load_buf(fw, buf, sz, model); + free(buf); + } + return ret; +} + +/* free rockbox firmware */ +static void rb_fw_free(struct rb_fw_t *fw) +{ + free(fw->boot); + fw->boot = NULL; + fw->boot_sz = 0; +} + +enum imx_error_t mkimxboot(const char *infile, const char *bootfile, + const char *outfile, struct imx_option_t opt) +{ + /* sanity check */ + if(opt.fw_variant > VARIANT_COUNT) + return IMX_ERROR; + /* Dump tables */ + dump_imx_dev_info("[INFO] "); + /* compute MD5 sum of the file */ + uint8_t file_md5sum[16]; + enum imx_error_t ret = compute_md5sum(infile, file_md5sum); + if(ret != IMX_SUCCESS) + return ret; printf("[INFO] MD5 sum of the file: "); print_hex(file_md5sum, 16, true); /* find model */ - enum imx_model_t model; int md5_idx; - do - { - int i = 0; - while(i < NR_IMX_SUMS) - { - uint8_t md5[20]; - if(strlen(imx_sums[i].md5sum) != 32) - { - printf("[INFO] Invalid MD5 sum in imx_sums\n"); - return IMX_ERROR; - } - for(int j = 0; j < 16; j++) - { - byte a, b; - if(convxdigit(imx_sums[i].md5sum[2 * j], &a) || convxdigit(imx_sums[i].md5sum[2 * j + 1], &b)) - { - printf("[ERR][INTERNAL] Bad checksum format: %s\n", imx_sums[i].md5sum); - return IMX_ERROR; - } - md5[j] = (a << 4) | b; - } - if(memcmp(file_md5sum, md5, 16) == 0) - break; - i++; - } - if(i == NR_IMX_SUMS) - { - printf("[ERR] MD5 sum doesn't match any known file\n"); - return IMX_NO_MATCH; - } - model = imx_sums[i].model; - md5_idx = i; - }while(0); + ret = find_model_by_md5sum(file_md5sum, &md5_idx); + if(ret != IMX_SUCCESS) + return ret; + enum imx_model_t model = imx_sums[md5_idx].model; printf("[INFO] File is for model %d (%s, version %s)\n", model, imx_models[model].model_name, imx_sums[md5_idx].version); /* load rockbox file */ - uint8_t *boot; - size_t boot_size; - do - { - FILE *f = fopen(bootfile, "rb"); - if(f == NULL) - { - printf("[ERR] Cannot open boot file\n"); - return IMX_OPEN_ERROR; - } - fseek(f, 0, SEEK_END); - boot_size = ftell(f); - fseek(f, 0, SEEK_SET); - boot = xmalloc(boot_size); - if(fread(boot, boot_size, 1, f) != 1) - { - free(boot); - fclose(f); - printf("[ERR] Cannot read boot file\n"); - return IMX_READ_ERROR; - } - fclose(f); - }while(0); - /* Check boot file */ - do - { - if(boot_size < 8) - { - printf("[ERR] Bootloader file is too small to be valid\n"); - free(boot); - return IMX_BOOT_INVALID; - } - /* check model name */ - uint8_t *name = boot + 4; - if(memcmp(name, imx_models[model].rb_model_name, 4) != 0) - { - printf("[ERR] Bootloader model doesn't match found model for input file\n"); - free(boot); - return IMX_BOOT_MISMATCH; - } - /* check checksum */ - uint32_t sum = imx_models[model].rb_model_num; - for(int i = 8; i < boot_size; i++) - sum += boot[i]; - if(sum != get_uint32be(boot)) - { - printf("[ERR] Bootloader checksum mismatch\n"); - free(boot); - return IMX_BOOT_CHECKSUM_ERROR; - } - }while(0); + struct rb_fw_t boot_fw; + ret = rb_fw_load(&boot_fw, bootfile, model); + if(ret != IMX_SUCCESS) + return ret; /* load OF file */ struct sb_file_t *sb_file; - do + ret = load_sb_file(infile, md5_idx, opt, &sb_file); + if(ret != IMX_SUCCESS) { - if(imx_sums[md5_idx].fw_variants[opt.fw_variant].size == 0) - { - printf("[ERR] Input file does not contain variant '%s'\n", imx_fw_variant[opt.fw_variant]); - free(boot); - return IMX_VARIANT_MISMATCH; - } - enum sb_error_t err; - g_debug = opt.debug; - clear_keys(); - add_keys(imx_models[model].keys, imx_models[model].nr_keys); - sb_file = sb_read_file_ex(infile, imx_sums[md5_idx].fw_variants[opt.fw_variant].offset, - imx_sums[md5_idx].fw_variants[opt.fw_variant].size, false, NULL, &imx_printf, &err); - if(sb_file == NULL) - { - clear_keys(); - free(boot); - return IMX_FIRST_SB_ERROR + err; - } - }while(0); + rb_fw_free(&boot_fw); + return ret; + } /* produce file */ - enum imx_error_t ret = patch_firmware(model, opt.fw_variant, opt.output, - sb_file, boot + 8, boot_size - 8, opt.force_version); + ret = patch_firmware(model, opt.fw_variant, opt.output, + sb_file, boot_fw, opt.force_version); if(ret == IMX_SUCCESS) ret = sb_write_file(sb_file, outfile); clear_keys(); - free(boot); + rb_fw_free(&boot_fw); sb_free(sb_file); return ret; } @@ -579,78 +662,38 @@ enum imx_error_t mkimxboot(const char *infile, const char *bootfile, enum imx_error_t extract_firmware(const char *infile, enum imx_firmware_variant_t fw_variant, const char *outfile) { - /* Dump tables */ - if(fw_variant > VARIANT_COUNT) { + /* sanity check */ + if(fw_variant > VARIANT_COUNT) return IMX_ERROR; - } + /* dump tables */ dump_imx_dev_info("[INFO] "); /* compute MD5 sum of the file */ + void *buf; + size_t sz; uint8_t file_md5sum[16]; - FILE *f = fopen(infile, "rb"); - if(f == NULL) - { - printf("[ERR] Cannot open input file\n"); - return IMX_OPEN_ERROR; - } - fseek(f, 0, SEEK_END); - size_t sz = ftell(f); - fseek(f, 0, SEEK_SET); - void *buf = xmalloc(sz); - if(fread(buf, sz, 1, f) != 1) + int ret = read_file(infile, &buf, &sz); + if(ret != IMX_SUCCESS) + return ret; + ret = compute_md5sum_buf(buf, sz, file_md5sum); + if(ret != IMX_SUCCESS) { - fclose(f); free(buf); - printf("[ERR] Cannot read file\n"); - return IMX_READ_ERROR; + return ret; } - md5_context ctx; - md5_starts(&ctx); - md5_update(&ctx, buf, sz); - md5_finish(&ctx, file_md5sum); - fclose(f); - printf("[INFO] MD5 sum of the file: "); print_hex(file_md5sum, 16, true); /* find model */ - enum imx_model_t model; int md5_idx; - do + ret = find_model_by_md5sum(file_md5sum, &md5_idx); + if(ret != IMX_SUCCESS) { - int i = 0; - while(i < NR_IMX_SUMS) - { - uint8_t md5[20]; - if(strlen(imx_sums[i].md5sum) != 32) - { - printf("[INFO] Invalid MD5 sum in imx_sums\n"); - return IMX_ERROR; - } - for(int j = 0; j < 16; j++) - { - byte a, b; - if(convxdigit(imx_sums[i].md5sum[2 * j], &a) || convxdigit(imx_sums[i].md5sum[2 * j + 1], &b)) - { - printf("[ERR][INTERNAL] Bad checksum format: %s\n", imx_sums[i].md5sum); - free(buf); - return IMX_ERROR; - } - md5[j] = (a << 4) | b; - } - if(memcmp(file_md5sum, md5, 16) == 0) - break; - i++; - } - if(i == NR_IMX_SUMS) - { - printf("[ERR] MD5 sum doesn't match any known file\n"); - return IMX_NO_MATCH; - } - model = imx_sums[i].model; - md5_idx = i; - }while(0); + free(buf); + return ret; + } + enum imx_model_t model = imx_sums[md5_idx].model; printf("[INFO] File is for model %d (%s, version %s)\n", model, imx_models[model].model_name, imx_sums[md5_idx].version); - + /* extract firmware */ if(imx_sums[md5_idx].fw_variants[fw_variant].size == 0) { printf("[ERR] Input file does not contain variant '%s'\n", imx_fw_variant[fw_variant]); @@ -658,23 +701,9 @@ enum imx_error_t extract_firmware(const char *infile, return IMX_VARIANT_MISMATCH; } - f = fopen(outfile, "wb"); - if(f == NULL) - { - printf("[ERR] Cannot open input file\n"); - free(buf); - return IMX_OPEN_ERROR; - } - enum imx_error_t ret = IMX_SUCCESS; - - if(fwrite(buf + imx_sums[md5_idx].fw_variants[fw_variant].offset, - imx_sums[md5_idx].fw_variants[fw_variant].size, 1, f) != 1) - { - printf("[ERR] Cannot write file\n"); - ret = IMX_ERROR; - } - fclose(f); + ret = write_file(outfile, + buf + imx_sums[md5_idx].fw_variants[fw_variant].offset, + imx_sums[md5_idx].fw_variants[fw_variant].size); free(buf); - return ret; } diff --git a/rbutil/mkimxboot/mkimxboot.h b/rbutil/mkimxboot/mkimxboot.h index 748742f523..de564dc5d4 100644 --- a/rbutil/mkimxboot/mkimxboot.h +++ b/rbutil/mkimxboot/mkimxboot.h @@ -41,7 +41,8 @@ enum imx_error_t IMX_BOOT_CHECKSUM_ERROR = -7, IMX_DONT_KNOW_HOW_TO_PATCH = -8, IMX_VARIANT_MISMATCH = -9, - IMX_FIRST_SB_ERROR = -10, + IMX_WRITE_ERROR = -10, + IMX_FIRST_SB_ERROR = -11, }; enum imx_output_type_t -- cgit v1.2.3