1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Amaury Pouly 2013-06-15 22:13:08 +02:00
parent 6483e23f07
commit 58e27b9fa1
2 changed files with 258 additions and 228 deletions

View file

@ -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,180 +421,98 @@ 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;
}
fclose(f);
md5_context ctx;
md5_starts(&ctx);
md5_update(&ctx, buf, sz);
md5_finish(&ctx, file_md5sum);
free(buf);
}while(0);
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)
byte a, b;
if(convxdigit(imx_sums[i].md5sum[2 * j], &a) || convxdigit(imx_sums[i].md5sum[2 * j + 1], &b))
{
printf("[INFO] Invalid MD5 sum in imx_sums\n");
printf("[ERR][INTERNAL] Bad checksum format: %s\n", imx_sums[i].md5sum);
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++;
md5[j] = (a << 4) | b;
}
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);
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
if(memcmp(file_md5sum, md5, 16) == 0)
break;
i++;
}
if(i == NR_IMX_SUMS)
{
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);
/* load OF file */
struct sb_file_t *sb_file;
do
{
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);
/* 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);
if(ret == IMX_SUCCESS)
ret = sb_write_file(sb_file, outfile);
clear_keys();
free(boot);
sb_free(sb_file);
return ret;
printf("[ERR] MD5 sum doesn't match any known file\n");
return IMX_NO_MATCH;
}
*md5_idx = i;
return IMX_SUCCESS;
}
enum imx_error_t extract_firmware(const char *infile,
enum imx_firmware_variant_t fw_variant, const char *outfile)
/* read a file to a buffer */
static enum imx_error_t read_file(const char *file, void **buffer, size_t *size)
{
/* Dump tables */
if(fw_variant > VARIANT_COUNT) {
return IMX_ERROR;
FILE *f = fopen(file, "rb");
if(f == NULL)
{
printf("[ERR] Cannot open file '%s' for reading: %m\n", file);
return IMX_OPEN_ERROR;
}
dump_imx_dev_info("[INFO] ");
/* compute MD5 sum of the file */
uint8_t file_md5sum[16];
FILE *f = fopen(infile, "rb");
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");
@ -603,54 +529,171 @@ enum imx_error_t extract_firmware(const char *infile,
printf("[ERR] Cannot read file\n");
return IMX_READ_ERROR;
}
md5_context ctx;
md5_starts(&ctx);
md5_update(&ctx, buf, sz);
md5_finish(&ctx, file_md5sum);
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);
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);
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 */
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;
ret = load_sb_file(infile, md5_idx, opt, &sb_file);
if(ret != IMX_SUCCESS)
{
rb_fw_free(&boot_fw);
return ret;
}
/* produce file */
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();
rb_fw_free(&boot_fw);
sb_free(sb_file);
return ret;
}
enum imx_error_t extract_firmware(const char *infile,
enum imx_firmware_variant_t fw_variant, const char *outfile)
{
/* 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];
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)
{
free(buf);
return ret;
}
printf("[INFO] MD5 sum of the file: ");
print_hex(file_md5sum, 16, true);
/* find model */
int md5_idx;
ret = find_model_by_md5sum(file_md5sum, &md5_idx);
if(ret != IMX_SUCCESS)
{
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;
}

View file

@ -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