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
This commit is contained in:
Warner Losh 2022-07-11 17:49:11 -06:00
parent a0c075229f
commit 75cbdbc983
10 changed files with 447 additions and 0 deletions

View File

@ -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}

View File

@ -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);
}

View File

@ -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)

View File

@ -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) }
}

View File

View File

@ -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"
);

View File

@ -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];
};

View File

@ -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

View File

@ -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

View File

@ -19,10 +19,15 @@ host_dup(int fd)
return host_syscall(SYS_dup, fd); return host_syscall(SYS_dup, fd);
} }
/* Same system call with different names on different Linux architectures due to history */
int int
host_fstat(int fd, struct host_kstat *sb) host_fstat(int fd, struct host_kstat *sb)
{ {
#ifdef SYS_newfstat
return host_syscall(SYS_newfstat, fd, (uintptr_t)sb); return host_syscall(SYS_newfstat, fd, (uintptr_t)sb);
#else
return host_syscall(SYS_fstat, fd, (uintptr_t)sb);
#endif
} }
int int