/*- * Copyright (c) 2022 Netflix, Inc * * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ /* * 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 amd64, the kernel starts all * the APs so we don't have to worry about them here. */ /* * Keep in sync with elf64_freebsd.c. Kexec starts tramp w/o any parameters, so * store them here. This is constructed to be a useful stack: * * struct trampoline_data { * // %rsp points here on start and we pop the args off and then retq to 'entry' * uint64_t memmap_src; // Linux-provided memory map PA * uint64_t memmap_dst; // Module data copy PA * uint64_t memmap_len; // Length to copy * uint64_t pt4; // Page table address to pop * uint64_t entry; // return address to jump to kernel * uint32_t fill1; // 0 * uint32_t modulep; // 4 module metadata * uint32_t kernend; // 8 kernel end * uint32_t fill2; // 12 * }; * * loader.kboot will construct a stack that btext expects, which is arguments on * the stack, not in registers, and these args are 32-bit not 64. The extra stuff * is data the trampoline code consumes. * * Processor is already in long mode when we're called, paging is enabled and * boot loader loads things such that: * - kernel mapped at KERNBASE, aligned to 2MB, below 4GB, contiguous memory * - %cr3 tells us our PA later in boot, so we install it before jumping * to the kernel. * - there is a 2M hole at KERNBASE (KERNSTART = KERNBASE + 2M) * - kernel is mapped with 2M superpages * - The kernel, modules and metadata is in first 4GB which is unity mapped * - There's additional memory after loader provided data for early allocations * * Unlike coming directly from loader.efi, we don't support copying the staging * area. We tell Linux to land the kernel in its final location with the needed * alignment, etc. We copy the trampoline code to 1MB offset above KERNBASE * since that memory is otherwise free and safely above the lower 1MB swamp we * inherited from IBM PC, though this code makes no assumptions about where that * might be. * * Thus, the trampoline just needs to set %rsp to that stack pop the systab * patch value, pop the %cr3 value, set it and then retq to jump to the kernel * with its stack args filled in. Since the handoff to this code used to be * from 32-bit code, it uses the i386 calling conventions which put the * arguments on the stack. The kernel's btext routine expects this setup. */ .text .globl tramp tramp: cli /* Make sure we don't get interrupted. */ cld /* Copy in a sane direction */ leaq stack_start(%rip), %rsp /* Setup our pre-filled-in stack */ /* * If we have a EFI memory map, copy it over. These data are always * on the stack, so we pop them all off before testing to skip the copy. */ popq %rsi /* memmap_src */ popq %rdi /* memmap_dst */ popq %rcx /* memmap_size */ testq %rsi, %rsi je no_map_copy rep movsb /* Make the copy */ no_map_copy: popq %rax /* Pop off the PT4 ptr for %cr3 */ movq %rax, %cr3 /* set the page table */ retq /* Return addr and args already on stack */ /* * The following is the stack for the above code. The stack will increase in * address as things are popped off of it, so we start with the stack pointing * to tramp_pt4. */ .p2align 3 /* Stack has to be 8 byte aligned */ trampoline_data: stack_start: /* %rsp at start. */ tramp_memmap_src: .quad 0 /* SRC PA (data from Linux) */ tramp_memmap_dst: .quad 0 /* DST PA (data to FreeBSD's metadata */ tramp_memmap_len: .quad 0 /* Length */ tramp_pt4: .quad 0 /* New %cr3 value */ tramp_entry: .quad 0 /* Entry to kernel (btext) */ /* %rsp points here on entry to amd64 kernel's btext */ .long 0 /* 0 filler, ignored (current loaders set to 0) */ tramp_modulep: .long 0 /* 4 moudlep */ tramp_kernend: .long 0 /* 8 kernend */ .long 0 /* 12 alignment filler (also 0) */ tramp_end: .data .type tramp_size,@object .globl tramp_size tramp_size: .long tramp_end-tramp .size tramp_size, 4 .type tramp_data_offset,@object .globl tramp_data_offset tramp_data_offset: .long trampoline_data-tramp .size tramp_data_offset, 4 .section .note.GNU-stack,"",%progbits