root/stand/kboot/kboot/arch/aarch64/tramp.S
/*
 * 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