diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2013-06-16 01:33:16 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2013-06-16 02:14:52 +0200 |
commit | d561017f35e797d88cf0742546b5cedfc8ee7d14 (patch) | |
tree | 5d3c5b7425c6f945ad3b8b7ad0285276053f1dcf | |
parent | 7c7fa369186536adaf9ff35ec356525b5c5a8379 (diff) | |
download | rockbox-d561017f35e797d88cf0742546b5cedfc8ee7d14.tar.gz rockbox-d561017f35e797d88cf0742546b5cedfc8ee7d14.zip |
mkimxboot: factor code, add support for ELF files
Refactor code.
This tool can now either load a scrambled rockbox
firmware (in which case the model is check against the
firmware), or any ELF file. This is useful for example
for hwstub which produces a ELF file and still needs
to be loaded by producing a SB file.
Change-Id: I7aa381b3f6587788d1950793e89ce5608c53cccc
-rw-r--r-- | rbutil/mkimxboot/Makefile | 2 | ||||
-rw-r--r-- | rbutil/mkimxboot/main.c | 5 | ||||
-rw-r--r-- | rbutil/mkimxboot/mkimxboot.c | 176 |
3 files changed, 150 insertions, 33 deletions
diff --git a/rbutil/mkimxboot/Makefile b/rbutil/mkimxboot/Makefile index acfe8de6bb..1b0dbb15d3 100644 --- a/rbutil/mkimxboot/Makefile +++ b/rbutil/mkimxboot/Makefile | |||
@@ -13,7 +13,7 @@ CFLAGS += -std=c99 -g -O3 | |||
13 | OUTPUT = mkimxboot | 13 | OUTPUT = mkimxboot |
14 | 14 | ||
15 | # inputs for lib | 15 | # inputs for lib |
16 | IMXTOOLS_SOURCES = misc.c sb.c crypto.c crc.c aes128.c sha1.c | 16 | IMXTOOLS_SOURCES = misc.c sb.c crypto.c crc.c aes128.c sha1.c elf.c |
17 | LIBSOURCES := dualboot.c mkimxboot.c md5.c \ | 17 | LIBSOURCES := dualboot.c mkimxboot.c md5.c \ |
18 | $(addprefix $(IMXTOOLS_DIR),$(IMXTOOLS_SOURCES)) | 18 | $(addprefix $(IMXTOOLS_DIR),$(IMXTOOLS_SOURCES)) |
19 | # inputs for binary only | 19 | # inputs for binary only |
diff --git a/rbutil/mkimxboot/main.c b/rbutil/mkimxboot/main.c index b77aa27ce8..8e7eb2901a 100644 --- a/rbutil/mkimxboot/main.c +++ b/rbutil/mkimxboot/main.c | |||
@@ -66,6 +66,11 @@ static void usage(void) | |||
66 | } | 66 | } |
67 | printf("\n"); | 67 | printf("\n"); |
68 | printf("By default a dualboot image is built\n"); | 68 | printf("By default a dualboot image is built\n"); |
69 | printf("This tools supports the following format for the boot file:\n"); | ||
70 | printf("- rockbox scramble format\n"); | ||
71 | printf("- elf format\n"); | ||
72 | printf("Additional checks will be performed on rockbox scramble format to\n"); | ||
73 | printf("ensure soundness of operation.\n"); | ||
69 | exit(1); | 74 | exit(1); |
70 | } | 75 | } |
71 | 76 | ||
diff --git a/rbutil/mkimxboot/mkimxboot.c b/rbutil/mkimxboot/mkimxboot.c index da1e49876d..64df2ca02c 100644 --- a/rbutil/mkimxboot/mkimxboot.c +++ b/rbutil/mkimxboot/mkimxboot.c | |||
@@ -27,13 +27,15 @@ | |||
27 | #include "sb.h" | 27 | #include "sb.h" |
28 | #include "dualboot.h" | 28 | #include "dualboot.h" |
29 | #include "md5.h" | 29 | #include "md5.h" |
30 | #include "elf.h" | ||
30 | 31 | ||
31 | /* abstract structure to represent a Rockbox firmware. It can be a scrambled file | 32 | /* abstract structure to represent a Rockbox firmware. It can be a scrambled file |
32 | * or an ELF file or whatever. */ | 33 | * or an ELF file or whatever. */ |
33 | struct rb_fw_t | 34 | struct rb_fw_t |
34 | { | 35 | { |
35 | void *boot; | 36 | int nr_insts; |
36 | size_t boot_sz; | 37 | struct sb_inst_t *insts; |
38 | int entry_idx; | ||
37 | }; | 39 | }; |
38 | 40 | ||
39 | struct imx_fw_variant_desc_t | 41 | struct imx_fw_variant_desc_t |
@@ -167,6 +169,24 @@ static const struct imx_model_desc_t imx_models[] = | |||
167 | #define MAGIC_RECOVERY 0xfee1dead | 169 | #define MAGIC_RECOVERY 0xfee1dead |
168 | #define MAGIC_NORMAL 0xcafebabe | 170 | #define MAGIC_NORMAL 0xcafebabe |
169 | 171 | ||
172 | static int rb_fw_get_sb_inst_count(struct rb_fw_t *fw) | ||
173 | { | ||
174 | return fw->nr_insts; | ||
175 | } | ||
176 | |||
177 | /* fill sb instruction for the firmware, fill fill rb_fw_get_sb_inst_count() instructions */ | ||
178 | static void rb_fw_fill_sb(struct rb_fw_t *fw, struct sb_inst_t *inst, | ||
179 | uint32_t entry_arg) | ||
180 | { | ||
181 | memcpy(inst, fw->insts, fw->nr_insts * sizeof(struct sb_inst_t)); | ||
182 | /* copy data if needed */ | ||
183 | for(int i = 0; i < fw->nr_insts; i++) | ||
184 | if(fw->insts[i].inst == SB_INST_LOAD) | ||
185 | fw->insts[i].data = memdup(fw->insts[i].data, fw->insts[i].size); | ||
186 | /* replace call argument of the entry point */ | ||
187 | inst[fw->entry_idx].argument = entry_arg; | ||
188 | } | ||
189 | |||
170 | static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, | 190 | static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, |
171 | enum imx_output_type_t type, struct sb_file_t *sb_file, struct rb_fw_t boot_fw) | 191 | enum imx_output_type_t type, struct sb_file_t *sb_file, struct rb_fw_t boot_fw) |
172 | { | 192 | { |
@@ -189,6 +209,9 @@ static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, | |||
189 | sb_file->override_crypto_iv = false; | 209 | sb_file->override_crypto_iv = false; |
190 | sb_file->override_real_key = false; | 210 | sb_file->override_real_key = false; |
191 | 211 | ||
212 | /* used to manipulate entries */ | ||
213 | int nr_boot_inst = rb_fw_get_sb_inst_count(&boot_fw); | ||
214 | |||
192 | /* first locate the good instruction */ | 215 | /* first locate the good instruction */ |
193 | struct sb_section_t *sec = &sb_file->sections[0]; | 216 | struct sb_section_t *sec = &sb_file->sections[0]; |
194 | int jump_idx = 0; | 217 | int jump_idx = 0; |
@@ -230,19 +253,12 @@ static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, | |||
230 | /* create a new section */ | 253 | /* create a new section */ |
231 | struct sb_section_t rock_sec; | 254 | struct sb_section_t rock_sec; |
232 | memset(&rock_sec, 0, sizeof(rock_sec)); | 255 | memset(&rock_sec, 0, sizeof(rock_sec)); |
233 | /* section has two instructions: load and call */ | 256 | /* section can have any number of instructions */ |
234 | rock_sec.identifier = MAGIC_ROCK; | 257 | rock_sec.identifier = MAGIC_ROCK; |
235 | rock_sec.alignment = BLOCK_SIZE; | 258 | rock_sec.alignment = BLOCK_SIZE; |
236 | rock_sec.nr_insts = 2; | 259 | rock_sec.nr_insts = nr_boot_inst; |
237 | rock_sec.insts = xmalloc(2 * sizeof(struct sb_inst_t)); | 260 | rock_sec.insts = xmalloc(nr_boot_inst * sizeof(struct sb_inst_t)); |
238 | memset(rock_sec.insts, 0, 2 * sizeof(struct sb_inst_t)); | 261 | rb_fw_fill_sb(&boot_fw, rock_sec.insts, MAGIC_NORMAL); |
239 | rock_sec.insts[0].inst = SB_INST_LOAD; | ||
240 | rock_sec.insts[0].size = boot_fw.boot_sz; | ||
241 | rock_sec.insts[0].data = memdup(boot_fw.boot, boot_fw.boot_sz); | ||
242 | rock_sec.insts[0].addr = imx_models[model].bootloader_addr; | ||
243 | rock_sec.insts[1].inst = SB_INST_JUMP; | ||
244 | rock_sec.insts[1].addr = imx_models[model].bootloader_addr; | ||
245 | rock_sec.insts[1].argument = MAGIC_NORMAL; | ||
246 | 262 | ||
247 | sb_file->sections = augment_array(sb_file->sections, | 263 | sb_file->sections = augment_array(sb_file->sections, |
248 | sizeof(struct sb_section_t), sb_file->nr_sections, | 264 | sizeof(struct sb_section_t), sb_file->nr_sections, |
@@ -254,23 +270,16 @@ static enum imx_error_t patch_std_zero_host_play(int jump_before, int model, | |||
254 | else if(type == IMX_SINGLEBOOT || type == IMX_RECOVERY) | 270 | else if(type == IMX_SINGLEBOOT || type == IMX_RECOVERY) |
255 | { | 271 | { |
256 | bool recovery = type == IMX_RECOVERY; | 272 | bool recovery = type == IMX_RECOVERY; |
257 | /* remove everything after the call and add two instructions: load and call */ | 273 | /* remove everything after the call and add instructions for firmware */ |
258 | struct sb_inst_t *new_insts = xmalloc(sizeof(struct sb_inst_t) * (jump_idx + 2)); | 274 | struct sb_inst_t *new_insts = xmalloc(sizeof(struct sb_inst_t) * (jump_idx + nr_boot_inst)); |
259 | memcpy(new_insts, sec->insts, sizeof(struct sb_inst_t) * jump_idx); | 275 | memcpy(new_insts, sec->insts, sizeof(struct sb_inst_t) * jump_idx); |
260 | for(int i = jump_idx; i < sec->nr_insts; i++) | 276 | for(int i = jump_idx; i < sec->nr_insts; i++) |
261 | sb_free_instruction(sec->insts[i]); | 277 | sb_free_instruction(sec->insts[i]); |
262 | memset(new_insts + jump_idx, 0, 2 * sizeof(struct sb_inst_t)); | 278 | rb_fw_fill_sb(&boot_fw, &new_insts[jump_idx], recovery ? MAGIC_RECOVERY : MAGIC_NORMAL); |
263 | new_insts[jump_idx + 0].inst = SB_INST_LOAD; | ||
264 | new_insts[jump_idx + 0].size = boot_fw.boot_sz; | ||
265 | new_insts[jump_idx + 0].data = memdup(boot_fw.boot, boot_fw.boot_sz); | ||
266 | new_insts[jump_idx + 0].addr = imx_models[model].bootloader_addr; | ||
267 | new_insts[jump_idx + 1].inst = SB_INST_JUMP; | ||
268 | new_insts[jump_idx + 1].addr = imx_models[model].bootloader_addr; | ||
269 | new_insts[jump_idx + 1].argument = recovery ? MAGIC_RECOVERY : MAGIC_NORMAL; | ||
270 | 279 | ||
271 | free(sec->insts); | 280 | free(sec->insts); |
272 | sec->insts = new_insts; | 281 | sec->insts = new_insts; |
273 | sec->nr_insts = jump_idx + 2; | 282 | sec->nr_insts = jump_idx + nr_boot_inst; |
274 | /* remove all other sections */ | 283 | /* remove all other sections */ |
275 | for(int i = 1; i < sb_file->nr_sections; i++) | 284 | for(int i = 1; i < sb_file->nr_sections; i++) |
276 | sb_free_section(sb_file->sections[i]); | 285 | sb_free_section(sb_file->sections[i]); |
@@ -558,9 +567,10 @@ static enum imx_error_t load_sb_file(const char *file, int md5_idx, | |||
558 | return IMX_SUCCESS; | 567 | return IMX_SUCCESS; |
559 | } | 568 | } |
560 | 569 | ||
561 | /* Load a rockbox firwmare from a buffer. Data is copied. */ | 570 | /* Load a rockbox firwmare from a buffer. Data is copied. Assume firmware is |
562 | static enum imx_error_t rb_fw_load_buf(struct rb_fw_t *fw, uint8_t *buf, size_t sz, | 571 | * using our scramble format. */ |
563 | enum imx_model_t model) | 572 | static enum imx_error_t rb_fw_load_buf_scramble(struct rb_fw_t *fw, uint8_t *buf, |
573 | size_t sz, enum imx_model_t model) | ||
564 | { | 574 | { |
565 | if(sz < 8) | 575 | if(sz < 8) |
566 | { | 576 | { |
@@ -583,11 +593,112 @@ static enum imx_error_t rb_fw_load_buf(struct rb_fw_t *fw, uint8_t *buf, size_t | |||
583 | printf("[ERR] Bootloader checksum mismatch\n"); | 593 | printf("[ERR] Bootloader checksum mismatch\n"); |
584 | return IMX_BOOT_CHECKSUM_ERROR; | 594 | return IMX_BOOT_CHECKSUM_ERROR; |
585 | } | 595 | } |
586 | fw->boot = memdup(buf + 8, sz - 8); | 596 | /* two instructions: load and jump */ |
587 | fw->boot_sz = sz - 8; | 597 | fw->nr_insts = 2; |
598 | fw->entry_idx = 1; | ||
599 | fw->insts = xmalloc(fw->nr_insts * sizeof(struct sb_inst_t)); | ||
600 | memset(fw->insts, 0, fw->nr_insts * sizeof(struct sb_inst_t)); | ||
601 | fw->insts[0].inst = SB_INST_LOAD; | ||
602 | fw->insts[0].addr = imx_models[model].bootloader_addr; | ||
603 | fw->insts[0].size = sz - 8; | ||
604 | fw->insts[0].data = memdup(buf + 8, sz - 8); | ||
605 | fw->insts[1].inst = SB_INST_JUMP; | ||
606 | fw->insts[1].addr = imx_models[model].bootloader_addr; | ||
607 | return IMX_SUCCESS; | ||
608 | } | ||
609 | |||
610 | struct elf_user_t | ||
611 | { | ||
612 | void *buf; | ||
613 | size_t sz; | ||
614 | }; | ||
615 | |||
616 | static bool elf_read(void *user, uint32_t addr, void *buf, size_t count) | ||
617 | { | ||
618 | struct elf_user_t *u = user; | ||
619 | if(addr + count <= u->sz) | ||
620 | { | ||
621 | memcpy(buf, u->buf + addr, count); | ||
622 | return true; | ||
623 | } | ||
624 | else | ||
625 | return false; | ||
626 | } | ||
627 | |||
628 | static void elf_printf(void *user, bool error, const char *fmt, ...) | ||
629 | { | ||
630 | if(!g_debug && !error) | ||
631 | return; | ||
632 | (void) user; | ||
633 | va_list args; | ||
634 | va_start(args, fmt); | ||
635 | vprintf(fmt, args); | ||
636 | va_end(args); | ||
637 | } | ||
638 | /* Load a rockbox firwmare from a buffer. Data is copied. Assume firmware is | ||
639 | * using ELF format. */ | ||
640 | static enum imx_error_t rb_fw_load_buf_elf(struct rb_fw_t *fw, uint8_t *buf, | ||
641 | size_t sz, enum imx_model_t model) | ||
642 | { | ||
643 | struct elf_params_t elf; | ||
644 | struct elf_user_t user; | ||
645 | user.buf = buf; | ||
646 | user.sz = sz; | ||
647 | elf_init(&elf); | ||
648 | if(!elf_read_file(&elf, &elf_read, &elf_printf, &user)) | ||
649 | { | ||
650 | elf_release(&elf); | ||
651 | printf("[ERR] Error parsing ELF file\n"); | ||
652 | return IMX_BOOT_INVALID; | ||
653 | } | ||
654 | elf_translate_addresses(&elf); | ||
655 | fw->nr_insts = elf_get_nr_sections(&elf) + 1; | ||
656 | fw->insts = xmalloc(fw->nr_insts * sizeof(struct sb_inst_t)); | ||
657 | fw->entry_idx = fw->nr_insts - 1; | ||
658 | memset(fw->insts, 0, fw->nr_insts * sizeof(struct sb_inst_t)); | ||
659 | struct elf_section_t *sec = elf.first_section; | ||
660 | for(int i = 0; sec; i++, sec = sec->next) | ||
661 | { | ||
662 | fw->insts[i].addr = sec->addr; | ||
663 | fw->insts[i].size = sec->size; | ||
664 | if(sec->type == EST_LOAD) | ||
665 | { | ||
666 | fw->insts[i].inst = SB_INST_LOAD; | ||
667 | fw->insts[i].data = memdup(sec->section, sec->size); | ||
668 | } | ||
669 | else if(sec->type == EST_FILL) | ||
670 | { | ||
671 | fw->insts[i].inst = SB_INST_FILL; | ||
672 | fw->insts[i].pattern = sec->pattern; | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | printf("[WARN] Warning parsing ELF file: unsupported section type mapped to NOP!\n"); | ||
677 | fw->insts[i].inst = SB_INST_NOP; | ||
678 | } | ||
679 | } | ||
680 | fw->insts[fw->nr_insts - 1].inst = SB_INST_JUMP; | ||
681 | if(!elf_get_start_addr(&elf, &fw->insts[fw->nr_insts - 1].addr)) | ||
682 | { | ||
683 | elf_release(&elf); | ||
684 | printf("[ERROR] Error parsing ELF file: it has no entry point!\n"); | ||
685 | return IMX_BOOT_INVALID; | ||
686 | } | ||
687 | elf_release(&elf); | ||
588 | return IMX_SUCCESS; | 688 | return IMX_SUCCESS; |
589 | } | 689 | } |
590 | 690 | ||
691 | /* Load a rockbox firwmare from a buffer. Data is copied. */ | ||
692 | static enum imx_error_t rb_fw_load_buf(struct rb_fw_t *fw, uint8_t *buf, | ||
693 | size_t sz, enum imx_model_t model) | ||
694 | { | ||
695 | /* detect file format */ | ||
696 | if(sz >= 4 && buf[0] == 0x7f && memcmp(buf + 1, "ELF", 3) == 0) | ||
697 | return rb_fw_load_buf_elf(fw, buf, sz, model); | ||
698 | else | ||
699 | return rb_fw_load_buf_scramble(fw, buf, sz, model); | ||
700 | } | ||
701 | |||
591 | /* load a rockbox firmware from a file. */ | 702 | /* load a rockbox firmware from a file. */ |
592 | static enum imx_error_t rb_fw_load(struct rb_fw_t *fw, const char *file, | 703 | static enum imx_error_t rb_fw_load(struct rb_fw_t *fw, const char *file, |
593 | enum imx_model_t model) | 704 | enum imx_model_t model) |
@@ -606,9 +717,10 @@ static enum imx_error_t rb_fw_load(struct rb_fw_t *fw, const char *file, | |||
606 | /* free rockbox firmware */ | 717 | /* free rockbox firmware */ |
607 | static void rb_fw_free(struct rb_fw_t *fw) | 718 | static void rb_fw_free(struct rb_fw_t *fw) |
608 | { | 719 | { |
609 | free(fw->boot); | 720 | for(int i = 0; i < fw->nr_insts; i++) |
610 | fw->boot = NULL; | 721 | sb_free_instruction(fw->insts[i]); |
611 | fw->boot_sz = 0; | 722 | free(fw->insts); |
723 | memset(fw, 0, sizeof(struct rb_fw_t)); | ||
612 | } | 724 | } |
613 | 725 | ||
614 | enum imx_error_t mkimxboot(const char *infile, const char *bootfile, | 726 | enum imx_error_t mkimxboot(const char *infile, const char *bootfile, |