svn commit: r355487 - in head/sys: arm64/arm64 arm64/include conf

Michal Meloun mmel at FreeBSD.org
Sat Dec 7 16:14:25 UTC 2019


Author: mmel
Date: Sat Dec  7 16:14:23 2019
New Revision: 355487
URL: https://svnweb.freebsd.org/changeset/base/355487

Log:
  Add support for booting kernel directly from U-Boot using booti command.
  
  In some cases, like is locked bootstrap or device's inability to boot from
  removable media, we cannot use standard boot sequence and is necessary to
  boot kernel directly from U-Boot.
  
  Discussed with:	jhibbits
  MFC after:	1 month
  Differential Revision:	https://reviews.freebsd.org/D13861

Added:
  head/sys/arm64/arm64/machdep_boot.c   (contents, props changed)
Modified:
  head/sys/arm64/arm64/locore.S
  head/sys/arm64/arm64/machdep.c
  head/sys/arm64/include/machdep.h
  head/sys/conf/Makefile.arm64
  head/sys/conf/files.arm64
  head/sys/conf/options.arm64

Modified: head/sys/arm64/arm64/locore.S
==============================================================================
--- head/sys/arm64/arm64/locore.S	Sat Dec  7 15:17:00 2019	(r355486)
+++ head/sys/arm64/arm64/locore.S	Sat Dec  7 16:14:23 2019	(r355487)
@@ -43,6 +43,24 @@
 	.globl	kernbase
 	.set	kernbase, KERNBASE
 
+
+/* U-Boot booti related constants. */
+#if defined(LINUX_BOOT_ABI)
+#define FDT_MAGIC		0xEDFE0DD0	/* FDT blob Magic */
+
+#ifndef UBOOT_IMAGE_OFFSET
+#define	UBOOT_IMAGE_OFFSET	0		/* Image offset from start of */
+#endif						/*  2 MiB page */
+
+#ifndef UBOOT_IMAGE_SIZE			/* Total size of image */
+#define	UBOOT_IMAGE_SIZE	 _end - _start
+#endif
+
+#ifndef UBOOT_IMAGE_FLAGS
+#define	UBOOT_IMAGE_FLAGS	0		/* LE kernel, unspecified */
+#endif						/*  page size */
+#endif /* defined(LINUX_BOOT_ABI) */
+
 /*
  * We assume:
  *  MMU      on with an identity map, or off
@@ -54,6 +72,21 @@
 	.text
 	.globl _start
 _start:
+#if defined(LINUX_BOOT_ABI)
+	/* U-boot image header */
+	b	1f			/* code 0 */
+	.long	0			/* code 1 */
+	.quad	UBOOT_IMAGE_OFFSET	/* Image offset in 2 MiB page, LE */
+	.quad	UBOOT_IMAGE_SIZE	/* Image size, LE */
+	.quad	UBOOT_IMAGE_FLAGS	/* Flags for kernel. LE */
+	.quad 	0			/* Reserved */
+	.quad 	0			/* Reserved */
+	.quad 	0			/* Reserved */
+	.long 	0x644d5241		/* Magic  "ARM\x64", LE */
+	.long 	0			/* Reserved for PE COFF offset*/
+1:
+#endif /* defined(LINUX_BOOT_ABI) */
+
 	/* Drop to EL1 */
 	bl	drop_to_el1
 
@@ -350,11 +383,50 @@ create_pagetables:
 
 	/* Find the size of the kernel */
 	mov	x6, #(KERNBASE)
+
+#if defined(LINUX_BOOT_ABI)
+	/* X19 is used as 'map FDT data' flag */
+	mov	x19, xzr
+
+	/* No modules or FDT pointer ? */
+	cbz	x0, booti_no_fdt
+
+	/* Test if modulep points to modules descriptor or to FDT */
+	ldr	w8, [x0]
+	ldr	w7, =FDT_MAGIC
+	cmp	w7, w8
+	b.eq	booti_fdt
+#endif
+
+	/* Booted with modules pointer */
 	/* Find modulep - begin */
 	sub	x8, x0, x6
 	/* Add two 2MiB pages for the module data and round up */
 	ldr	x7, =(3 * L2_SIZE - 1)
 	add	x8, x8, x7
