diff options
Diffstat (limited to 'lib/elf.c')
-rw-r--r-- | lib/elf.c | 68 |
1 files changed, 64 insertions, 4 deletions
diff --git a/lib/elf.c b/lib/elf.c index 9a794f9cba8..e767a42a3b3 100644 --- a/lib/elf.c +++ b/lib/elf.c @@ -7,6 +7,7 @@ #include <cpu_func.h> #include <elf.h> #include <env.h> +#include <errno.h> #include <net.h> #include <vxworks.h> #ifdef CONFIG_X86 @@ -15,6 +16,59 @@ #include <linux/linkage.h> #endif +/** + * bootelf_exec() - start the ELF image execution. + * + * @entry: address of entry point of ELF. + * + * May by used to allow ports to override the default behavior. + */ +unsigned long bootelf_exec(ulong (*entry)(int, char * const[]), + int argc, char *const argv[]) +{ + return entry(argc, argv); +} + +/** + * bootelf() - Boot ELF from memory. + * + * @addr: Loading address of ELF in memory. + * @flags: Bits like ELF_PHDR to control boot details. + * @argc: May be used to pass command line arguments (maybe unused). + * Necessary for backward compatibility with the CLI command. + * If unused, must be 0. + * @argv: see @argc. If unused, must be NULL. + * Return: Number returned by ELF application. + * + * Sets errno = ENOEXEC if the ELF image is not valid. + */ +unsigned long bootelf(unsigned long addr, Bootelf_flags flags, + int argc, char *const argv[]) +{ + unsigned long entry_addr; + char *args[] = {"", NULL}; + + errno = 0; + + if (!valid_elf_image(addr)) { + errno = ENOEXEC; + return 1; + } + + entry_addr = flags.phdr ? load_elf_image_phdr(addr) + : load_elf_image_shdr(addr); + + if (!flags.autostart) + return 0; + + if (!argc && !argv) { + argc = 1; + argv = args; + } + + return bootelf_exec((void *)entry_addr, argc, argv); +} + /* * A very simple ELF64 loader, assumes the image is valid, returns the * entry point address. @@ -32,10 +86,14 @@ unsigned long load_elf64_image_phdr(unsigned long addr) phdr = (Elf64_Phdr *)(addr + (ulong)ehdr->e_phoff); /* Load each program header */ - for (i = 0; i < ehdr->e_phnum; ++i) { + for (i = 0; i < ehdr->e_phnum; ++i, ++phdr) { void *dst = (void *)(ulong)phdr->p_paddr; void *src = (void *)addr + phdr->p_offset; + /* Only load PT_LOAD program header */ + if (phdr->p_type != PT_LOAD) + continue; + debug("Loading phdr %i to 0x%p (%lu bytes)\n", i, dst, (ulong)phdr->p_filesz); if (phdr->p_filesz) @@ -45,7 +103,6 @@ unsigned long load_elf64_image_phdr(unsigned long addr) phdr->p_memsz - phdr->p_filesz); flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN), roundup(phdr->p_memsz, ARCH_DMA_MINALIGN)); - ++phdr; } if (ehdr->e_machine == EM_PPC64 && (ehdr->e_flags & @@ -147,10 +204,14 @@ unsigned long load_elf_image_phdr(unsigned long addr) phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff); /* Load each program header */ - for (i = 0; i < ehdr->e_phnum; ++i) { + for (i = 0; i < ehdr->e_phnum; ++i, ++phdr) { void *dst = (void *)(uintptr_t)phdr->p_paddr; void *src = (void *)addr + phdr->p_offset; + /* Only load PT_LOAD program header */ + if (phdr->p_type != PT_LOAD) + continue; + debug("Loading phdr %i to 0x%p (%i bytes)\n", i, dst, phdr->p_filesz); if (phdr->p_filesz) @@ -160,7 +221,6 @@ unsigned long load_elf_image_phdr(unsigned long addr) phdr->p_memsz - phdr->p_filesz); flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN), roundup(phdr->p_memsz, ARCH_DMA_MINALIGN)); - ++phdr; } return ehdr->e_entry; |