blob: e6fd57a5568d44da4954d9ca9abdef370acac0d6 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Corey Tabaka
// Copyright (c) 2015 Intel Corporation
// Copyright (c) 2016 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <asm.h>
#include <arch/x86/asm.h>
#include <arch/x86/descriptor.h>
#include <arch/x86/mmu.h>
#include <arch/x86/registers.h>
#include <zircon/tls.h>
/* shared code to set up a default 64bit page table structure */
.macro page_table_init
/* Setting the First PML4E with a PDP table reference*/
movl $PHYS(pdp), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(pml4)
/* Setting the First PDPTE with a Page table reference*/
movl $PHYS(pte), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(pdp)
/* point the pml4e at the second high PDP (for -2GB mapping) */
movl $PHYS(pdp_high), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(pml4 + 8*511)
/* point the second pdp at the same low level page table */
movl $PHYS(pte), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(pdp_high + 8*510)
/* map the first 1GB in this table */
movl $PHYS(pte), %esi
movl $0x200, %ecx
xor %eax, %eax
0:
mov %eax, %ebx
shll $21, %ebx
orl $X86_KERNEL_PD_LP_FLAGS, %ebx
movl %ebx, (%esi)
addl $8,%esi
inc %eax
loop 0b
/* set up a linear map of the first 64GB at 0xffffff8000000000 */
movl $PHYS(linear_map_pdp), %esi
movl $32768, %ecx
xor %eax, %eax
/* loop across these page tables, incrementing the address by 2MB */
0:
mov %eax, %ebx
shll $21, %ebx
orl $X86_KERNEL_PD_LP_FLAGS, %ebx # lower word of the entry
movl %ebx, (%esi)
mov %eax, %ebx
shrl $11, %ebx # upper word of the entry
movl %ebx, 4(%esi)
addl $8,%esi
inc %eax
loop 0b
/* point the high pdp at our linear mapping page tables */
movl $PHYS(pdp_high), %esi
movl $64, %ecx
movl $PHYS(linear_map_pdp), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
0:
movl %eax, (%esi)
add $8, %esi
addl $4096, %eax
loop 0b
.endm
/* The magic number passed by a Multiboot-compliant boot loader. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
.section .text.boot, "ax", @progbits
.code32
FUNCTION_LABEL(_multiboot_start)
cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
jne .Lcommon_boot
movl %ebx, PHYS(_multiboot_info)
.Lcommon_boot:
/* load our new gdt by physical pointer */
lgdt PHYS(_gdtr_phys)
/* load our data selectors */
movw $DATA_SELECTOR, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
/* We need to jump to our sane 32 bit CS */
pushl $CODE_SELECTOR
pushl $PHYS(.Lfarjump)
lret
.Lfarjump:
/* zero the bss section */
bss_setup
paging_setup:
/* Preparing 64 bit paging, we will use 2MB pages covering 1GB
* for initial bootstrap, this page table will be 1 to 1
*/
/* Set the PAE bit to enable 64bit paging
* Set PGE to enable global kernel pages
*/
mov %cr4, %eax
or $(X86_CR4_PAE|X86_CR4_PGE), %eax
mov %eax, %cr4
/* Long Mode Enabled at this point */
movl $X86_MSR_IA32_EFER, %ecx
rdmsr
orl $X86_EFER_LME,%eax
wrmsr
/* initialize the default page tables */
page_table_init
/* load the physical pointer to the top level page table */
movl $PHYS(pml4), %eax
mov %eax, %cr3
/* Enabling Paging and from this point we are in
32 bit compatibility mode*/
mov %cr0, %eax
btsl $(31), %eax
mov %eax, %cr0
/* Start using the zircon stack from its physical address. */
mov $PHYS(_kstack + 4096), %esp
/* Using another long jump to be on 64 bit mode
after this we will be on real 64 bit mode */
pushl $CODE_64_SELECTOR /*Need to put it in a the right CS*/
pushl $PHYS(farjump64)
lret
.align 8
.code64
farjump64:
/* branch to our high address */
mov $high_entry, %rax
jmp *%rax
high_entry:
/* zero our kernel segment data registers */
xor %eax, %eax
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
mov %eax, %ss
/* load the high kernel stack */
mov $(_kstack + 4096), %rsp
/* reload the gdtr */
lgdt _gdtr
// Set %gs.base to &bp_percpu. It's statically initialized
// with kernel_unsafe_sp set, so after this it's safe to call
// into C code that might use safe-stack and/or stack-protector.
lea bp_percpu(%rip), %rax
mov %rax, %rdx
shr $32, %rdx
mov $X86_MSR_IA32_GS_BASE, %ecx
wrmsr
/* set up the idt */
mov $_idt_startup, %rdi
call idt_setup
lidt _idtr
/* assign this core CPU# 0 and initialize its per cpu state */
xor %edi, %edi
call x86_init_percpu
// Fill the stack canary with a random value as early as possible.
// This isn't done in x86_init_percpu because the hw_rng_get_entropy
// call would make it eligible for stack-guard checking itself. But
// %gs is not set up yet in the prologue of the function, so it would
// crash if it tried to use the stack-guard.
call choose_stack_guard
// Move it into place.
mov %rcx, %gs:ZX_TLS_STACK_GUARD_OFFSET
// Don't leak that value to other code.
xor %ecx, %ecx
/* call the main module */
call lk_main
0: /* just sit around waiting for interrupts */
hlt /* interrupts will unhalt the processor */
pause
jmp 0b /* so jump back to halt to conserve power */
/* 64bit entry point from a secondary loader */
.align 8
FUNCTION_LABEL(_entry64)
mov %esi, PHYS(_bootdata_base)
/* ensure the stack pointer is sane */
/* This is enough space for the 2x push + lretq below.
* After, we switch to _kstack + 4096.
* But for some reason using _kstack + 4096 here causes
* crashes on some UEFI platforms...
*/
mov $PHYS(farjump64), %rsp
/* zero the bss */
bss_setup
.Lpaging_setup64:
/* initialize the default page tables */
page_table_init
/*
* Set PGE to enable global kernel pages
*/
mov %cr4, %rax
or $(X86_CR4_PGE), %rax
mov %rax, %cr4
/* load the physical pointer to the top level page table */
mov $PHYS(pml4), %rax
mov %rax, %cr3
/* load our gdtr */
lgdt _gdtr
/* long jump to our code selector and the high address */
push $CODE_64_SELECTOR
push $high_entry
lretq