From b8d35c042de327a7ac5b35496283cdc2ab22d991 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Fri, 31 Oct 2014 16:09:28 +0100 Subject: rknanotools: fix rknano stages processing Change-Id: Ia88f5aa2a6c56b312f80b31afab41d1dc68b871b --- utils/rknanoutils/rkboottool/elf.c | 73 ++++++----- utils/rknanoutils/rkboottool/elf.h | 5 +- utils/rknanoutils/rkboottool/rkboottool.c | 211 +++++++++++++++++++++++------- 3 files changed, 210 insertions(+), 79 deletions(-) diff --git a/utils/rknanoutils/rkboottool/elf.c b/utils/rknanoutils/rkboottool/elf.c index 481ab98dd6..78b29506a2 100644 --- a/utils/rknanoutils/rkboottool/elf.c +++ b/utils/rknanoutils/rkboottool/elf.c @@ -21,6 +21,14 @@ #include "elf.h" #include "misc.h" +static char *strdup(const char *str) +{ + int len = strlen(str); + char *s = malloc(len + 1); + memcpy(s, str, len + 1); + return s; +} + /** * Definitions * taken from elf.h linux header @@ -189,43 +197,55 @@ static struct elf_segment_t *elf_add_segment(struct elf_params_t *params) } void elf_add_load_section(struct elf_params_t *params, - uint32_t load_addr, uint32_t size, const void *section) + uint32_t load_addr, uint32_t size, const void *section, const char *name) { struct elf_section_t *sec = elf_add_section(params); + char buffer[32]; + if(name == NULL) + { + sprintf(buffer, ".text%d", params->unique_index++); + name = buffer; + } sec->type = EST_LOAD; sec->addr = load_addr; sec->size = size; sec->section = xmalloc(size); + sec->name = strdup(name); memcpy(sec->section, section, size); } void elf_add_fill_section(struct elf_params_t *params, uint32_t fill_addr, uint32_t size, uint32_t pattern) { + char buffer[32]; + sprintf(buffer, ".bss%d", params->unique_index++); + if(pattern != 0x00) { printf("oops, non-zero filling, ignore fill section\n"); return; } - + struct elf_section_t *sec = elf_add_section(params); - + sec->type = EST_FILL; sec->addr = fill_addr; sec->size = size; sec->pattern = pattern; + sec->name = strdup(buffer); } void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, elf_printf_fn_t printf, void *user) { (void) printf; - + Elf32_Ehdr ehdr; uint32_t phnum = 0; struct elf_section_t *sec = params->first_section; uint32_t offset = 0; + uint32_t strtbl_size = 1; /* offset 0 is for the NULL name */ Elf32_Phdr phdr; Elf32_Shdr shdr; memset(&ehdr, 0, EI_NIDENT); @@ -241,7 +261,9 @@ void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, { sec->offset = 0; } - + sec->name_offset = strtbl_size; + strtbl_size += strlen(sec->name) + 1; + phnum++; sec = sec->next; } @@ -275,16 +297,14 @@ void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, write(user, 0, &ehdr, sizeof ehdr); - /* allocate enough size to hold any combinaison of .text/.bss in the string table: - * - one empty name ("\0") - * - at most N names of the form ".textXXXX\0" or ".bssXXXX\0" - * - one name ".shstrtab\0" */ - char *strtbl_content = malloc(1 + strlen(".shstrtab") + 1 + - phnum * (strlen(".textXXXX") + 1)); - + /* the last name offset gives the size of the section, we need to add a small + * amount of .shstrtab name */ + uint32_t shstrtab_index = strtbl_size; + strtbl_size += strlen(".shstrtab") + 1; + char *strtbl_content = malloc(strtbl_size); + /* create NULL and shstrtab names */ strtbl_content[0] = '\0'; - strcpy(&strtbl_content[1], ".shstrtab"); - uint32_t strtbl_index = 1 + strlen(".shstrtab") + 1; + strcpy(&strtbl_content[shstrtab_index], ".shstrtab"); uint32_t data_offset = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize + ehdr.e_shnum * ehdr.e_shentsize; @@ -294,7 +314,8 @@ void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, while(sec) { sec->offset += data_offset; - + strcpy(&strtbl_content[sec->name_offset], sec->name); + phdr.p_type = PT_LOAD; if(sec->type == EST_LOAD) phdr.p_offset = sec->offset; @@ -336,21 +357,13 @@ void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, offset += sizeof(Elf32_Shdr); } - uint32_t text_idx = 0; - uint32_t bss_idx = 0; while(sec) { - shdr.sh_name = strtbl_index; + shdr.sh_name = sec->name_offset; if(sec->type == EST_LOAD) - { - strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".text%d", text_idx++); shdr.sh_type = SHT_PROGBITS; - } else - { - strtbl_index += 1 + sprintf(&strtbl_content[strtbl_index], ".bss%d", bss_idx++); shdr.sh_type = SHT_NOBITS; - } shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; shdr.sh_addr = sec->addr; shdr.sh_offset = sec->offset; @@ -367,12 +380,12 @@ void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, } { - shdr.sh_name = 1; + shdr.sh_name = shstrtab_index; shdr.sh_type = SHT_STRTAB; shdr.sh_flags = 0; shdr.sh_addr = 0; shdr.sh_offset = strtbl_offset + data_offset; - shdr.sh_size = strtbl_index; + shdr.sh_size = strtbl_size; shdr.sh_link = SHN_UNDEF; shdr.sh_info = 0; shdr.sh_addralign = 1; @@ -391,7 +404,7 @@ void elf_write_file(struct elf_params_t *params, elf_write_fn_t write, sec = sec->next; } - write(user, strtbl_offset + data_offset, strtbl_content, strtbl_index); + write(user, strtbl_offset + data_offset, strtbl_content, strtbl_size); free(strtbl_content); } @@ -399,7 +412,7 @@ bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, elf_printf_fn_t printf, void *user) { #define error_printf(...) ({printf(user, true, __VA_ARGS__); return false;}) - + /* read header */ Elf32_Ehdr ehdr; if(!read(user, 0, &ehdr, sizeof(ehdr))) @@ -459,7 +472,7 @@ bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, void *data = xmalloc(shdr.sh_size); if(!read(user, shdr.sh_offset, data, shdr.sh_size)) error_printf("error read self section data\n"); - elf_add_load_section(params, shdr.sh_addr, shdr.sh_size, data); + elf_add_load_section(params, shdr.sh_addr, shdr.sh_size, data, NULL); free(data); if(strtab) @@ -476,7 +489,6 @@ bool elf_read_file(struct elf_params_t *params, elf_read_fn_t read, if(strtab) printf(user, false, "filter out %s\n", &strtab[shdr.sh_name], shdr.sh_type); } - } free(strtab); /* run through segments */ @@ -562,6 +574,7 @@ void elf_release(struct elf_params_t *params) struct elf_section_t *next_sec = sec->next; if(sec->type == EST_LOAD) free(sec->section); + free(sec->name); free(sec); sec = next_sec; } diff --git a/utils/rknanoutils/rkboottool/elf.h b/utils/rknanoutils/rkboottool/elf.h index 2166833276..2408e0c588 100644 --- a/utils/rknanoutils/rkboottool/elf.h +++ b/utils/rknanoutils/rkboottool/elf.h @@ -42,6 +42,7 @@ struct elf_section_t uint32_t addr; /* virtual address */ uint32_t size; /* virtual size */ enum elf_section_type_t type; + char *name; /* */ void *section; /* data */ uint32_t pattern; /* fill pattern */ @@ -49,6 +50,7 @@ struct elf_section_t struct elf_section_t *next; /* Internal to elf_write_file */ uint32_t offset; + uint32_t name_offset; }; struct elf_segment_t @@ -68,6 +70,7 @@ struct elf_params_t struct elf_section_t *last_section; struct elf_segment_t *first_segment; struct elf_segment_t *last_segment; + int unique_index; }; typedef bool (*elf_read_fn_t)(void *user, uint32_t addr, void *buf, size_t count); @@ -77,7 +80,7 @@ typedef void (*elf_printf_fn_t)(void *user, bool error, const char *fmt, ...); void elf_init(struct elf_params_t *params); void elf_add_load_section(struct elf_params_t *params, - uint32_t load_addr, uint32_t size, const void *section); + uint32_t load_addr, uint32_t size, const void *section, const char *name); void elf_add_fill_section(struct elf_params_t *params, uint32_t fill_addr, uint32_t size, uint32_t pattern); uint32_t elf_translate_virtual_address(struct elf_params_t *params, uint32_t addr); diff --git a/utils/rknanoutils/rkboottool/rkboottool.c b/utils/rknanoutils/rkboottool/rkboottool.c index ee9b17e610..763751e41f 100644 --- a/utils/rknanoutils/rkboottool/rkboottool.c +++ b/utils/rknanoutils/rkboottool/rkboottool.c @@ -15,6 +15,11 @@ bool g_debug = false; typedef uint8_t packed_bcd_uint8_t; typedef uint16_t packed_bcd_uint16_t; +/** + * RKnanoFW + * contains resources and code stages + */ + struct rknano_date_t { packed_bcd_uint16_t year; @@ -176,7 +181,7 @@ static void save_blob(const struct rknano_blob_t *b, void *buf, uint32_t size, } encode_page(buff_ptr, out_ptr, len); } - + if(f) { fwrite(ptr, b->size, 1, f); @@ -276,6 +281,11 @@ static int do_nanofw_image(uint8_t *buf, unsigned long size) return 0; } +/** + * RKNano stage + * contains code and memory mapping + */ + struct rknano_stage_header_t { uint32_t addr; @@ -283,21 +293,28 @@ struct rknano_stage_header_t } __attribute__((packed)); /* - * The [code_pa,code_pa+code_sz[ and [data_pa,data_pa+data_sz[ ranges - * are consistent: they never overlap and have no gaps and fill the - * entire space. Furthermore they match the code sequences so it's - * reasonable to assume these fields are correct. - * The other fields are still quite unsure. */ + * NOTE this theory has not been tested against actual code, it's still a guess + * The firmware is too big to fit in memory so it's split into sections, + * each section having a "virtual address" and a "physical address". + * Except it gets tricky because the RKNano doesn't have a MMU but a MPU, + * so most probably the OF divides the memory into regions (8 would match + * hardware capabilities), each being able to contain one of the sections + * in the OF file. To gracefully handle jumps between sections, my guess is + * that the entire OF is linked as a flat image, cut into pieces and + * then each code section get relocated except for jump/calls outside of it: + * this will trigger an access fault when trying to access another section, which + * the OF can trap and then load the corresponding section. + */ struct rknano_stage_section_t { - uint32_t code_pa; uint32_t code_va; + uint32_t code_pa; uint32_t code_sz; - uint32_t data_pa; uint32_t data_va; + uint32_t data_pa; uint32_t data_sz; - uint32_t bss_va; + uint32_t bss_pa; uint32_t bss_sz; } __attribute__((packed)); @@ -319,14 +336,14 @@ static void elf_write(void *user, uint32_t addr, const void *buf, size_t count) fwrite(buf, count, 1, f); } -static void extract_elf_section(struct elf_params_t *elf, int count) +static void extract_elf_section(struct elf_params_t *elf) { if(g_out_prefix == NULL) return; char *filename = xmalloc(strlen(g_out_prefix) + 32); - sprintf(filename, "%s%d.elf", g_out_prefix, count); + sprintf(filename, "%s.elf", g_out_prefix); if(g_debug) - printf("Write entry %d to %s\n", count, filename); + printf("Write stage to %s\n", filename); FILE *fd = fopen(filename, "wb"); free(filename); @@ -337,67 +354,149 @@ static void extract_elf_section(struct elf_params_t *elf, int count) fclose(fd); } +struct range_t +{ + unsigned long start, size; + int section; + int type; +}; + +int range_cmp(const void *_a, const void *_b) +{ + const struct range_t *a = _a, *b = _b; + if(a->start == b->start) + return a->size - b->size; + return a->start - b->start; +} + +#define RANGE_TXT 0 +#define RANGE_DAT 1 + static int do_nanostage_image(uint8_t *buf, unsigned long size) { if(size < sizeof(struct rknano_stage_section_t)) return 1; struct rknano_stage_header_t *hdr = (void *)buf; + size_t hdr_size = sizeof(struct rknano_stage_header_t) + + hdr->count * sizeof(struct rknano_stage_section_t); + if(size < hdr_size) + return 1; cprintf(BLUE, "Header\n"); cprintf(GREEN, " Base Address: "); cprintf(YELLOW, "%#08x\n", hdr->addr); - cprintf(GREEN, " Load count: "); + cprintf(GREEN, " Section count: "); cprintf(YELLOW, "%d\n", hdr->count); - - struct rknano_stage_section_t *sec = (void *)(hdr + 1); + struct rknano_stage_section_t *sec = (void *)(hdr + 1); + struct elf_params_t elf; + elf_init(&elf); + bool error = false; + /* track range for overlap */ + struct range_t *ranges = malloc(sizeof(struct range_t) * 2 * hdr->count); + int nr_ranges = 0; for(unsigned i = 0; i < hdr->count; i++, sec++) { cprintf(BLUE, "Section %d\n", i); cprintf(GREEN, " Code: "); - cprintf(YELLOW, "0x%08x", sec->code_pa); + cprintf(YELLOW, "0x%08x", sec->code_va); cprintf(RED, "-(txt)-"); - cprintf(YELLOW, "0x%08x", sec->code_pa + sec->code_sz); + cprintf(YELLOW, "0x%08x", sec->code_va + sec->code_sz); cprintf(BLUE, " |--> "); - cprintf(YELLOW, "0x%08x", sec->code_va); + cprintf(YELLOW, "0x%08x", sec->code_pa); cprintf(RED, "-(txt)-"); - cprintf(YELLOW, "0x%08x\n", sec->code_va + sec->code_sz); + cprintf(YELLOW, "0x%08x\n", sec->code_pa + sec->code_sz); + + /* add ranges */ + ranges[nr_ranges].start = sec->code_va; + ranges[nr_ranges].size = sec->code_sz; + ranges[nr_ranges].section = i; + ranges[nr_ranges].type = RANGE_TXT; + ranges[nr_ranges + 1].start = sec->data_va; + ranges[nr_ranges + 1].size = sec->data_sz; + ranges[nr_ranges + 1].section = i; + ranges[nr_ranges + 1].type = RANGE_DAT; + nr_ranges += 2; cprintf(GREEN, " Data: "); - cprintf(YELLOW, "0x%08x", sec->data_pa); + cprintf(YELLOW, "0x%08x", sec->data_va); cprintf(RED, "-(dat)-"); - cprintf(YELLOW, "0x%08x", sec->data_pa + sec->data_sz); + cprintf(YELLOW, "0x%08x", sec->data_va + sec->data_sz); cprintf(BLUE, " |--> "); - cprintf(YELLOW, "0x%08x", sec->data_va); + cprintf(YELLOW, "0x%08x", sec->data_pa); cprintf(RED, "-(dat)-"); - cprintf(YELLOW, "0x%08x\n", sec->data_va + sec->data_sz); + cprintf(YELLOW, "0x%08x\n", sec->data_pa + sec->data_sz); cprintf(GREEN, " Data: "); cprintf(RED, " "); cprintf(BLUE, " |--> "); - cprintf(YELLOW, "0x%08x", sec->bss_va); + cprintf(YELLOW, "0x%08x", sec->bss_pa); cprintf(RED, "-(bss)-"); - cprintf(YELLOW, "0x%08x\n", sec->bss_va + sec->bss_sz); + cprintf(YELLOW, "0x%08x\n", sec->bss_pa + sec->bss_sz); + +#define check_range_(start,sz) \ + ((start) >= hdr_size && (start) + (sz) <= size) +#define check_range(start,sz) \ + ((start) >= hdr->addr && check_range_((start) - hdr->addr, sz)) + /* check ranges */ + if(sec->code_sz != 0 && !check_range(sec->code_va, sec->code_sz)) + { + cprintf(GREY, "Invalid stage: out of bound code\n"); + error = true; + break; + } + if(sec->data_sz != 0 && !check_range(sec->data_va, sec->data_sz)) + { + cprintf(GREY, "Invalid stage: out of bound data\n"); + error = true; + break; + } +#undef check_range_ +#undef check_range -#if 0 - struct rknano_blob_t blob; - blob.offset = sec->code_pa - hdr->addr; - blob.size = sec->code_sz; - save_blob(&blob, buf, size, "entry.", i, NO_ENC); -#else - struct elf_params_t elf; - elf_init(&elf); - elf_add_load_section(&elf, sec->code_va, sec->code_sz, buf + sec->code_pa - hdr->addr); - elf_add_load_section(&elf, sec->data_va, sec->data_sz, buf + sec->data_pa - hdr->addr); - elf_add_fill_section(&elf, sec->bss_va, sec->bss_sz, 0); - extract_elf_section(&elf, i); - elf_release(&elf); -#endif + char buffer[32]; + if(sec->code_sz != 0) + { + sprintf(buffer, ".text.%d", i); + elf_add_load_section(&elf, sec->code_va, sec->code_sz, + buf + sec->code_va - hdr->addr, buffer); + } + if(sec->data_sz != 0) + { + sprintf(buffer, ".data.%d", i); + elf_add_load_section(&elf, sec->data_va, sec->data_sz, + buf + sec->data_va - hdr->addr, buffer); + } + } + /* sort ranges and check overlap */ + qsort(ranges, nr_ranges, sizeof(struct range_t), range_cmp); + for(int i = 1; i < nr_ranges; i++) + { + if(ranges[i - 1].start + ranges[i - 1].size > ranges[i].start) + { + error = true; + static const char *type[] = {"txt", "dat"}; + cprintf(GREY, "Section overlap: section %d %s intersects section %d %s\n", + ranges[i - 1].section, type[ranges[i - 1].type], ranges[i].section, + type[ranges[i].type]); + break; + } } + if(!error) + extract_elf_section(&elf); + /* FIXME for full information, we could add segments to the ELF file to + * keep the mapping, but it's unclear if that would do any good */ + elf_release(&elf); + free(ranges); return 0; } +/** + * RKNano BOOT + * contains named bootloader stages + */ + #define MAGIC_BOOT "BOOT" #define MAGIC_BOOT_SIZE 4 @@ -428,6 +527,8 @@ struct rknano_boot_header_t uint32_t field_34; } __attribute__((packed)); +#define BOOT_CHIP_RKNANO 0x30 + struct rknano_boot_entry_t { uint8_t entry_size; // unsure @@ -588,7 +689,7 @@ static int do_boot_image(uint8_t *buf, unsigned long size) cprintf(RED, "OK\n"); else cprintf(RED, "Mismatch\n"); - + #define print(str, name) cprintf(GREEN, " "str": ");cprintf(YELLOW, "%#x\n", (unsigned)hdr->name) #define print_arr(str, name, sz) \ cprintf(GREEN, " "str":");for(int i = 0; i < sz; i++)cprintf(YELLOW, " %#x", (unsigned)hdr->name[i]);printf("\n") @@ -602,8 +703,12 @@ static int do_boot_image(uint8_t *buf, unsigned long size) hdr->hour, hdr->minute, hdr->second); cprintf(GREEN, " Chip: "); - cprintf(YELLOW, "%#x\n", hdr->chip); - + cprintf(YELLOW, "%#x ", hdr->chip); + if(hdr->chip == BOOT_CHIP_RKNANO) + cprintf(RED, "(RKNANO)\n"); + else + cprintf(RED, "(unknown)\n"); + print_arr("field_2A", field_2B, 9); print("field_34", field_34); @@ -625,10 +730,15 @@ static int do_boot_image(uint8_t *buf, unsigned long size) cprintf(RED, "OK\n"); else cprintf(RED, "Mismatch\n"); - + return 0; } +/** + * RKFW + * contains bootloader and update + */ + typedef struct rknano_blob_t rkfw_blob_t; #define MAGIC_RKFW "RKFW" @@ -637,7 +747,7 @@ typedef struct rknano_blob_t rkfw_blob_t; struct rkfw_header_t { char magic[MAGIC_RKFW_SIZE]; - uint16_t hdr_size; // UNSURE + uint16_t hdr_size; uint32_t version; uint32_t code; uint16_t year; @@ -652,6 +762,8 @@ struct rkfw_header_t uint8_t pad[61]; } __attribute__((packed)); +#define RKFW_CHIP_RKNANO 0x30 + static int do_rkfw_image(uint8_t *buf, unsigned long size) { if(size < sizeof(struct rkfw_header_t)) @@ -686,8 +798,12 @@ static int do_rkfw_image(uint8_t *buf, unsigned long size) hdr->hour, hdr->minute, hdr->second); cprintf(GREEN, " Chip: "); - cprintf(YELLOW, "%#x\n", hdr->chip); - + cprintf(YELLOW, "%#x ", hdr->chip); + if(hdr->chip == RKFW_CHIP_RKNANO) + cprintf(RED, "(RKNANO)\n"); + else + cprintf(RED, "(unknown)\n"); + cprintf(GREEN, " Loader: "); print_blob_interval(&hdr->loader); cprintf(OFF, "\n"); @@ -852,7 +968,7 @@ int main(int argc, char **argv) perror("Cannot read file"); return 1; } - + fclose(fin); if(try_nanofw && !do_nanofw_image(buf, size)) @@ -873,4 +989,3 @@ int main(int argc, char **argv) return 0; } - -- cgit v1.2.3