You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

347 lines
8.7 KiB
ArmAsm

/* See the end of this file for copyright and license terms. */
#include <asm/common.h>
#include <arch/multiboot.h>
#include <arch/page.h>
#include <arch/segment.h>
/*
* Early boot sequence on amd64.
*
* This is loosely based on the 32-bit example code from the OSDev.org wiki:
* <https://wiki.osdev.org/Higher_Half_x86_Bare_Bones>
* mixed with some of the Long Mode tutorial code:
* <https://wiki.osdev.org/Setting_Up_Long_Mode>
*
* When entering from the bootloader, we are still in 32-bit protected mode,
* meaning we have to enable long mode ourselves.
*
* Our basic setup procedure is:
* - set up the stack (unlike on i386, where we do it only as the last step)
* - perform some sanity checks, like whether the CPU actually supports 64 bits
* - load the GDT at its *physical* address and use its 32-bit segments
* - populate the page maps and %cr3 with the 4-level 64-bit structures:
* + the lowest 1GB (including the entire kernel image) is identity mapped
* + the same mapping is mirrored to KERNBASE (0xffffffff80000000)
* + the page tables are mapped recursively at X86_PMAP_OFFSET
* - enable IA-32e mode by setting IA32_EFER.LME = 1 (see Intel SDM vol 4, tbl 2-2)
* - enable paging and jump to the higher half mapping
* - update rsp and rbp to also point to the high address
* - reload the GDT at its higher half address
* - discard the identity mapping of low memory
* - call _boot()
*/
.code32 /* we enter in 32-bit protected mode */
/* from kernel64.ld */
.extern _image_start_phys
.extern _image_end_phys
.extern _boot /* main boot routine -- see ./boot.c */
/* initial page maps -- see ../mm/amd64/page.c */
.extern _pml4
.extern _pdp0
/* GDT stuff -- see ../mm/segment.S */
.extern _x86_gdt_desc
.extern _x86_gdt_desc_phys
.extern _x86_kern_tss
.extern _x86_user_tss
.section .multiboot.text, "ax", @progbits
/* C code is linked against high memory, this gets the physical address */
#define PADDR(c_symbol) (c_symbol - KERNBASE)
/*
* miscellaneous utility routines
*/
/* void fix_tss_base(u64 *gdt_entry, struct x86_tss *tss) */
L_ENTRY(fix_tss_base)
movl 4(%esp), %edi
movl 8(%esp), %eax
movw %ax, 2(%edi)
shrl $16, %eax
movb %al, 4(%edi)
movb %ah, 7(%edi)
ret
L_END(fix_tss_base)
/* void err_and_hlt(const char *message) */
L_ENTRY(err_and_hlt)
mov $0x000b8000, %edx
mov 4(%esp), %ecx
movb $0x4f, %ah /* BIOS color code: white text on red background */
1: movb (%ecx), %al
testb %al, %al
jz 2f
movw %ax, (%edx)
addl $2, %edx
incl %ecx
jmp 1b
2: cli /* interrupts should already be off, but it won't hurt */
hlt
jmp 2b
L_END(err_and_hlt)
/*
* actual setup routine
*/
ENTRY(_setup)
/*
* set up the stack
*/
movl $PADDR(stack_top), %esp
pushl $0 /* CPU number -- see include/arch/smp.h */
pushl $0 /* two longwords to keep the stack 8 byte aligned */
movl %esp, %ebp
push %ebx /* temporarily stash multiboot tag address away */
/*
* the bootloader should have loaded the multiboot magic
* value into eax, check if that is really the case
*/
cmp $MB2_BOOTLOADER_MAGIC, %eax
je 1f
pushl $errmsg_no_multiboot
jmp err_and_hlt
/*
* check if the CPU supports the CPUID instruction
* this is done by checking whether we can flip bit 21 in EFLAGS (??)
*/
1: pushf
pop %eax
movl %eax, %ecx
xorl $(1 << 21), %eax
push %eax
popf
pushf
pop %eax
push %ecx /* restore original flags */
popf
cmp %eax, %ecx
jne 2f
pushl $errmsg_no_cpuid
jmp err_and_hlt
/*
* check if the CPU supports extended CPUID addresses
*/
2: mov $0x80000000, %eax
cpuid
cmp $0x80000001, %eax
jae 3f
pushl $errmsg_no_ext_cpuid
jmp err_and_hlt
/*
* check if the CPU supports IA-32e mode
*/
3: mov $0x80000001, %eax
cpuid
test $(1 << 29), %edx
jnz 4f
pushl $errmsg_no_ia32e
jmp err_and_hlt
/*
* load the base values kernel and user TSS into the corresponding GDT entries
*/
4: pushl $PADDR(_x86_kern_tss)
pushl $PADDR(_x86_gdt + X86_KERN_TSS)
call fix_tss_base
pushl $PADDR(_x86_user_tss)
pushl $PADDR(_x86_gdt + X86_USER_TSS)
call fix_tss_base
addl $16, %esp
/*
* load our own GDT (at its physical address) and its 32-bit segments
*/
lgdt _x86_gdt_desc_phys
ljmp $(X86_32_KERN_CS), $1f
1: movl $(X86_KERN_DS), %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
/* XXX do we really need to load a TSS? */
movl $X86_KERN_TSS, %eax
ltr %ax
#if (KERNBASE % (1 << X86_PDP_SHIFT)) != 0
#error "KERNBASE must be aligned to at least a PDP entry (1 GB)"
#endif
#if (X86_PMAP_OFFSET % (1 << X86_PML4_SHIFT)) != 0
#error "X86_PMAP_OFFSET must be aligned to at least a PML4 entry (512 GB)"
#endif
#define V48 0xffff000000000000
#define PDP_OFFSET(ptr) (( (((ptr) - V48) >> X86_PDP_SHIFT) % 512 ) * 8)
#define PML4_OFFSET(ptr) ( ((ptr) - V48) >> (X86_PML4_SHIFT) * 8 )
/*
* statically map the low 2 GB to itself and to the high kernel half
*/
/* for the identity mapping */
movl $0x00000083, PADDR(_pdp0) /* present (0), write (1), huge (7) */
movl $0x40000083, PADDR(_pdp0 + 8)
/* for the -2GB at the end of virtual memory (we use the same PDP for both) */
movl $0x00000083, PADDR(_pdp0 + PDP_OFFSET(KERNBASE))
movl $0x40000083, PADDR(_pdp0 + PDP_OFFSET(KERNBASE) + 8)
movl $PADDR(_pdp0 + 0x003), PADDR(_pml4) /* present (0), write (1), huge (7) */
movl $PADDR(_pdp0 + 0x003), PADDR(_pml4 + PML4_OFFSET(KERNBASE))
/* map the PML4 to itself */
movl $PADDR(_pml4 + 0x003), PADDR(_pml4 + PML4_OFFSET(X86_PMAP_OFFSET))
movb $0x80, PADDR(_pml4 + PML4_OFFSET(X86_PMAP_OFFSET) + 7) /* NX bit */
/*
* ensure paging is disabled by clearing CR0.PG (bit 32)
*/
movl %cr0, %eax
andl $0x7fffffff, %eax
movl %eax, %cr0
/*
* enable:
* CR4.PSE (Page Size Extensions, bit 4)
* CR4.PAE (Physical Address Extension, bit 5)
* CR4.PGE (Page Global Enable, bit 7)
*/
movl %cr4, %eax
orl $0x000000b0, %eax
movl %eax, %cr4
/* load cr3 with the PML4 */
movl $PADDR(_pml4), %eax
movl %eax, %cr3
/*
* enable IA-32e by setting IA32_EFER.LME (bit 8)
* (and also set No Execute Enable (bit 11) while we're at it)
*/
movl $0xc0000080, %ecx
rdmsr
orl $0x00000900, %eax
wrmsr
/*
* enable:
* CR0.PG (Paging, bit31)
* CR0.WP (Write Protect, bit 16)
*/
movl %cr0, %eax
orl $0x80010000, %eax
movl %eax, %cr0
/* remember when we pushed the multiboot tag address stored in ebx
* like, 100 lines ago? Yeah we restore that now, and put it into
* the right register to pass it as the first parameter to _boot(). */
pop %edi
/* we can't jump to the high half of 64-bit memory directly since this is
* still a 32-bit instruction, so we need to add an intermediate step to
* a trampoline which can make the actual 64-bit far jump to high memory */
ljmpl $X86_64_KERN_CS, $trampoline
END(_setup)
/*
* native 64-bit code starts here
*/
.code64
L_ENTRY(trampoline)
movabsq $_setup_highmem, %rcx
jmpq *%rcx
L_END(trampoline)
.text
ASM_ENTRY(_setup_highmem)
/*
* update all pointers to virtual address space
*/
movabsq $KERNBASE, %rax
addq %rax, %rsp
addq %rax, %rbp
addq %rax, %rdi /* multiboot tag */
/*
* reload the GDT, this time with the virtual mapping
*/
lgdt _x86_gdt_desc
/* data segments are ignored in 64-bit mode, load the null descriptor */
1: xor %eax, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
/* reset RFLAGS */
pushq $0
popfq
/* remove the low memory identity mapping and bonk the TLB */
movl $0, _pdp0
movl $0, _pdp0 + 8
movl $0, _pml4
movq %cr3, %rax
movq %rax, %cr3
callq _boot
/* this should Never Be Reached(TM) */
cli
2: hlt
jmp 2b
ASM_END(_setup_highmem)
.section .multiboot.data, "a", @progbits
L_DATA(errmsg_no_multiboot)
.asciz "Invalid Multiboot 2 magic number in %eax"
L_END(errmsg_no_multiboot)
L_DATA(errmsg_no_cpuid)
.asciz "CPUID instruction not supported"
L_END(errmsg_no_cpuid)
L_DATA(errmsg_no_ext_cpuid)
.asciz "No extended CPUID features available"
L_END(errmsg_no_ext_cpuid)
L_DATA(errmsg_no_ia32e)
.asciz "CPU does not appear to support IA-32e mode"
L_END(errmsg_no_ia32e)
.section .bootstrap_stack, "aw", @nobits
stack_bottom:
.skip 16384 /* 16 K for the stack should be plenty for now */
stack_top:
/*
* This file is part of GayBSD.
* Copyright (c) 2021 fef <owo@fef.moe>.
*
* GayBSD is nonviolent software: you may only use, redistribute, and/or
* modify it under the terms of the Cooperative Nonviolent Public License
* (CNPL) as found in the LICENSE file in the source code root directory
* or at <https://git.pixie.town/thufie/npl-builder>; either version 7
* of the license, or (at your option) any later version.
*
* GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent
* permitted by applicable law. See the CNPL for details.
*/