Finished implementing paging init and long mode switch

This commit is contained in:
2022-12-13 22:22:04 +00:00
parent 3402207602
commit 766667a6ad
5 changed files with 219 additions and 24 deletions

View File

@@ -6,6 +6,8 @@
.set CHECKSUM, -(MAGIC + FLAGS)
/* the amount of memory to allocate for bootstrap page directories. */
/* with buffer size 0x1000 (enough for exactly one page directory)
/* we can map 1 GiB of virtual memory */
#define BOOTSTRAP_PDIR_SIZE 0x1000
.extern start_64 # defined in start_64.S
@@ -13,12 +15,41 @@
.code32
.section .boot.rodata, "a", @progbits
/*******
ERROR MESSAGE STRINGS
*******/
no_long_mode_err:
.asciz "This system is not 64-bit. Press any key to reboot."
unsupported_cpu_err:
.asciz "Your CPU is not supported by Magenta. Press any key to reboot."
/*******
LONG MDOE BOOSTRAP GDT
*******/
bootstrap_gdt:
/* 0x00: null descriptor */
.long 0x0
.long 0x0
/* 0x08: kernel code segment */
.long 0x0000ffff
.long 0x002f9a00
/* 0x10: kernel data segment */
.long 0x0000ffff
.long 0x002f9200
bootstrap_gdt_ptr:
.word 24 /* 8 bytes for each entry in bootstrap_gdt */
.long bootstrap_gdt
/*******
LONG MODE BOOTSTRAP PAGING STRUCTURES
*******/
.section .boot.bss, "aw", @nobits
bootstrap_pml4t: # a single PML4T
.skip 0x1000
@@ -30,6 +61,9 @@ bootstrap_stack_bottom:
.skip 0x1000
bootstrap_stack_top:
/*******
MULTIBOOT HEADER
*******/
.section .boot.text, "ax", @progbits
.align 4
.long MAGIC
@@ -94,6 +128,8 @@ unsupported_cpu:
cli
hlt
.global init_page_tables
.type init_page_tables, @function
init_page_tables:
/* STEP 1: add an entry in the PML4T pointing to the PDPT.
NOTE that we actually add two entries in the PML4T that both point to the same PDPT.
@@ -103,7 +139,7 @@ init_page_tables:
mov $bootstrap_pdpt, %esi
# in %ESI, build a PML4T entry containing the base address of the PDPT
xor $0xFFF, %esi
and $0xFFFFF000, %esi
or $0x3, %esi
# add the PML4T entries at PML4T[0] and PML4T[511]
@@ -111,8 +147,7 @@ init_page_tables:
add $0xFF8, %edi
mov %esi, (%edi)
/* STEP 2: initialise the PDPT.
We need to create an entry in the PDPT for every page directory we intend to use. */
/* STEP 2: write the pgdir entries to the BEGINNING of the PDPT */
# BOOTSTRAP_PDIR_SIZE contains the total size of the page directory buffer.
# use it to calculate the number of page directories we're using.
@@ -129,22 +164,61 @@ init_page_tables:
%EBX is where we build the PDPT entry. */
1:
mov %esi, %ebx
xor $0xFFF, %ebx
and $0xFFFFF000, %ebx
or $0x3, %ebx
# skip the first 4 bytes of the 8-byte entry, leaving them zero.
add $0x4, %edi
mov %ebx, (%edi)
add $0x1000, %esi
add $0x4, %edi
add $0x8, %edi
sub $0x1, %eax
cmp $0x0, %eax
jne 1b
/* STEP 3: initialise the page directories.
Each page directory can be used to map 2MiB of virtual memory.
/* STEP 3: write the pgdir entries to the END of the PDPT */
# BOOTSTRAP_PDIR_SIZE contains the total size of the page directory buffer.
# use it to calculate the number of page directories we're using.
mov $0, %edx
mov $BOOTSTRAP_PDIR_SIZE, %eax
mov $0x1000, %ebx
idiv %ebx
mov $bootstrap_pdpt, %edi
mov $bootstrap_pdir_buf, %esi
/* with the number of page directories in %EAX, calculate the starting index into the PDPT.
we need to duplicate the pgdir entries here to map the kernel at vaddr 0xffffffff80000000 and up */
mov %eax, %ecx
mov $0x8, %edx
mul %edx
xchg %eax, %ecx
add $0x1000, %edi
sub %ecx, %edi
sub $0x08, %edi # subtract one from the index here, otherwise the mapping will be at 0xffffffffc0000000
# (each entry in the array is 8 bytes long)
/* %ESI is a pointer into the pagedir buffer.
%EDI is a pointer into the PDPT.
%EAX is the total number of page directories.
%EBX is where we build the PDPT entry. */
2:
mov %esi, %ebx
and $0xFFFFF000, %ebx
or $0x3, %ebx
mov %ebx, (%edi)
add $0x1000, %esi
add $0x8, %edi
sub $0x1, %eax
cmp $0x0, %eax
jne 2b
/* STEP 4: initialise the page directories.
Each page directory entry can be used to map 2MiB of virtual memory.
The exact number of page directories in use is determined by BOOTSTRAP_PDIR_SIZE */
/* the total number of page directory entries is stored in %EAX */
@@ -160,24 +234,55 @@ init_page_tables:
%EAX is the total number of page directories.
%EBX is where we build the pagedir entry. */
2:
3:
mov %esi, %ebx
xor $0xFFF, %ebx
or $0x3, %ebx
and $0xFFFFF000, %ebx
or $0x83, %ebx
# skip the first 4 bytes of the 8-byte entry, leaving them zero.
add $0x4, %edi
mov %ebx, (%edi)
add $0x200000, %esi
add $0x4, %edi
dec %eax
jnz 2b
add $0x8, %edi
sub $0x1, %eax
cmp $0x0, %eax
jne 3b
ret
.global long_mode_switch
.type long_mode_switch, @function
long_mode_switch:
ret
/* load %CR3 with the physical address of the PML4T */
mov $bootstrap_pml4t, %eax
mov %eax, %cr3
/* enable long mode and the syscall/sysret instructions */
movl $0xC0000080, %ecx
rdmsr
orl $0x00000101, %eax
wrmsr
lgdt (bootstrap_gdt_ptr)
mov %cr4, %ecx
orl $0x00000020, %ecx
mov %ecx, %cr4
mov %cr0, %ecx
orl $0x8000002a, %ecx
mov %ecx, %cr0
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
ljmpl $0x08, $start_64
cli
hlt
.global _start
.type _start, @function