Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 25 May 2015 01:06:55 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r283505 - in stable/10/sys/boot: arm/uboot common uboot/common uboot/lib
Message-ID:  <201505250106.t4P16tCn061384@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Mon May 25 01:06:55 2015
New Revision: 283505
URL: https://svnweb.freebsd.org/changeset/base/283505

Log:
  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.

Added:
  stable/10/sys/boot/common/self_reloc.c
     - copied unchanged from r282727, head/sys/boot/common/self_reloc.c
Modified:
  stable/10/sys/boot/arm/uboot/Makefile
  stable/10/sys/boot/arm/uboot/ldscript.arm
  stable/10/sys/boot/arm/uboot/start.S
  stable/10/sys/boot/common/load_elf.c
  stable/10/sys/boot/uboot/common/main.c
  stable/10/sys/boot/uboot/lib/copy.c
  stable/10/sys/boot/uboot/lib/elf_freebsd.c
  stable/10/sys/boot/uboot/lib/libuboot.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/boot/arm/uboot/Makefile
==============================================================================
--- stable/10/sys/boot/arm/uboot/Makefile	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/arm/uboot/Makefile	Mon May 25 01:06:55 2015	(r283505)
@@ -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 ${.C
 	cat ${.ALLSRC} | \
 	    awk -f ${.CURDIR}/../../common/merge_help.awk > ${.TARGET}
 
-${PROG}: ldscript.generated ${.CURDIR}/ldscript.${MACHINE_CPUARCH}
+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}
 
-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
+CLEANFILES+=	ldscript.abs ldscript.pie ubldr ubldr.pie ubldr.bin
 
 .if !defined(LOADER_ONLY)
 .PATH: ${.CURDIR}/../../forth

Modified: stable/10/sys/boot/arm/uboot/ldscript.arm
==============================================================================
--- stable/10/sys/boot/arm/uboot/ldscript.arm	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/arm/uboot/ldscript.arm	Mon May 25 01:06:55 2015	(r283505)
@@ -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*) }

Modified: stable/10/sys/boot/arm/uboot/start.S
==============================================================================
--- stable/10/sys/boot/arm/uboot/start.S	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/arm/uboot/start.S	Mon May 25 01:06:55 2015	(r283505)
@@ -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()
  */

Modified: stable/10/sys/boot/common/load_elf.c
==============================================================================
--- stable/10/sys/boot/common/load_elf.c	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/common/load_elf.c	Mon May 25 01:06:55 2015	(r283505)
@@ -141,22 +141,15 @@ __elfN(loadfile)(char *filename, u_int64
      * 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
 	    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 
     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 
 	    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;

Copied: stable/10/sys/boot/common/self_reloc.c (from r282727, head/sys/boot/common/self_reloc.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/10/sys/boot/common/self_reloc.c	Mon May 25 01:06:55 2015	(r283505, copy of r282727, head/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);
+	}
+}

Modified: stable/10/sys/boot/uboot/common/main.c
==============================================================================
--- stable/10/sys/boot/uboot/common/main.c	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/uboot/common/main.c	Mon May 25 01:06:55 2015	(r283505)
@@ -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;

Modified: stable/10/sys/boot/uboot/lib/copy.c
==============================================================================
--- stable/10/sys/boot/uboot/lib/copy.c	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/uboot/lib/copy.c	Mon May 25 01:06:55 2015	(r283505)
@@ -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));
 }

Modified: stable/10/sys/boot/uboot/lib/elf_freebsd.c
==============================================================================
--- stable/10/sys/boot/uboot/lib/elf_freebsd.c	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/uboot/lib/elf_freebsd.c	Mon May 25 01:06:55 2015	(r283505)
@@ -80,7 +80,7 @@ __elfN(uboot_exec)(struct preloaded_file
 	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();

Modified: stable/10/sys/boot/uboot/lib/libuboot.h
==============================================================================
--- stable/10/sys/boot/uboot/lib/libuboot.h	Mon May 25 00:30:26 2015	(r283504)
+++ stable/10/sys/boot/uboot/lib/libuboot.h	Mon May 25 01:06:55 2015	(r283505)
@@ -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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505250106.t4P16tCn061384>