190 lines
6.5 KiB
NASM
190 lines
6.5 KiB
NASM
#define ASM_FILE
|
|
#include <arch/mlayout.h>
|
|
#include "multiboot2.h"
|
|
%define BOCHS_BREAK xchg bx, bx
|
|
%define GET_PADDR(x) ((x) - KERN_BASE_START)
|
|
%define GET_PML4(vaddr) (((vaddr) >> 39 ) & 0x1FF)
|
|
%define GET_PDPT(vaddr) (((vaddr) >> 30 ) & 0x1FF)
|
|
%define GET_PDE(vaddr) (((vaddr) >> 21 ) & 0x1FF)
|
|
|
|
; make arch_init_32 visiable
|
|
global arch_init_32
|
|
|
|
; arch_main is the C long mode entry point
|
|
extern arch_main
|
|
|
|
section .text
|
|
bits 32
|
|
; the bootloader calls this dude, we switch to long mode with some basic setup:
|
|
; Identity map the first 4G memory, where the kernel binary and multiboot info is
|
|
; Map the first 4G memory to KERN_PMAP temporarily so we have access to printf
|
|
; Map the first 1G memory, which contains the kernel, to KERN_BASE_START
|
|
arch_init_32:
|
|
cli ; close interrupt
|
|
cld ; set direction
|
|
cmp eax, MULTIBOOT2_BOOTLOADER_MAGIC ; compare with multiboot2 magic
|
|
jne .end ; if not loaded by multiboot2 compliant bootloader, loop infinitely
|
|
|
|
BOCHS_BREAK
|
|
|
|
; save multiboot info
|
|
mov dword [GET_PADDR(multiboot_info_ptr)], ebx
|
|
|
|
; disable paging first
|
|
mov eax, cr0 ; Set the A-register to control register 0.
|
|
and eax, ~(1 << 31) & 0xFFFFFFFF ; Clear the PG-bit, which is bit 31, and hack to get rid of warning
|
|
mov cr0, eax ; Set control register 0 to the A-register.
|
|
|
|
; identity map the first 4G page
|
|
mov eax, GET_PADDR(kern_early_pml4)
|
|
add eax, GET_PML4(0) * 8 ; eax = offset of pml4e in pml4 for the 0th GB
|
|
mov dword [eax], GET_PADDR(kern_early_ident_pdpt) + 11b ; let the corresponding pml4e point to the kern_early_ident_pdpt
|
|
|
|
mov eax, GET_PADDR(kern_early_ident_pdpt)
|
|
add eax, GET_PDPT(0) * 8
|
|
mov ebx, 10000011b ; ebx lower bits is attribute = R/W + SU + 1G page, high bits = physical 0th GB
|
|
mov ecx, 4 ; 4 times = 4GB
|
|
.l0:
|
|
mov dword [eax], ebx ; set the corresponding pdpte to map 1GB pages in ebx
|
|
add ebx, 1*1024*1024*1024 ; add another 1G to ebx higher bits
|
|
add eax, 8 ; increment to next pdpte
|
|
loop .l0
|
|
|
|
; map the first 4G to pmap
|
|
mov eax, GET_PADDR(kern_early_pml4)
|
|
add eax, GET_PML4(KERN_PMAP_START) * 8 ; eax = offset of pml4e in pml4 for PMAP region
|
|
mov dword [eax], GET_PADDR(kern_early_pmap_pdpt) + 11b ; let the corresponding pml4e point to the kern_early_pmap_pdpt
|
|
|
|
mov eax, GET_PADDR(kern_early_pmap_pdpt)
|
|
add eax, GET_PDPT(KERN_PMAP_START) * 8
|
|
mov ebx, 10000011b ; ebx lower bits is attribute = R/W + SU + 1G page, high bits = physical 0th GB
|
|
mov ecx, 4 ; 4 times = 4GB
|
|
.l1:
|
|
mov dword [eax], ebx ; set the corresponding pdpte to map 1GB pages in ebx
|
|
add ebx, 1*1024*1024*1024 ; add another 1G to ebx higher bits
|
|
add eax, 8 ; increment to next pdpte
|
|
loop .l1
|
|
|
|
; map the first 1G to kern_base
|
|
; point the first PML4 entry to the identity pdpt
|
|
mov eax, GET_PADDR(kern_early_pml4)
|
|
add eax, GET_PML4(KERN_BASE_START) * 8
|
|
mov dword [eax], GET_PADDR(kern_early_img_pdpt) + 11b ; let the corresponding pml4e point to the kern_early_img_pdpt
|
|
|
|
mov eax, GET_PADDR(kern_early_img_pdpt)
|
|
add eax, GET_PDPT(KERN_BASE_START) * 8
|
|
mov dword [eax], 10000011b ; ebx lower bits is attribute = R/W + SU + 1G page, high bits = physical 0th GB
|
|
|
|
BOCHS_BREAK
|
|
|
|
; enable PAE
|
|
mov eax, cr4 ; Set the A-register to control register 4.
|
|
or eax, 1 << 5 ; Set the PAE-bit, which is the 6th bit (bit 5).
|
|
mov cr4, eax ; Set control register 4 to the A-register.
|
|
|
|
; enable long mode
|
|
mov ecx, 0xC0000080 ; Set the C-register to 0xC0000080, which is the EFER MSR.
|
|
rdmsr ; Read from the model-specific register.
|
|
or eax, 1 << 8 ; Set the LM-bit which is the 9th bit (bit 8).
|
|
wrmsr ; Write to the model-specific register.
|
|
|
|
; let cr3 point at page table
|
|
mov eax, GET_PADDR(kern_early_pml4)
|
|
mov cr3, eax
|
|
|
|
xchg bx, bx
|
|
; enable paging, enter compatibility mode
|
|
mov eax, cr0 ; Set the A-register to control register 0.
|
|
or eax, 1 << 31 ; Set the PG-bit, which is bit 31.
|
|
mov cr0, eax ; Set control register 0 to the A-register.
|
|
|
|
; now we are in compat mode
|
|
; load the long mode GDT
|
|
lgdt [GET_PADDR(kern_early_gdt.ptr)]
|
|
|
|
; switch to long mode
|
|
jmp kern_early_gdt.code:GET_PADDR(arch_init_64)
|
|
; should not reach this point
|
|
.end:
|
|
jmp $
|
|
|
|
section .data
|
|
bits 32
|
|
multiboot_info_ptr:
|
|
dd 0
|
|
|
|
section .text
|
|
bits 64
|
|
arch_init_64:
|
|
; note that we are in long mode but rip is still lower
|
|
; switch to high address
|
|
mov rax, .high
|
|
jmp rax
|
|
.high:
|
|
; set proper segment registers
|
|
mov ax,kern_early_gdt.data
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov fs,ax
|
|
mov gs,ax
|
|
mov ss,ax
|
|
|
|
; initial kernel stack, 4k
|
|
mov rsp, kern_early_stack
|
|
xor rdi, rdi
|
|
mov edi, dword [multiboot_info_ptr]
|
|
; init arch
|
|
call arch_main
|
|
; should not reach this point
|
|
jmp $
|
|
|
|
|
|
section .data
|
|
bits 64
|
|
align 0x1000
|
|
times KERN_PAGE_SZ db 0
|
|
kern_early_stack:
|
|
|
|
align 0x1000
|
|
kern_early_pml4:
|
|
times 0x1000 db 0
|
|
|
|
align 0x1000
|
|
kern_early_ident_pdpt:
|
|
times 0x1000 db 0
|
|
|
|
align 0x1000
|
|
kern_early_pmap_pdpt:
|
|
times 0x1000 db 0
|
|
|
|
align 0x1000
|
|
kern_early_img_pdpt:
|
|
times 0x1000 db 0
|
|
|
|
kern_early_gdt: ; Global Descriptor Table (long mode).
|
|
.null: equ $ - kern_early_gdt ; The null descriptor.
|
|
dw 0 ; Limit (low).
|
|
dw 0 ; Base (low).
|
|
db 0 ; Base (middle)
|
|
db 0 ; Access.
|
|
db 0 ; Granularity.
|
|
db 0 ; Base (high).
|
|
.code: equ $ - kern_early_gdt ; The code descriptor.
|
|
dw 0 ; Limit (low).
|
|
dw 0 ; Base (low).
|
|
db 0 ; Base (middle)
|
|
db 10011010b ; Access (exec/read).
|
|
db 00100000b ; Granularity.
|
|
db 0 ; Base (high).
|
|
.data: equ $ - kern_early_gdt ; The data descriptor.
|
|
dw 0 ; Limit (low).
|
|
dw 0 ; Base (low).
|
|
db 0 ; Base (middle)
|
|
db 10010010b ; Access (read/write).
|
|
db 00000000b ; Granularity.
|
|
db 0 ; Base (high).
|
|
.ptr:
|
|
; GDT PTR
|
|
dw $ - kern_early_gdt - 1 ; Limit.
|
|
dq GET_PADDR(kern_early_gdt) ; Base.
|