summaryrefslogtreecommitdiff
path: root/lib/elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/elf.c')
-rw-r--r--lib/elf.c68
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;