git: 75cbdbc9832e - main - kboot: aarch64 support

From: Warner Losh <imp_at_FreeBSD.org>
Date: Thu, 28 Jul 2022 21:37:44 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=75cbdbc9832e72a59bdcb1c307a7a4ea77e29fce

commit 75cbdbc9832e72a59bdcb1c307a7a4ea77e29fce
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2022-07-11 23:49:11 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2022-07-28 21:35:42 +0000

    kboot: aarch64 support
    
    Add support for aarch64. exec.c and ldscript are copied from the EFI
    version with #ifdefs for the differences. Once complete, I'll refactor
    them. host_syscall.S implements a generic system call. tramp.S is a
    first attempt to create a tramoline that we can use to jump to the
    aarch64 kernel. Add aarch64-specific startup and stat files as well.
    exec.c tweaked slightly to avoid bringing in bi_load(), which will come
    in later. Includes tweaks to stat due to name differences between names
    on different Linux architectures.
    
    Sponsored by:           Netflix
---
 stand/kboot/arch/aarch64/Makefile.inc     |  12 ++
 stand/kboot/arch/aarch64/exec.c           | 188 ++++++++++++++++++++++++++++++
 stand/kboot/arch/aarch64/host_syscall.S   |  18 +++
 stand/kboot/arch/aarch64/ldscript.aarch64 |  72 ++++++++++++
 stand/kboot/arch/aarch64/load_addr.c      |   0
 stand/kboot/arch/aarch64/start_arch.h     |  36 ++++++
 stand/kboot/arch/aarch64/stat_arch.h      |  29 +++++
 stand/kboot/arch/aarch64/syscall_nr.h     |  20 ++++
 stand/kboot/arch/aarch64/tramp.S          |  67 +++++++++++
 stand/kboot/host_syscalls.c               |   5 +
 10 files changed, 447 insertions(+)