+	b	common
+
+#if defined(LINUX_BOOT_ABI)
+booti_fdt:
+	/* Booted by U-Boot booti with FDT data */
+	/* Set 'map FDT data' flag */
+	mov	x19, #1
+
+booti_no_fdt:
+	/* Booted by U-Boot booti without FTD data */
+	/* Find the end - begin */
+	ldr     x7, .Lend
+	sub     x8, x7, x6
+
+	/*
+	 * Add one 2MiB page for copy of FDT data (maximum FDT size),
+	 * one for metadata and round up
+	 */
+	ldr	x7, =(3 * L2_SIZE - 1)
+	add	x8, x8, x7
+#endif
+
+common:
 	/* Get the number of l2 pages to allocate, rounded down */
 	lsr	x10, x8, #(L2_SHIFT)
 
@@ -404,9 +476,21 @@ create_pagetables:
 	bl	build_l1_block_pagetable
 #endif
 
-	/*
-	 * Create the VA = PA map
-	 */
+#if defined(LINUX_BOOT_ABI)
+	/* Map FDT data ? */
+	cbz	x19, 1f
+
+	/* Create the identity mapping for FDT data (2 MiB max) */
+	mov	x7, #(ATTR_nG | ATTR_IDX(VM_MEMATTR_UNCACHEABLE))
+	mov	x9, x0
+	mov	x8, x0		/* VA start (== PA start) */
+	mov	x10, #1
+	bl	build_l1_block_pagetable
+
+1:
+#endif
+
+	/* Create the VA = PA map */
 	mov	x7, #(ATTR_nG | ATTR_IDX(VM_MEMATTR_UNCACHEABLE))
 	mov	x9, x27
 	mov	x8, x9		/* VA start (== PA start) */

Modified: head/sys/arm64/arm64/machdep.c
==============================================================================
--- head/sys/arm64/arm64/machdep.c	Sat Dec  7 15:17:00 2019	(r355486)
+++ head/sys/arm64/arm64/machdep.c	Sat Dec  7 16:14:23 2019	(r355487)
@@ -964,6 +964,15 @@ try_load_dtb(caddr_t kmdp)
 	vm_offset_t dtbp;
 
 	dtbp = MD_FETCH(kmdp, MODINFOMD_DTBP, vm_offset_t);
+#if defined(FDT_DTB_STATIC)
+	/*
+	 * In case the device tree blob was not retrieved (from metadata) try
+	 * to use the statically embedded one.
+	 */
+	if (dtbp == 0)
+		dtbp = (vm_offset_t)&fdt_static_dtb;
+#endif
+
 	if (dtbp == (vm_offset_t)NULL) {
 		printf("ERROR loading DTB\n");
 		return;
@@ -974,6 +983,8 @@ try_load_dtb(caddr_t kmdp)
 
 	if (OF_init((void *)dtbp) != 0)
 		panic("OF_init failed with the found device tree");
+
+	parse_fdt_bootargs();
 }
 #endif
 
@@ -1082,26 +1093,18 @@ initarm(struct arm64_bootparams *abp)
 	caddr_t kmdp;
 	bool valid;
 
-	/* Set the module data location */
-	preload_metadata = (caddr_t)(uintptr_t)(abp->modulep);
+	/* Parse loader or FDT boot parametes. Determine last used address. */
+	lastaddr = parse_boot_param(abp);
 
 	/* Find the kernel address */
 	kmdp = preload_search_by_type("elf kernel");
 	if (kmdp == NULL)
 		kmdp = preload_search_by_type("elf64 kernel");
 
-	boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
-	init_static_kenv(MD_FETCH(kmdp, MODINFOMD_ENVP, char *), 0);
 	link_elf_ireloc(kmdp);
