diff options
author | Warner Losh <imp@FreeBSD.org> | 2022-07-11 23:49:11 +0000 |
---|---|---|
committer | Warner Losh <imp@FreeBSD.org> | 2022-07-28 21:35:42 +0000 |
commit | 75cbdbc9832e72a59bdcb1c307a7a4ea77e29fce (patch) | |
tree | 4e692cf8585945036958ae8300c6f6b7b3140527 | |
parent | a0c075229f19ce1a10836042907e2bd89907aa0b (diff) | |
download | src-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.inc | 12 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/exec.c | 188 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/host_syscall.S | 18 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/ldscript.aarch64 | 72 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/load_addr.c | 0 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/start_arch.h | 36 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/stat_arch.h | 29 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/syscall_nr.h | 20 | ||||
-rw-r--r-- | stand/kboot/arch/aarch64/tramp.S | 67 | ||||
-rw-r--r-- | stand/kboot/host_syscalls.c | 5 |
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 |