aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2022-07-11 23:49:11 +0000
committerWarner Losh <imp@FreeBSD.org>2022-07-28 21:35:42 +0000
commit75cbdbc9832e72a59bdcb1c307a7a4ea77e29fce (patch)
tree4e692cf8585945036958ae8300c6f6b7b3140527
parenta0c075229f19ce1a10836042907e2bd89907aa0b (diff)
downloadsrc-75cbdbc9832e72a59bdcb1c307a7a4ea77e29fce.tar.gz
src-75cbdbc9832e72a59bdcb1c307a7a4ea77e29fce.zip
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
-rw-r--r--stand/kboot/arch/aarch64/Makefile.inc12
-rw-r--r--stand/kboot/arch/aarch64/exec.c188
-rw-r--r--stand/kboot/arch/aarch64/host_syscall.S18
-rw-r--r--stand/kboot/arch/aarch64/ldscript.aarch6472
-rw-r--r--stand/kboot/arch/aarch64/load_addr.c0
-rw-r--r--stand/kboot/arch/aarch64/start_arch.h36
-rw-r--r--stand/kboot/arch/aarch64/stat_arch.h29
-rw-r--r--stand/kboot/arch/aarch64/syscall_nr.h20
-rw-r--r--stand/kboot/arch/aarch64/tramp.S67
-rw-r--r--stand/kboot/host_syscalls.c5
10 files changed, 447 insertions, 0 deletions
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
--- /dev/null
+++ b/stand/kboot/arch/aarch64/load_addr.c
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