-
-#ifdef FDT
 	try_load_dtb(kmdp);
-#endif
 
 	efi_systbl_phys = MD_FETCH(kmdp, MODINFOMD_FW_HANDLE, vm_paddr_t);
-
-	/* Find the address to start allocating from */
-	lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t);
 
 	/* Load the physical memory ranges */
 	efihdr = (struct efi_map_header *)preload_search_info(kmdp,

Added: head/sys/arm64/arm64/machdep_boot.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/arm64/machdep_boot.c	Sat Dec  7 16:14:23 2019	(r355487)
@@ -0,0 +1,254 @@
+/*-
+ * Copyright (c) 2004 Olivier Houchard
+ * Copyright (c) 1994-1998 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * 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 "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+#include <sys/linker.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#ifdef FDT
+#include <sys/boot.h>
+#endif
+
+#include <machine/cpu.h>
+#include <machine/machdep.h>
+#include <machine/metadata.h>
+#include <machine/vmparam.h>
+
+#ifdef FDT
+#include <contrib/libfdt/libfdt.h>
+#include <dev/fdt/fdt_common.h>
+#endif
+
+extern int *end;
+static char *loader_envp;
+static char static_kenv[4096];
+
+
+#ifdef FDT
+#define	CMDLINE_GUARD "FreeBSD:"
+#define	LBABI_MAX_COMMAND_LINE 512
+static char linux_command_line[LBABI_MAX_COMMAND_LINE + 1];
+#endif
+
+/*
+ * Fake up a boot descriptor table
+ */
+ #define PRELOAD_PUSH_VALUE(type, value) do {		\
+	*(type *)(preload_ptr + size) = (value);	\
+	size += sizeof(type);				\
+} while (0)
+
+ #define PRELOAD_PUSH_STRING(str) do {			\
+ 	uint32_t ssize;					\
+ 	ssize = strlen(str) + 1;			\
+ 	PRELOAD_PUSH_VALUE(uint32_t, ssize);		\
+	strcpy((char*)(preload_ptr + size), str);	\
+	size += ssize;					\
+	size = roundup(size, sizeof(u_long));		\
+} while (0)
+
+
+/* Build minimal set of metatda. */
+static vm_offset_t
+fake_preload_metadata(void *dtb_ptr, size_t dtb_size)
+{
+#ifdef DDB
+	vm_offset_t zstart = 0, zend = 0;
+#endif
+	vm_offset_t lastaddr;
+	static char fake_preload[256];
+	caddr_t preload_ptr;
+	size_t size;
+
+	preload_ptr = (caddr_t)&fake_preload[0];
+	size = 0;
+
+	PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME);
+	PRELOAD_PUSH_STRING("kernel");
+
+	PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE);
+	PRELOAD_PUSH_STRING("elf kernel");
+
+	PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR);
+	PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t));
+	PRELOAD_PUSH_VALUE(uint64_t, VM_MIN_KERNEL_ADDRESS);
+
+	PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE);
+	PRELOAD_PUSH_VALUE(uint32_t, sizeof(size_t));
+	PRELOAD_PUSH_VALUE(uint64_t, (size_t)(&end - VM_MIN_KERNEL_ADDRESS));
+#ifdef DDB
+	if (*(uint64_t *)VM_MIN_KERNEL_ADDRESS == MAGIC_TRAMP_NUMBER) {
+		PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA|MODINFOMD_SSYM);
+		PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t));
+		PRELOAD_PUSH_VALUE(uint64_t,
+		    *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 4));
+
+		PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_ESYM);
+		PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t));
+		PRELOAD_PUSH_VALUE(uint64_t,
+		    *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 8));
+
+		lastaddr = *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 8);
+		zend = lastaddr;
+		zstart = *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 4);
+		db_fetch_ksymtab(zstart, zend);
+	} else
+#endif
+		lastaddr = (vm_offset_t)&end;
+	if (dtb_ptr != NULL) {
+		/* Copy DTB to KVA space and insert it into module chain. */
+		lastaddr = roundup(lastaddr, sizeof(int));
+		PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_DTBP);
+		PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t));
+		PRELOAD_PUSH_VALUE(uint64_t, (uint64_t)lastaddr);
+		memmove((void *)lastaddr, dtb_ptr, dtb_size);
+		lastaddr += dtb_size;
+		lastaddr = roundup(lastaddr, sizeof(int));
+	}
+	/* End marker */
+	PRELOAD_PUSH_VALUE(uint32_t, 0);
+	PRELOAD_PUSH_VALUE(uint32_t, 0);
+
+	preload_metadata = (caddr_t)(uintptr_t)fake_preload;
+
+	init_static_kenv(NULL, 0);
+
+	return (lastaddr);
+}
+
+#ifdef FDT
+
+/* Convert the U-Boot command line into FreeBSD kenv and boot options. */
+static void
+cmdline_set_env(char *cmdline, const char *guard)
+{
+	size_t guard_len;
+
+	/* Skip leading spaces. */
+	while (isspace(*cmdline))
+		cmdline++;
+
+	/* Test and remove guard. */
+	if (guard != NULL && guard[0] != '\0') {
+		guard_len  =  strlen(guard);
+		if (strncasecmp(cmdline, guard, guard_len) != 0)
+			return;
+		cmdline += guard_len;
+	}
+
+	boothowto |= boot_parse_cmdline(cmdline);
+}
+
+void
+parse_fdt_bootargs(void)
+{
+
+	if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line,
+	    LBABI_MAX_COMMAND_LINE) == 0) {
+		init_static_kenv(static_kenv, sizeof(static_kenv));
+		cmdline_set_env(linux_command_line, CMDLINE_GUARD);
+	}
+}
+
+#endif
+
+#if defined(LINUX_BOOT_ABI) && defined(FDT)
+static vm_offset_t
+linux_parse_boot_param(struct arm64_bootparams *abp)
+{
+	struct fdt_header *dtb_ptr;
+	size_t dtb_size;
+
+	if (abp->modulep == 0)
+		return (0);
+	/* Test if modulep point to valid DTB. */
+	dtb_ptr = (struct fdt_header *)abp->modulep;
+	if (fdt_check_header(dtb_ptr) != 0)
+		return (0);
+	dtb_size = fdt_totalsize(dtb_ptr);
+	return (fake_preload_metadata(dtb_ptr, dtb_size));
+}
+
+#endif
+
+static vm_offset_t
+freebsd_parse_boot_param(struct arm64_bootparams *abp)
+{
+	vm_offset_t lastaddr = 0;
+	void *kmdp;
+#ifdef DDB
+	vm_offset_t ksym_start;
+	vm_offset_t ksym_end;
+#endif
+
+	if (abp->modulep == 0)
+		return (0);
+
+	preload_metadata = (caddr_t)(uintptr_t)(abp->modulep);
+	kmdp = preload_search_by_type("elf kernel");
+	if (kmdp == NULL)
+		return (0);
+
+	boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
+	loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
+	init_static_kenv(static_kenv, 0);
+	lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t);
+#ifdef DDB
+	ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
+	ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
+	db_fetch_ksymtab(ksym_start, ksym_end);
+#endif
+	return (lastaddr);
+}
+
+vm_offset_t
+parse_boot_param(struct arm64_bootparams *abp)
+{
+	vm_offset_t lastaddr;
+
+#if defined(LINUX_BOOT_ABI) && defined(FDT)
+	lastaddr = linux_parse_boot_param(abp);
+	if (lastaddr != 0)
+		return (lastaddr);
+#endif
+	lastaddr = freebsd_parse_boot_param(abp);
+	if (lastaddr != 0)
+		return (lastaddr);
+
+	/* Fall back to hardcoded metadata. */
+	lastaddr = fake_preload_metadata(NULL, 0);
+
+	return (lastaddr);
+}

