summaryrefslogtreecommitdiffstats
path: root/sys/boot
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2015-05-25 01:06:55 +0000
committerian <ian@FreeBSD.org>2015-05-25 01:06:55 +0000
commite82e4a2984f477320d58c14411c8586e21927e7f (patch)
tree3a6c2f4b5391fdbd325cc93515716b0cabf36c16 /sys/boot
parent78deac1ab875086fbd65b24fcda18097e5692921 (diff)
downloadFreeBSD-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')
-rw-r--r--sys/boot/arm/uboot/Makefile40
-rw-r--r--sys/boot/arm/uboot/ldscript.arm18
-rw-r--r--sys/boot/arm/uboot/start.S48
-rw-r--r--sys/boot/common/load_elf.c86
-rw-r--r--sys/boot/common/self_reloc.c123
-rw-r--r--sys/boot/uboot/common/main.c9
-rw-r--r--sys/boot/uboot/lib/copy.c113
-rw-r--r--sys/boot/uboot/lib/elf_freebsd.c2
-rw-r--r--sys/boot/uboot/lib/libuboot.h5
9 files changed, 347 insertions, 97 deletions
diff --git a/sys/boot/arm/uboot/Makefile b/sys/boot/arm/uboot/Makefile
index 8f718be..b4c5463 100644
--- a/sys/boot/arm/uboot/Makefile
+++ b/sys/boot/arm/uboot/Makefile
@@ -2,7 +2,8 @@
.include <bsd.own.mk>
-PROG= ubldr
+FILES= ubldr ubldr.bin
+
NEWVERSWHAT= "U-Boot loader" ${MACHINE_ARCH}
BINDIR?= /boot
INSTALLFLAGS= -b
@@ -12,7 +13,7 @@ WARNS?= 1
UBLDR_LOADADDR?= 0x1000000
# Architecture-specific loader code
-SRCS= start.S conf.c vers.c
+SRCS= start.S conf.c self_reloc.c vers.c
.if !defined(LOADER_NO_DISK_SUPPORT)
LOADER_DISK_SUPPORT?= yes
@@ -93,9 +94,7 @@ CLEANFILES+= vers.c loader.help
CFLAGS+= -ffreestanding -msoft-float
-LDFLAGS= -nostdlib -static
-LDFLAGS+= -T ldscript.generated
-LDFLAGS+= -T ${.CURDIR}/ldscript.${MACHINE_CPUARCH}
+LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.${MACHINE_CPUARCH}
# Pull in common loader code
.PATH: ${.CURDIR}/../../uboot/common
@@ -116,6 +115,8 @@ NO_WERROR.clang=
DPADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} ${LIBSTAND}
LDADD= ${LIBFICL} ${LIBUBOOT} ${LIBFDT} ${LIBUBOOT_FDT} -lstand
+OBJS+= ${SRCS:N*.h:R:S/$/.o/g}
+
vers.c: ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version
sh ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version ${NEWVERSWHAT}
@@ -123,17 +124,24 @@ loader.help: help.common help.uboot ${.CURDIR}/../../fdt/help.fdt
cat ${.ALLSRC} | \
awk -f ${.CURDIR}/../../common/merge_help.awk > ${.TARGET}
-${PROG}: ldscript.generated ${.CURDIR}/ldscript.${MACHINE_CPUARCH}
-
-ldscript.generated::
- rm -f ldscript.generated.tmp
- echo "UBLDR_LOADADDR = ${UBLDR_LOADADDR};" >ldscript.generated.tmp
- if diff ldscript.generated ldscript.generated.tmp > /dev/null; then \
- true; \
- else \
- rm -f ldscript.generated; \
- mv ldscript.generated.tmp ldscript.generated; \
- fi
+ldscript.abs:
+ echo "UBLDR_LOADADDR = ${UBLDR_LOADADDR};" >${.TARGET}
+
+ldscript.pie:
+ echo "UBLDR_LOADADDR = 0;" >${.TARGET}
+
+ubldr: ${OBJS} ldscript.abs ${.CURDIR}/ldscript.${MACHINE_CPUARCH} ${DPADD}
+ ${CC} ${CFLAGS} -T ldscript.abs ${LDFLAGS} \
+ -o ${.TARGET} ${OBJS} ${LDADD}
+
+ubldr.pie: ${OBJS} ldscript.pie ${.CURDIR}/ldscript.${MACHINE_CPUARCH} ${DPADD}
+ ${CC} ${CFLAGS} -T ldscript.pie ${LDFLAGS} -pie -Wl,-Bsymbolic \
+ -o ${.TARGET} ${OBJS} ${LDADD}
+
+ubldr.bin: ubldr.pie
+ ${OBJCOPY} -S -O binary ubldr.pie ${.TARGET}
+
+CLEANFILES+= ldscript.abs ldscript.pie ubldr ubldr.pie ubldr.bin
.if !defined(LOADER_ONLY)
.PATH: ${.CURDIR}/../../forth
diff --git a/sys/boot/arm/uboot/ldscript.arm b/sys/boot/arm/uboot/ldscript.arm
index b3be119..1eb10a8 100644
--- a/sys/boot/arm/uboot/ldscript.arm
+++ b/sys/boot/arm/uboot/ldscript.arm
@@ -6,6 +6,15 @@ SECTIONS
{
/* Read-only sections, merged into text segment: */
. = UBLDR_LOADADDR + SIZEOF_HEADERS;
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
@@ -32,15 +41,6 @@ SECTIONS
.rela.sbss : { *(.rela.sbss) }
.rela.sdata2 : { *(.rela.sdata2) }
.rela.sbss2 : { *(.rela.sbss2) }
- .text :
- {
- *(.text)
- /* .gnu.warning sections are handled specially by elf32.em. */
- *(.gnu.warning)
- *(.gnu.linkonce.t*)
- } =0
- _etext = .;
- PROVIDE (etext = .);
.init : { *(.init) } =0
.fini : { *(.fini) } =0
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
diff --git a/sys/boot/arm/uboot/start.S b/sys/boot/arm/uboot/start.S
index 6e8f117..1ef21f3 100644
--- a/sys/boot/arm/uboot/start.S
+++ b/sys/boot/arm/uboot/start.S
@@ -29,12 +29,38 @@
#include <machine/asm.h>
#include <machine/armreg.h>
+ .text
+ .extern _C_LABEL(self_reloc), _C_LABEL(main)
+ .weak _DYNAMIC
+
/*
* Entry point to the loader that U-Boot passes control to.
*/
- .text
.globl _start
_start:
+
+#ifdef _ARM_ARCH_6
+ mrc p15, 0, ip, c1, c0, 0
+ orr ip, ip, #(CPU_CONTROL_UNAL_ENABLE)
+ orr ip, ip, #(CPU_CONTROL_AFLT_ENABLE)
+ mcr p15, 0, ip, c1, c0, 0
+#endif
+ /*
+ * Do self-relocation when the weak external symbol _DYNAMIC is non-NULL.
+ * When linked as a dynamic relocatable file, the linker automatically
+ * defines _DYNAMIC with a value that is the offset of the dynamic
+ * relocation info section.
+ * Note that we're still on u-boot's stack here, but the self_reloc
+ * code uses only a couple dozen bytes of stack space.
+ */
+ adr ip, .here_off /* .here_off is a symbol whose value */
+ ldr r0, [ip] /* is its own offset in the text seg. */
+ sub r0, ip, r0 /* Get its pc-relative address and */
+ ldr r1, .dynamic_off /* subtract its value and we get */
+ teq r1, #0 /* r0 = physaddr we were loaded at. */
+ addne r1, r1, r0 /* r1 = dynamic section physaddr. */
+ blne _C_LABEL(self_reloc) /* Do reloc if _DYNAMIC is non-NULL. */
+
/* Hint where to look for the API signature */
ldr ip, =uboot_address
str sp, [ip]
@@ -44,16 +70,20 @@ _start:
str r8, [ip, #0]
str r9, [ip, #4]
-#ifdef _ARM_ARCH_6
- mrc p15, 0, r2, c1, c0, 0
- orr r2, r2, #(CPU_CONTROL_UNAL_ENABLE)
- orr r2, r2, #(CPU_CONTROL_AFLT_ENABLE)
- mcr p15, 0, r2, c1, c0, 0
-#endif
-
- /* Start loader */
+ /*
+ * Start loader. This is basically a tail-recursion call; if main()
+ * returns, it returns to u-boot (which reports the value returned r0).
+ */
b main
+ /*
+ * Data for self-relocation, in the text segment for pc-rel access.
+ */
+.here_off:
+ .word .
+.dynamic_off:
+ .word _DYNAMIC
+
/*
* syscall()
*/
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;
diff --git a/sys/boot/common/self_reloc.c b/sys/boot/common/self_reloc.c
new file mode 100644
index 0000000..9864a48
--- /dev/null
+++ b/sys/boot/common/self_reloc.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2008-2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <elf.h>
+#include <bootstrap.h>
+
+#if defined(__aarch64__)
+#define ElfW_Rel Elf64_Rela
+#define ElfW_Dyn Elf64_Dyn
+#define ELFW_R_TYPE ELF64_R_TYPE
+#define ELF_RELA
+#elif defined(__arm__) || defined(__i386__)
+#define ElfW_Rel Elf32_Rel
+#define ElfW_Dyn Elf32_Dyn
+#define ELFW_R_TYPE ELF32_R_TYPE
+#elif defined(__amd64__)
+#define ElfW_Rel Elf64_Rel
+#define ElfW_Dyn Elf64_Dyn
+#define ELFW_R_TYPE ELF64_R_TYPE
+#else
+#error architecture not supported
+#endif
+#if defined(__aarch64__)
+#define RELOC_TYPE_NONE R_AARCH64_NONE
+#define RELOC_TYPE_RELATIVE R_AARCH64_RELATIVE
+#elif defined(__amd64__)
+#define RELOC_TYPE_NONE R_X86_64_NONE
+#define RELOC_TYPE_RELATIVE R_X86_64_RELATIVE
+#elif defined(__arm__)
+#define RELOC_TYPE_NONE R_ARM_NONE
+#define RELOC_TYPE_RELATIVE R_ARM_RELATIVE
+#elif defined(__i386__)
+#define RELOC_TYPE_NONE R_386_NONE
+#define RELOC_TYPE_RELATIVE R_386_RELATIVE
+#endif
+
+/*
+ * A simple elf relocator.
+ */
+void
+self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic)
+{
+ Elf_Word relsz, relent;
+ Elf_Addr *newaddr;
+ ElfW_Rel *rel;
+ ElfW_Dyn *dynp;
+
+ /*
+ * Find the relocation address, its size and the relocation entry.
+ */
+ relsz = 0;
+ relent = 0;
+ for (dynp = dynamic; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ case DT_RELA:
+ rel = (ElfW_Rel *)(dynp->d_un.d_ptr + baseaddr);
+ break;
+ case DT_RELSZ:
+ case DT_RELASZ:
+ relsz = dynp->d_un.d_val;
+ break;
+ case DT_RELENT:
+ case DT_RELAENT:
+ relent = dynp->d_un.d_val;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Perform the actual relocation.
+ */
+ for (; relsz > 0; relsz -= relent) {
+ switch (ELFW_R_TYPE(rel->r_info)) {
+ case RELOC_TYPE_NONE:
+ /* No relocation needs be performed. */
+ break;
+
+ case RELOC_TYPE_RELATIVE:
+ /* Address relative to the base address. */
+ newaddr = (Elf_Addr *)(rel->r_offset + baseaddr);
+ *newaddr += baseaddr;
+ /* Add the addend when the ABI uses them */
+#ifdef ELF_RELA
+ *newaddr += rel->r_addend;
+#endif
+ break;
+ default:
+ /* XXX: do we need other relocations ? */
+ break;
+ }
+ rel = (ElfW_Rel *) ((caddr_t) rel + relent);
+ }
+}
diff --git a/sys/boot/uboot/common/main.c b/sys/boot/uboot/common/main.c
index 919b541..b830354 100644
--- a/sys/boot/uboot/common/main.c
+++ b/sys/boot/uboot/common/main.c
@@ -28,6 +28,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/param.h>
#include <stand.h>
@@ -44,6 +45,9 @@ struct uboot_devdesc currdev;
struct arch_switch archsw; /* MI/MD interface boundary */
int devs_no;
+uintptr_t uboot_heap_start;
+uintptr_t uboot_heap_end;
+
struct device_type {
const char *name;
int type;
@@ -414,7 +418,9 @@ main(void)
* Initialise the heap as early as possible. Once this is done,
* alloc() is usable. The stack is buried inside us, so this is safe.
*/
- setheap((void *)end, (void *)(end + 512 * 1024));
+ uboot_heap_start = round_page((uintptr_t)end);
+ uboot_heap_end = uboot_heap_start + 512 * 1024;
+ setheap((void *)uboot_heap_start, (void *)uboot_heap_end);
/*
* Set up console.
@@ -487,6 +493,7 @@ main(void)
setenv("LINES", "24", 1); /* optional */
setenv("prompt", "loader>", 1);
+ archsw.arch_loadaddr = uboot_loadaddr;
archsw.arch_getdev = uboot_getdev;
archsw.arch_copyin = uboot_copyin;
archsw.arch_copyout = uboot_copyout;
diff --git a/sys/boot/uboot/lib/copy.c b/sys/boot/uboot/lib/copy.c
index 3adf7eb..bb658e3 100644
--- a/sys/boot/uboot/lib/copy.c
+++ b/sys/boot/uboot/lib/copy.c
@@ -27,66 +27,131 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/param.h>
#include <stand.h>
#include <stdint.h>
#include "api_public.h"
#include "glue.h"
+#include "libuboot.h"
/*
* MD primitives supporting placement of module data
*/
-void *
-uboot_vm_translate(vm_offset_t o) {
+#ifdef __arm__
+#define KERN_ALIGN (2 * 1024 * 1024)
+#else
+#define KERN_ALIGN PAGE_SIZE
+#endif
+
+/*
+ * Avoid low memory, u-boot puts things like args and dtb blobs there.
+ */
+#define KERN_MINADDR max(KERN_ALIGN, (1024 * 1024))
+
+extern void _start(void); /* ubldr entry point address. */
+
+/*
+ * This is called for every object loaded (kernel, module, dtb file, etc). The
+ * expected return value is the next address at or after the given addr which is
+ * appropriate for loading the given object described by type and data. On each
+ * call the addr is the next address following the previously loaded object.
+ *
+ * The first call is for loading the kernel, and the addr argument will be zero,
+ * and we search for a big block of ram to load the kernel and modules.
+ *
+ * On subsequent calls the addr will be non-zero, and we just round it up so
+ * that each object begins on a page boundary.
+ */
+uint64_t
+uboot_loadaddr(u_int type, void *data, uint64_t addr)
+{
struct sys_info *si;
- static uintptr_t start = 0;
- static size_t size = 0;
+ uintptr_t sblock, eblock, subldr, eubldr;
+ uintptr_t biggest_block, this_block;
+ size_t biggest_size, this_size;
int i;
+ char * envstr;
+
+ if (addr == 0) {
+ /*
+ * If the loader_kernaddr environment variable is set, blindly
+ * honor it. It had better be right. We force interpretation
+ * of the value in base-16 regardless of any leading 0x prefix,
+ * because that's the U-Boot convention.
+ */
+ envstr = ub_env_get("loader_kernaddr");
+ if (envstr != NULL)
+ return (strtoul(envstr, NULL, 16));
- if (size == 0) {
+ /*
+ * Find addr/size of largest DRAM block. Carve our own address
+ * range out of the block, because loading the kernel over the
+ * top ourself is a poor memory-conservation strategy. Avoid
+ * memory at beginning of the first block of physical ram,
+ * since u-boot likes to pass args and data there. Assume that
+ * u-boot has moved itself to the very top of ram and
+ * optimistically assume that we won't run into it up there.
+ */
if ((si = ub_get_sys_info()) == NULL)
panic("could not retrieve system info");
- /* Find start/size of largest DRAM block. */
+ biggest_block = 0;
+ biggest_size = 0;
+ subldr = rounddown2((uintptr_t)_start, KERN_ALIGN);
+ eubldr = roundup2(uboot_heap_end, KERN_ALIGN);
for (i = 0; i < si->mr_no; i++) {
- if (si->mr[i].flags == MR_ATTR_DRAM
- && si->mr[i].size > size) {
- start = si->mr[i].start;
- size = si->mr[i].size;
+ if (si->mr[i].flags != MR_ATTR_DRAM)
+ continue;
+ sblock = roundup2(si->mr[i].start, KERN_ALIGN);
+ eblock = rounddown2(si->mr[i].start + si->mr[i].size,
+ KERN_ALIGN);
+ if (biggest_size == 0)
+ sblock += KERN_MINADDR;
+ if (subldr >= sblock && subldr < eblock) {
+ if (subldr - sblock > eblock - eubldr) {
+ this_block = sblock;
+ this_size = subldr - sblock;
+ } else {
+ this_block = eubldr;
+ this_size = eblock - eubldr;
+ }
+ }
+ if (biggest_size < this_size) {
+ biggest_block = this_block;
+ biggest_size = this_size;
}
}
-
- if (size <= 0)
- panic("No suitable DRAM?\n");
- /*
- printf("Loading into memory region 0x%08X-0x%08X (%d MiB)\n",
- start, start + size, size / 1024 / 1024);
- */
+ if (biggest_size == 0)
+ panic("Not enough DRAM to load kernel\n");
+#if 0
+ printf("Loading kernel into region 0x%08x-0x%08x (%u MiB)\n",
+ biggest_block, biggest_block + biggest_size - 1,
+ biggest_size / 1024 / 1024);
+#endif
+ return (biggest_block);
}
- if (o > size)
- panic("Address offset 0x%08jX bigger than size 0x%08X\n",
- (intmax_t)o, size);
- return (void *)(start + o);
+ return roundup2(addr, PAGE_SIZE);
}
ssize_t
uboot_copyin(const void *src, vm_offset_t dest, const size_t len)
{
- bcopy(src, uboot_vm_translate(dest), len);
+ bcopy(src, (void *)dest, len);
return (len);
}
ssize_t
uboot_copyout(const vm_offset_t src, void *dest, const size_t len)
{
- bcopy(uboot_vm_translate(src), dest, len);
+ bcopy((void *)src, dest, len);
return (len);
}
ssize_t
uboot_readin(const int fd, vm_offset_t dest, const size_t len)
{
- return (read(fd, uboot_vm_translate(dest), len));
+ return (read(fd, (void *)dest, len));
}
diff --git a/sys/boot/uboot/lib/elf_freebsd.c b/sys/boot/uboot/lib/elf_freebsd.c
index 6b828d3..b72d07c 100644
--- a/sys/boot/uboot/lib/elf_freebsd.c
+++ b/sys/boot/uboot/lib/elf_freebsd.c
@@ -80,7 +80,7 @@ __elfN(uboot_exec)(struct preloaded_file *fp)
if ((error = md_load(fp->f_args, &mdp)) != 0)
return (error);
- entry = uboot_vm_translate(e->e_entry);
+ entry = (void *)e->e_entry;
printf("Kernel entry at 0x%x...\n", (unsigned)entry);
dev_cleanup();
diff --git a/sys/boot/uboot/lib/libuboot.h b/sys/boot/uboot/lib/libuboot.h
index 79005f2..e4201d8 100644
--- a/sys/boot/uboot/lib/libuboot.h
+++ b/sys/boot/uboot/lib/libuboot.h
@@ -57,7 +57,10 @@ extern int devs_no;
extern struct netif_driver uboot_net;
extern struct devsw uboot_storage;
-void *uboot_vm_translate(vm_offset_t);
+extern uintptr_t uboot_heap_start;
+extern uintptr_t uboot_heap_end;
+
+uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr);
ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len);
ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len);
ssize_t uboot_readin(const int fd, vm_offset_t dest, const size_t len);
OpenPOWER on IntegriCloud