root/sys/amd64/amd64/locore.S
/*-
 * Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
 * All rights reserved.
 *
 * Copyright (c) 2020, 2021 The FreeBSD Foundation
 *
 * Portions of this software were developed by
 * Konstantin Belousov <kib@FreeBSD.org> under sponsorship from
 * the FreeBSD Foundation.
 *
 * 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.
 */

#include <machine/asmacros.h>
#include <machine/psl.h>
#include <machine/pmap.h>
#include <machine/specialreg.h>

#include "assym.inc"

/*
 * Compiled KERNBASE location
 */
        .globl  kernbase, kernload
        .set    kernbase,KERNBASE
        .set    kernload,KERNLOAD

#define BOOTSTACK_SIZE  (PAGE_SIZE * KSTACK_PAGES)

        .text
/**********************************************************************
 *
 * This is where the loader trampoline start us, set the ball rolling...
 *
 * We are called with the stack looking like this:
 * 0(%rsp) = 32 bit return address (cannot be used)
 * 4(%rsp) = 32 bit modulep
 * 8(%rsp) = 32 bit kernend
 *
 * We are already in long mode, on a 64 bit %cs and running at KERNBASE.
 */
ENTRY(btext)

        /* Don't trust what the loader gives for rflags. */
        pushq   $PSL_KERNEL
        popfq

        /* Get onto a stack that we can trust - there is no going back now. */
        movq    %rsp, %rbp
        movq    $bootstack,%rsp

#ifdef KASAN
        /* Bootstrap a shadow map for the boot stack. */
        movq    $bootstack, %rdi
        subq    $BOOTSTACK_SIZE, %rdi
        movq    $BOOTSTACK_SIZE, %rsi
        call    kasan_init_early
#endif

        /* Grab metadata pointers from the loader. */
        movl    4(%rbp),%edi            /* modulep (arg 1) */
        movl    8(%rbp),%esi            /* kernend (arg 2) */
        xorq    %rbp, %rbp

        call    hammer_time             /* set up cpu for unix operation */
        movq    %rax,%rsp               /* set up kstack for mi_startup() */
        call    mi_startup              /* autoconfiguration, mountroot etc */
0:      hlt
        jmp     0b

/*
 * void la57_trampoline(%rdi pml5)
 *
 * Entered in 4-level paging long mode on AP, hopefully returns alive in
 * 5-level paging mode. The parameter is a pointer to a 5-level page
 * table root. The passed 5-level page table, and the current 4-level page
 * table, both must map the trampoline code page 1:1 physical, below 4G.
 * The trampoline must be PIC because it is copied from kernel text into
 * this page.
 *
 * The current paging level cannot be changed while paging is enabled, and
 * paging cannot be disabled while in long mode.  As consequence, code
 * switches into the compat mode, then disables paging to descend into
 * protected mode.  There, the paging level bit CR4.LA57 can be changed,
 * and code directly jumps back into long mode.
 *
 * Falling into the protected mode requires single-purpose GDT entries,
 * which are provided by the private GDT.  It is the caller's responsibility
 * to
 * - restore the GDT and %gsbase after the call
 * - reset IDT back to long mode.
 */
ENTRY(la57_trampoline)
        movq    %rsp,lst(%rip)          /* save registers into memory */
        movq    %rbx,lst+8(%rip)        /* upper halves are not saved .. */
        movq    %rbp,lst+0x10(%rip)     /* by 64->32->64 switch */
        movq    %cr4,%rax
        orq     $CR4_LA57,%rax          /* 5-lvl %cr4 */
        movq    %rax,lst+0x18(%rip)
        leaq    la57_trampoline_end(%rip),%rsp /* priv stack */

        movq    %cr0,%rbp
        leaq    la57_trampoline_gdt(%rip),%rax
        movq    %rax,la57_trampoline_gdt_desc+2(%rip)
        lgdtq   la57_trampoline_gdt_desc(%rip)

        pushq   $(2<<3)
        leaq    l1(%rip),%rax
        leaq    l2(%rip),%rbx

        pushq   %rax
        lretq
        .code32

l1:     movl    $(3<<3),%eax
        movl    %eax,%ss                /* 32bit paged, priv gdt and stack */

        movl    %cr4,%eax
        andl    $~(CR4_PGE | CR4_PCIDE),%eax /* clear sensitive paging ctrls */
        movl    %eax,%cr4

        movl    %ebp,%eax
        andl    $~CR0_PG,%eax           /* protected mode */
        movl    %eax,%cr0

        movl    $MSR_EFER,%ecx          /* disable long mode bit */
        rdmsr                           /* to safer tweaking LA57 */
        andl    $~EFER_LME,%eax
        wrmsr

        movl    %cr4,%eax               /* finally safe to switch bit */
        orl     $CR4_LA57,%eax
        movl    %eax,%cr4

        movl    %edi,%cr3               /* and load the 5-level pgtable root */

        rdmsr
        orl     $EFER_LME,%eax
        wrmsr                           /* prepare for ... */

        movl    %ebp,%cr0               /* and jump back directly into long */
        jmp     1f                      /* mode from protected by enabling pg */

1:      pushl   $(1<<3)                 /* reload %cs */
        pushl   %ebx
        lretl
        .code64

l2:     movq    lst(%rip),%rsp          /* back on C stack */
        movq    lst+8(%rip),%rbx
        movq    lst+0x10(%rip),%rbp
        movq    lst+0x18(%rip),%rax
        movq    %rax,%cr4               /* re-enable paging controls */
        retq                            /* back to C */
        .p2align 4,0
lst:    .quad   0,0,0,0
ENTRY(la57_trampoline_gdt_desc)
        .word   la57_trampoline_end - la57_trampoline_gdt
        .long   0, 0            /* filled by pmap_bootstrap_la57 */
        .p2align 4,0
ENTRY(la57_trampoline_gdt)
        .long   0x00000000      /* null desc */
        .long   0x00000000
        .long   0x00000000      /* 64bit code */
        .long   0x00209800
        .long   0x0000ffff      /* 32bit code */
        .long   0x00cf9b00
        .long   0x0000ffff      /* universal data */
        .long   0x00cf9300
        .dcb.l  16,0
ENTRY(la57_trampoline_end)

        .bss
        .p2align PAGE_SHIFT
        .globl  bootstack
        .space  BOOTSTACK_SIZE          /* space for bootstack - temporary stack */
bootstack:

        .section .note.GNU-stack,"",%progbits