/* * 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 to 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 * uint64_t memmap_src; // 16 Linux-provided memory map PA * uint64_t memmap_dst; // 24 Module data copy PA * uint64_t memmap_len; // 32 Length to copy * }; * * FreeBSD's arm64 entry point is _start which 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 * * The rest of the boot loader tells Linux to land the kernel in its final * location with the needed alignment, etc. It does this, and then we take over. * * The linux kernel will helpfully turn off the MMU, flush the caches, disables * them, etc. It calls the tramp with two args: FDT blob address in x0 and the * EL2 vectors in x1. Currently, we make use of neither of these parameters: we * pass whatever dtb we think we need as part of the module data and we're a bit * weak on hypervisor support at the moment. _start's requirements are all * satisifed. * * This trampoline sets up the arguments the kernel expects and jumps to the * kernel _start address. We pass the modulep pointer in x0, as _start * expects. We assume that the various cache flushing, invalidation, etc that * linux did during or after copying the data down is sufficient, though we may * need to be mindful of cache flushing if we run in EL2 (TBD). * * Note, if TRAMP_MEMMAP_SRC is non-zero, then we have to copy the Linux * provided UEFI memory map. It's easier to do that here. In kboot we couldn't * access the physical memory, and it's a chicken and egg problem later in the * kernel. */ #define TRAMP_ENTRY 0 #define TRAMP_MODULEP 8 #define TRAMP_MEMMAP_SRC 16 #define TRAMP_MEMMAP_DST 24 #define TRAMP_MEMMAP_LEN 32 #define TRAMP_TOTAL 40 .text .globl tramp tramp: adr x8, trampoline_data ldr x10, [x8, #TRAMP_MEMMAP_SRC] cmp x10, xzr b.eq 9f /* * Copy over the memory map into area we have reserved for it. Assume * the copy is a multiple of 8, since we know table entries are made up * of several 64-bit quantities. */ ldp x11, x12, [x8, #TRAMP_MEMMAP_DST] /* x12 = len */ 1: ldr x13, [x10], #8 str x13, [x11], #8 subs x12, x12, #8 b.hi 1b 9: ldp x9, x0, [x8, #TRAMP_ENTRY] /* x0 = modulep */ br x9 .p2align 4 trampoline_data: .space TRAMP_TOTAL #define TMPSTACKSIZE 48 /* 16 bytes for args +8 for pushq/popfq + 24 spare */ .space TMPSTACKSIZE tramp_end: /* padding doubles as stack */ .data .globl tramp_size tramp_size: .long tramp_end-tramp .globl tramp_data_offset tramp_data_offset: .long trampoline_data-tramp .section .note.GNU-stack,"",%progbits