Modified: head/sys/arm64/include/machdep.h
==============================================================================
--- head/sys/arm64/include/machdep.h	Sat Dec  7 15:17:00 2019	(r355486)
+++ head/sys/arm64/include/machdep.h	Sat Dec  7 16:14:23 2019	(r355487)
@@ -47,6 +47,10 @@ extern enum arm64_bus arm64_bus_method;
 
 void dbg_init(void);
 void initarm(struct arm64_bootparams *);
+vm_offset_t parse_boot_param(struct arm64_bootparams *abp);
+#ifdef FDT
+void parse_fdt_bootargs(void);
+#endif
 extern void (*pagezero)(void *);
 
 #endif /* _MACHINE_MACHDEP_H_ */

Modified: head/sys/conf/Makefile.arm64
==============================================================================
--- head/sys/conf/Makefile.arm64	Sat Dec  7 15:17:00 2019	(r355486)
+++ head/sys/conf/Makefile.arm64	Sat Dec  7 16:14:23 2019	(r355487)
@@ -27,10 +27,24 @@ S=	../../..
 
 INCLUDES+= -I$S/contrib/libfdt
 
+#SYSTEM_LD:= ${SYSTEM_LD:$S/conf/ldscript.$M=ldscript.$M}
+#SYSTEM_DEP:= ${SYSTEM_DEP:$S/conf/ldscript.$M=ldscript.$M}
+
 .if !empty(DDB_ENABLED)
 CFLAGS += -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer
 .endif
 