diff --git a/stand/kboot/arch/aarch64/Makefile.inc b/stand/kboot/arch/aarch64/Makefile.inc
new file mode 100644
index 000000000000..5573d264ec4e
--- /dev/null
+++ b/stand/kboot/arch/aarch64/Makefile.inc
@@ -0,0 +1,12 @@
+SRCS+=		host_syscall.S tramp.S exec.c load_addr.c
+
+.PATH:	${BOOTSRC}/arm64/libarm64
+CFLAGS+=-I${BOOTSRC}/arm64/libarm64
+SRCS+=	cache.c
+
+CFLAGS+=	-I${SYSDIR}/contrib/dev/acpica/include
+# load address. set in linker script
+RELOC?=		0x0
+CFLAGS+=	-DRELOC=${RELOC}
+
+LDFLAGS=	-nostdlib -static -T ${.CURDIR}/arch/${MACHINE_ARCH}/ldscript.${MACHINE_ARCH}
diff --git a/stand/kboot/arch/aarch64/exec.c b/stand/kboot/arch/aarch64/exec.c
new file mode 100644
index 000000000000..56a206c0f09f
--- /dev/null
+++ b/stand/kboot/arch/aarch64/exec.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2006 Marcel Moolenaar
+ * 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 ``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 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 <stand.h>
+#include <string.h>
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <machine/elf.h>
+
+#include <bootstrap.h>
+
+#ifdef EFI
+#include <efi.h>
+#include <efilib.h>
+
+#include "loader_efi.h"
+
+#endif
+
+#include "bootstrap.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+#include "cache.h"
+
+#ifdef EFI
+static EFI_GUID acpi_guid = ACPI_TABLE_GUID;
+static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID;
+#endif
+
+static int elf64_exec(struct preloaded_file *amp);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+/* Stub out temporarily */
+static int
+bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp,
+    bool exit_bs)
+{
+	return EINVAL;
+}
+
+static struct file_format arm64_elf = {
+	elf64_loadfile,
+	elf64_exec
+};
+
+struct file_format *file_formats[] = {
+	&arm64_elf,
+	NULL
+};
+
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+	vm_offset_t modulep, kernendp;
+	vm_offset_t clean_addr;
+	size_t clean_size;
+	struct file_metadata *md;
+	Elf_Ehdr *ehdr;
+	void (*entry)(vm_offset_t);
+	int err;
+#ifdef EFI
+	ACPI_TABLE_RSDP *rsdp;
+	char buf[24];
+	int revision;
+#endif
+	/*
+	 * Report the RSDP to the kernel. The old code used the 'hints' method
+	 * to communite this to the kernel. However, while convenient, the
+	 * 'hints' method is fragile and does not work when static hints are
+	 * compiled into the kernel. Instead, move to setting different tunables
+	 * that start with acpi. The old 'hints' can be removed before we branch
+	 * for FreeBSD 15.
+	 */
+#ifdef EFI
+	rsdp = efi_get_table(&acpi20_guid);
+	if (rsdp == NULL) {
+		rsdp = efi_get_table(&acpi_guid);
+	}
+	if (rsdp != NULL) {
+		sprintf(buf, "0x%016llx", (unsigned long long)rsdp);
+		setenv("hint.acpi.0.rsdp", buf, 1);
+		setenv("acpi.rsdp", buf, 1);
+		revision = rsdp->Revision;
+		if (revision == 0)
+			revision = 1;
+		sprintf(buf, "%d", revision);
+		setenv("hint.acpi.0.revision", buf, 1);
+		setenv("acpi.revision", buf, 1);
+		strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+		buf[sizeof(rsdp->OemId)] = '\0';
+		setenv("hint.acpi.0.oem", buf, 1);
+		setenv("acpi.oem", buf, 1);
+		sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress);
+		setenv("hint.acpi.0.rsdt", buf, 1);
+		setenv("acpi.rsdt", buf, 1);
+		if (revision >= 2) {
+			/* XXX extended checksum? */
+			sprintf(buf, "0x%016llx",
+			    (unsigned long long)rsdp->XsdtPhysicalAddress);
+			setenv("hint.acpi.0.xsdt", buf, 1);
+			setenv("acpi.xsdt", buf, 1);
+			sprintf(buf, "%d", rsdp->Length);
+			setenv("hint.acpi.0.xsdt_length", buf, 1);
+			setenv("acpi.xsdt_length", buf, 1);
+		}
+	}
+#else
+#endif
+
+	if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+        	return(EFTYPE);
+
+	ehdr = (Elf_Ehdr *)&(md->md_data);
+#ifdef EFI
+	entry = efi_translate(ehdr->e_entry);
+
+	efi_time_fini();
+#else
+	entry = (void *)ehdr->e_entry;
+#endif
+	err = bi_load(fp->f_args, &modulep, &kernendp, true);
+	if (err != 0) {
+#ifdef EFI
+		efi_time_init();
+#endif
+		return (err);
+	}
+
+	dev_cleanup();
+
+	/* Clean D-cache under kernel area and invalidate whole I-cache */
+#ifdef EFI
+	clean_addr = (vm_offset_t)efi_translate(fp->f_addr);
+	clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr;
+#else
+	clean_addr = (vm_offset_t)fp->f_addr;
+	clean_size = (vm_offset_t)kernendp - clean_addr;
+#endif
+
+	cpu_flush_dcache((void *)clean_addr, clean_size);
+	cpu_inval_icache();
+
+	(*entry)(modulep);
+
+	panic("exec returned");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+
+	printf("%s called for preloaded file %p (=%s):\n", __func__, fp,
+	    fp->f_name);
+	return (ENOSYS);
+}
+
diff --git a/stand/kboot/arch/aarch64/host_syscall.S b/stand/kboot/arch/aarch64/host_syscall.S
new file mode 100644
index 000000000000..db3ecf0f885d
--- /dev/null
+++ b/stand/kboot/arch/aarch64/host_syscall.S
@@ -0,0 +1,18 @@
+#include <machine/asm.h>
+
+/*
+ * Emulate the Linux system call interface. System call number in x8.
+ * Args in x0, x1, x2, x3, x4 and x5. Return in x0.
+ */
+ENTRY(host_syscall)
+	mov	x8, x0
+	mov	x0, x1
+	mov	x1, x2
+	mov	x2, x3
+	mov	x3, x4
+	mov	x4, x5
+	mov	x5, x6
+	svc	0
+	ret
+/* Note: We're exposing the raw return value to the caller */
+END(host_syscall)
diff --git a/stand/kboot/arch/aarch64/ldscript.aarch64 b/stand/kboot/arch/aarch64/ldscript.aarch64
new file mode 100644
index 000000000000..d7107fe8c18a
--- /dev/null
+++ b/stand/kboot/arch/aarch64/ldscript.aarch64
@@ -0,0 +1,72 @@
+/* $FreeBSD$ */
+OUTPUT_FORMAT("elf64-aarch64", "elf64-aarch64", "elf64-aarch64")
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x401000;
+  ImageBase = .;
+  .hash : { *(.hash) }  /* this MUST come first! */
+  . = ALIGN(4096);
+  .eh_frame :
+  {
+    *(.eh_frame)
+  }
+  . = ALIGN(4096);
+  .text		: {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em. */
+    *(.gnu.warning)
+    *(.plt)
+  } =0xCCCCCCCC
+  . = ALIGN(4096);
+  .data		: {
+    *(.rodata .rodata.* .gnu.linkonce.r.*)
+    *(.rodata1)
+    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+    *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
+    *(.opd)
+    *(.data .data.* .gnu.linkonce.d.*)
+    *(.data1)
+    *(.plabel)
+    *(.dynbss)
+    *(.bss .bss.* .gnu.linkonce.b.*)
+    *(COMMON)
+  }
+  . = ALIGN(4096);
+  set_Xcommand_set	: {
+    __start_set_Xcommand_set = .;
+    *(set_Xcommand_set)
+    __stop_set_Xcommand_set = .;
+  }
+  set_Xficl_compile_set	: {
+    __start_set_Xficl_compile_set = .;
+    *(set_Xficl_compile_set)
+    __stop_set_Xficl_compile_set = .;
+  }
+  . = ALIGN(4096);
+  __gp = .;
+  .sdata	: {
+    *(.got.plt .got)
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+    *(dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+  }
+  . = ALIGN(4096);
+  .dynamic	: { *(.dynamic) }
+  . = ALIGN(4096);
+  .rela.dyn	: {
+    *(.rela.data*)
+    *(.rela.got)
+    *(.rela.stab)
+    *(.relaset_*)
+  }
+  . = ALIGN(4096);
+  .reloc	: { *(.reloc) }
+  . = ALIGN(4096);
+  .dynsym	: { *(.dynsym) }
+  . = ALIGN(4096);
+  .dynstr	: { *(.dynstr) }
+}
diff --git a/stand/kboot/arch/aarch64/load_addr.c b/stand/kboot/arch/aarch64/load_addr.c
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/stand/kboot/arch/aarch64/start_arch.h b/stand/kboot/arch/aarch64/start_arch.h
new file mode 100644
index 000000000000..467ba054c9f0
--- /dev/null
+++ b/stand/kboot/arch/aarch64/start_arch.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022, Netflix, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * Provides a _start routine that calls a _start_c routine that takes a pointer
+ * to the stack as documented in crt1.c. We skip the pointer to _DYNAMIC since
+ * we don't support dynamic libraries, at all. And while _start_c is our own
+ * thing and doesn't have a second arg, we comport to the calling conventions
+ * that glibc and musl have by passing x1 as 0 for the dynamic pointer. We
+ * likely could call main directly with only a few more lines of code, but this
+ * is simple enough and concentrates all the expressable in C stuff there.  We
+ * also generate eh_frames should we need to debug things (it doesn't change the
+ * genreated code, but leaves enough breadcrumbs to keep gdb happy)
+ */
+
+__asm__(
+".text\n"		/* ENTRY(_start) -- can't expand and stringify, so by hand */
+".align 2\n"
+".global _start\n"
+".type _start, #function\n"
+"_start:\n"
+".cfi_startproc\n"
+/*
+ * Linux zeros all registers so x29 (frame pointer) and x30 (link register) are 0.
+ */
+"	mov	x0, sp\n"	/* Pointer to argc, etc kernel left on the stack */
+"	and	sp, x0, #-16\n"	/* Align stack to 16-byte boundary */
+"	b	_start_c\n"	/* Our MI code takes it from here */
+/* NORETURN */
+".ltorg\n"		/* END(_start) */
+".cfi_endproc\n"
+".size _start, .-_start\n"
+);
diff --git a/stand/kboot/arch/aarch64/stat_arch.h b/stand/kboot/arch/aarch64/stat_arch.h
new file mode 100644
index 000000000000..2462caed8682
--- /dev/null
+++ b/stand/kboot/arch/aarch64/stat_arch.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2005-2020 Rich Felker, et al.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Note: From the musl project
+ */
+
+struct host_kstat {
+	host_dev_t st_dev;
+	host_ino_t st_ino;
+	host_mode_t st_mode;
+	host_nlink_t st_nlink;
+	host_uid_t st_uid;
+	host_gid_t st_gid;
+	host_dev_t st_rdev;
+	unsigned long __pad;
+	host_off_t st_size;
+	host_blksize_t st_blksize;
+	int __pad2;
+	host_blkcnt_t st_blocks;
+	long st_atime_sec;
+	long st_atime_nsec;
+	long st_mtime_sec;
+	long st_mtime_nsec;
+	long st_ctime_sec;
+	long st_ctime_nsec;
+	unsigned __pad_for_future[2];
+};
diff --git a/stand/kboot/arch/aarch64/syscall_nr.h b/stand/kboot/arch/aarch64/syscall_nr.h
new file mode 100644
index 000000000000..511d1fa8b2a7
--- /dev/null
+++ b/stand/kboot/arch/aarch64/syscall_nr.h
@@ -0,0 +1,20 @@
+#define SYS_close		 57
+#define	SYS_dup			 23
+#define SYS_fstat		 80
+#define SYS_getdents64		 61
+#define	SYS_getpid		172
+#define SYS_gettimeofday	169
+#define SYS_lseek		 62
+#define SYS_kexec_load		104
+#define	SYS_mkdirat		 34
+#define SYS_mmap		222
+#define SYS_mount		 40
+#define	SYS_munmap		215
+#define SYS_newfstatat		 79
+#define SYS_openat		 56
+#define SYS_pselect6		 72
+#define SYS_read		 63
+#define SYS_reboot		142
+#define SYS_symlinkat		 36
+#define SYS_uname		160
+#define SYS_write		 64
diff --git a/stand/kboot/arch/aarch64/tramp.S b/stand/kboot/arch/aarch64/tramp.S
new file mode 100644
index 000000000000..1edb6823bdc9
--- /dev/null
+++ b/stand/kboot/arch/aarch64/tramp.S
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2022, Netflix, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * This is the trampoline that starts the FreeBSD kernel. Since the Linux kernel
+ * calls this routine with no args, and has a different environment than the boot
+ * loader provides and that the kernel expects, this code is responsible for setting
+ * all that up and calling the normal kernel entry point. It's analogous ot the
+ * "purgatory" code in the linux kernel. Details about these operations are
+ * contained in comments below. On aarch64, the kernel will start all the APs so
+ * we don't have to worry about them here.
+ */
+
+/*
+ * Keep in sync with exec.c. Kexec starts aarch64_tramp w/o any
+ * parameters, so store them here.
+ *
+ * struct trampoline_data {
+ *	uint64_t	entry;			//  0 (PA where kernel loaded)
+ *	uint64_t	modulep;		//  8 module metadata
+ * };
+ *
+ * The aarch64 _start routine assumes:
+ *  MMU      on with an identity map, or off
+ *  D-Cache: off
+ *  I-Cache: on or off
+ *  We are loaded at a 2MiB aligned address
+ *  Module data (modulep) pointer in x0
+ *
+ * Unlike EFI, we don't support copying the staging area. We tell Linunx to land
+ * the kernel in its final location with the needed alignment, etc.
+ *
+ * This trampoline installs sets up the arguments the kernel expects, flushes
+ * the cache lines and jumps to the kernel _start address. We pass the modulep
+ * pointer in x0, as _start expects.
+ */
+	.text
+	.globl	aarch64_tramp
+aarch64_tramp:
+	b	1f		/* skip over our saved args */
+	.p2align	3
+trampoline_data:
+#define TRAMP_ENTRY	0
+#define TRAMP_MODULEP	8
+#define TRAMP_TOTAL	16
+	.space TRAMP_TOTAL
+#define TMPSTACKSIZE	48	/* 16 bytes for args +8 for pushq/popfq + 24 spare */
+1:
+	adr	x2, trampoline_data
+	ldr	x1, [x2, #TRAMP_ENTRY]
+	ldr	x0, [x2, #TRAMP_MODULEP]
+	br	x1
+
+	.p2align 4
+	.space	TMPSTACKSIZE
+aarch64_tramp_end:			/* padding doubles as stack */
+
+	.data
+	.globl	aarch64_tramp_size
+aarch64_tramp_size:
+	.long	aarch64_tramp_end-aarch64_tramp
+	.globl	aarch64_tramp_data_offset
+aarch64_tramp_data_offset:
+	.long	trampoline_data-aarch64_tramp
diff --git a/stand/kboot/host_syscalls.c b/stand/kboot/host_syscalls.c
index 771f9e128fdd..52371021f282 100644
--- a/stand/kboot/host_syscalls.c
+++ b/stand/kboot/host_syscalls.c
@@ -19,10 +19,15 @@ host_dup(int fd)
 	return host_syscall(SYS_dup, fd);
 }
 
+/* Same system call with different names on different Linux architectures due to history */
 int
 host_fstat(int fd, struct host_kstat *sb)
 {
+#ifdef SYS_newfstat
 	return host_syscall(SYS_newfstat, fd, (uintptr_t)sb);
+#else
+	return host_syscall(SYS_fstat, fd, (uintptr_t)sb);
+#endif
 }
 
 int