/** * CILO Elf Loader * (c) 2008 Philippe Vachon * * Licensed under the GNU General Public License v2. See COPYING * in the distribution source directory for more information. */ #include #include #include #include /* platform-specific defines */ #include /** * load a single ELF section into memory at address. Assumes ELF data is * contiguous in memory. * @param base location (in memory) of the ELF file * @param address address at which the ELF section will be loaded * @param file_offset offset (in bytes) in the ELF file where the section is * @param length Length of the section (in bytes) */ void load_elf32_section(struct file *fp, uint32_t address, uint32_t file_offset, uint32_t length) { uint8_t *elf_loc = (uint8_t *)address; #ifdef DEBUG printf("Init data: %08x length %08x\n", address, length); #endif cilo_seek(fp, (uint32_t)file_offset, SEEK_SET); cilo_read(elf_loc, (uint32_t)length, 1, fp); } /** * Create an uninitialized data (.bss) region of memory. * @param address Start address of this region * @param lenght length of this region */ void load_elf32_uninitialized_memory(uint32_t address, uint32_t length) { int i = 0; uint8_t *p = (uint8_t *)address; #ifdef DEBUG printf("Uninit data: %08x, len %08x\n", address, length); #endif for (i = 0; i < length; i++) { p[i] = 0; } } /** * Load an ELF file into memory from the given base. Loads at * offset + image_size so that a later memcpy routine can be used to copy * things into RAM and then kick off the boot process. * @param base The address of the ELF file in memory * @param loader_addr address of the loader binary in memory * @return */ int load_elf32_file(struct file *fp, char *cmd_line) { struct elf32_header hdr; uint32_t mem_sz = 0; /* read in header entries */ cilo_read(&hdr, sizeof(struct elf32_header), 1, fp); /* check the file magic */ if (hdr.ident[0] != ELF_MAGIC_1 || hdr.ident[1] != ELF_MAGIC_2 || hdr.ident[2] != ELF_MAGIC_3 || hdr.ident[3] != ELF_MAGIC_4) { printf("Bad ELF magic found. Found: %#2x %#2x %#2x %#2x.\n", hdr.ident[0], hdr.ident[1], hdr.ident[2], hdr.ident[3]); return -1; } /* check machine class: */ if (!hdr.ident[ELF_INDEX_CLASS] == ELF_CLASS_32) { printf("Invalid ELF machine class found. Found: %2x.\n", hdr.ident[ELF_INDEX_CLASS]); return -1; } /* check endianess: */ if (hdr.ident[ELF_INDEX_DATA] != ELF_DATA_MSB) { printf("Non-big endian ELF file detected. Aborting load.\n"); return -1; } if (hdr.ehsize != 52 /* bytes */) { printf("Warning: ELF header greater than 52 bytes found. Found: %u\n", hdr.ehsize); } if (hdr.phnum == 0) { printf("Found zero segments in ELF file. Aborting load.\n"); return -1; } int i; struct elf32_phdr phdr; cilo_seek(fp, hdr.phoff, SEEK_SET); /* read the PT_LOAD segments into memory at paddr + mem_sz */ cilo_seek(fp, hdr.phoff, SEEK_SET); for (i = 0; i < hdr.phnum; i++) { cilo_read(&phdr, sizeof(struct elf32_phdr), 1, fp); /* skip unloadable segments */ if (phdr.type != ELF_PT_LOAD) continue; load_elf32_section(fp, phdr.paddr, phdr.offset, phdr.filesz); mem_sz += phdr.memsz; if (phdr.memsz - phdr.filesz > 0) { load_elf32_uninitialized_memory(phdr.paddr + phdr.filesz, phdr.memsz - phdr.filesz); } cilo_seek(fp, hdr.phoff + sizeof(struct elf32_phdr) * (i + 1), SEEK_SET); } /* assume the entry point is the smallest address we're loading */ printf("Loaded %d bytes.\n", mem_sz); printf("Kicking into Linux.\n"); #ifdef DEBUG printf("hdr.entry = 0x%08x\n", hdr.entry); printf("mem_sz = 0x%08x\n", mem_sz); #endif ((void (*)(uint32_t mem_sz, char *cmd_line))(hdr.entry)) (c_memsz(), cmd_line); return -1; /* something failed, badly */ } /** * load a single ELF section into memory at address. Assumes ELF data is * contiguous in memory. * @param base location (in memory) of the ELF file * @param address address at which the ELF section will be loaded * @param file_offset offset (in bytes) in the ELF file where the section is * @param length Length of the section (in bytes) */ void load_elf64_section(struct file *fp, uint64_t address, uint64_t file_offset, uint64_t length) { uint8_t *elf_loc = (uint8_t *)address; #ifdef DEBUG printf("Init data: %016x length %016x\n", address, length); #endif cilo_seek(fp, (uint32_t)file_offset, SEEK_SET); cilo_read(elf_loc, (uint32_t)length, 1, fp); } /** * Create an uninitialized data (.bss) region of memory. * @param address Start address of this region * @param lenght length of this region */ void load_elf64_uninitialized_memory(uint64_t address, uint64_t length) { int i = 0; uint8_t *p = (uint8_t *)address; #ifdef DEBUG printf("Uninit data: %016x, len %016x\n", address, length); #endif for (i = 0; i < length; i++) { p[i] = 0; } } int load_elf64_file(struct file *fp, char *cmd_line) { struct elf64_hdr hdr; uint32_t mem_sz = 0; cilo_seek(fp, 0, SEEK_SET); /* read in header entries */ cilo_read(&hdr, sizeof(struct elf64_hdr), 1, fp); /* check the file magic */ if (hdr.e_ident[0] != ELF_MAGIC_1 || hdr.e_ident[1] != ELF_MAGIC_2 || hdr.e_ident[2] != ELF_MAGIC_3 || hdr.e_ident[3] != ELF_MAGIC_4) { printf("Bad ELF magic found. Found: %#2x %#2x %#2x %#2x.\n", hdr.e_ident[0], hdr.e_ident[1], hdr.e_ident[2], hdr.e_ident[3]); return -1; } /* check machine class: */ if (!hdr.e_ident[ELF_INDEX_CLASS] == ELF_CLASS_64) { printf("Invalid ELF machine class found. Found: %2x.\n", hdr.e_ident[ELF_INDEX_CLASS]); return -1; } /* check endianess: */ if (hdr.e_ident[ELF_INDEX_DATA] != ELF_DATA_MSB) { printf("Non-big endian ELF file detected. Aborting load.\n"); return -1; } if (hdr.e_ehsize != 52 /* bytes */) { printf("Warning: ELF header greater than 52 bytes found. Found: %u\n", hdr.e_ehsize); } if (hdr.e_phnum == 0) { printf("Found zero segments in ELF file. Aborting load.\n"); return -1; } cilo_seek(fp, hdr.e_phoff, SEEK_SET); struct elf64_phdr phdr; int i; for (i = 0; i < hdr.e_phnum; i++) { cilo_read(&phdr, sizeof(struct elf64_phdr), 1, fp); if (phdr.p_type != ELF_PT_LOAD) continue; load_elf64_section(fp, phdr.p_paddr, phdr.p_offset, phdr.p_filesz); mem_sz += phdr.p_memsz; if (phdr.p_memsz - phdr.p_filesz > 0) { load_elf64_uninitialized_memory(phdr.p_paddr + phdr.p_filesz, phdr.p_memsz - phdr.p_filesz); } } return -1; }