+SYSTEM_LD_ = ${LD} -m ${LD_EMULATION} -Bdynamic -T ldscript.$M.noheader \
+	${_LDFLAGS} --no-warn-mismatch --warn-common --export-dynamic \
+	--dynamic-linker /red/herring \
+	-o ${FULLKERNEL}.noheader -X ${SYSTEM_OBJS} vers.o
+SYSTEM_LD_TAIL +=;sed s/" + SIZEOF_HEADERS"// $(LDSCRIPT)\
+		>ldscript.$M.noheader;\
+		${SYSTEM_LD_}; \
+		${OBJCOPY} -S -O binary ${FULLKERNEL}.noheader \
+		${KERNEL_KO}.bin; \
+		rm ${FULLKERNEL}.noheader
+
 %BEFORE_DEPEND
 
 %OBJS
@@ -42,6 +56,7 @@ CFLAGS += -fno-omit-frame-pointer -mno-omit-leaf-frame
 %FILES.m
 
 %CLEAN
+CLEAN+=	ldscript.$M ${KERNEL_KO}.bin ldscript.$M.noheader
 
 %RULES
 

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64	Sat Dec  7 15:17:00 2019	(r355486)
+++ head/sys/conf/files.arm64	Sat Dec  7 16:14:23 2019	(r355487)
@@ -156,6 +156,7 @@ arm64/arm64/identcpu.c		standard
 arm64/arm64/in_cksum.c		optional	inet | inet6
 arm64/arm64/locore.S		standard	no-obj
 arm64/arm64/machdep.c		standard
+arm64/arm64/machdep_boot.c	standard
 arm64/arm64/mem.c		standard
 arm64/arm64/memcpy.S		standard
 arm64/arm64/memmove.S		standard

Modified: head/sys/conf/options.arm64
==============================================================================
--- head/sys/conf/options.arm64	Sat Dec  7 15:17:00 2019	(r355486)
+++ head/sys/conf/options.arm64	Sat Dec  7 16:14:23 2019	(r355487)
@@ -6,6 +6,7 @@ SOCDEV_PA			opt_global.h
 SOCDEV_VA			opt_global.h
 THUNDERX_PASS_1_1_ERRATA	opt_global.h
 VFP				opt_global.h
+LINUX_BOOT_ABI			opt_global.h
 
 # Binary compatibility
 COMPAT_FREEBSD32		opt_global.h


More information about the svn-src-head mailing list