diff options
author | ian <ian@FreeBSD.org> | 2015-05-25 01:06:55 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2015-05-25 01:06:55 +0000 |
commit | e82e4a2984f477320d58c14411c8586e21927e7f (patch) | |
tree | 3a6c2f4b5391fdbd325cc93515716b0cabf36c16 /sys/boot/common/load_elf.c | |
parent | 78deac1ab875086fbd65b24fcda18097e5692921 (diff) | |
download | FreeBSD-src-e82e4a2984f477320d58c14411c8586e21927e7f.zip FreeBSD-src-e82e4a2984f477320d58c14411c8586e21927e7f.tar.gz |
MFC r277962, r277988, r282661, r282727, r282731, r283013, r283035:
Add support for booting relocatable kernels on PowerPC.
Add code to support loading relocatable kernels at offsets that are
not zero.
Move ubldr text section to the start of the output file, so that when you
create a stripped .bin file from it the entry point is the first byte of
the file. (Will allow "load $addr $file ; go $addr" in u-boot.)
Create a relocatable instance of ubldr for ARM (ubldr.bin).
Re-link ubldr when any of its libraries change.
An ARM kernel can be loaded at any 2MB boundary, make ubldr aware of that.
Diffstat (limited to 'sys/boot/common/load_elf.c')
-rw-r--r-- | sys/boot/common/load_elf.c | 86 |
1 files changed, 50 insertions, 36 deletions
diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c index 672c566..edbad05 100644 --- a/sys/boot/common/load_elf.c +++ b/sys/boot/common/load_elf.c @@ -141,22 +141,15 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) * Check to see what sort of module we are. */ kfp = file_findfile(NULL, NULL); - if (ehdr->e_type == ET_DYN) { - /* Looks like a kld module */ - if (kfp == NULL) { - printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); - err = EPERM; - goto oerr; - } - if (strcmp(__elfN(kerneltype), kfp->f_type)) { - printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); - err = EPERM; - goto oerr; - } - /* Looks OK, got ahead */ - ef.kernel = 0; - - } else if (ehdr->e_type == ET_EXEC) { +#ifdef __powerpc__ + /* + * Kernels can be ET_DYN, so just assume the first loaded object is the + * kernel. This assumption will be checked later. + */ + if (kfp == NULL) + ef.kernel = 1; +#endif + if (ef.kernel || ehdr->e_type == ET_EXEC) { /* Looks like a kernel */ if (kfp != NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); @@ -164,16 +157,39 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) goto oerr; } /* - * Calculate destination address based on kernel entrypoint + * Calculate destination address based on kernel entrypoint. + * + * For ARM, the destination address is independent of any values in the + * elf header (an ARM kernel can be loaded at any 2MB boundary), so we + * leave dest set to the value calculated by archsw.arch_loadaddr() and + * passed in to this function. */ - dest = (ehdr->e_entry & ~PAGE_MASK); - if (dest == 0) { +#ifndef __arm__ + if (ehdr->e_type == ET_EXEC) + dest = (ehdr->e_entry & ~PAGE_MASK); +#endif + if ((ehdr->e_entry & ~PAGE_MASK) == 0) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); err = EPERM; goto oerr; } ef.kernel = 1; + } else if (ehdr->e_type == ET_DYN) { + /* Looks like a kld module */ + if (kfp == NULL) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); + err = EPERM; + goto oerr; + } + if (strcmp(__elfN(kerneltype), kfp->f_type)) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); + err = EPERM; + goto oerr; + } + /* Looks OK, got ahead */ + ef.kernel = 0; + } else { err = EFTYPE; goto oerr; @@ -259,7 +275,7 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) ret = 0; firstaddr = lastaddr = 0; ehdr = ef->ehdr; - if (ef->kernel) { + if (ehdr->e_type == ET_EXEC) { #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */ @@ -291,32 +307,30 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) off = 0; #elif defined(__arm__) /* - * The elf headers in some kernels specify virtual addresses in all - * header fields. More recently, the e_entry and p_paddr fields are the - * proper physical addresses. Even when the p_paddr fields are correct, - * the MI code below uses the p_vaddr fields with an offset added for - * loading (doing so is arguably wrong). To make loading work, we need - * an offset that represents the difference between physical and virtual - * addressing. ARM kernels are always linked at 0xCnnnnnnn. Depending - * on the headers, the offset value passed in may be physical or virtual - * (because it typically comes from e_entry), but we always replace - * whatever is passed in with the va<->pa offset. On the other hand, we - * always remove the high-order part of the entry address whether it's - * physical or virtual, because it will be adjusted later for the actual - * physical entry point based on where the image gets loaded. + * The elf headers in arm kernels specify virtual addresses in all + * header fields, even the ones that should be physical addresses. + * We assume the entry point is in the first page, and masking the page + * offset will leave us with the virtual address the kernel was linked + * at. We subtract that from the load offset, making 'off' into the + * value which, when added to a virtual address in an elf header, + * translates it to a physical address. We do the va->pa conversion on + * the entry point address in the header now, so that later we can + * launch the kernel by just jumping to that address. */ - off = -0xc0000000; - ehdr->e_entry &= ~0xf0000000; + off -= ehdr->e_entry & ~PAGE_MASK; + ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off); #endif #else off = 0; /* other archs use direct mapped kernels */ #endif - __elfN(relocation_offset) = off; } ef->off = off; + if (ef->kernel) + __elfN(relocation_offset) = off; + if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); goto out; |