From 170f3c0fe5def46c4d287a385e2354536fc5f693 Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Mon, 8 Mar 2021 22:39:16 +0200 Subject: Map kernel to higher half --- src/boot.S | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 8 deletions(-) (limited to 'src/boot.S') diff --git a/src/boot.S b/src/boot.S index b17aea3..635b38f 100644 --- a/src/boot.S +++ b/src/boot.S @@ -36,17 +36,95 @@ stack_bottom: .skip 16 * 1024 stack_top: +.section .pages, "aw", @nobits +.align 4096 +boot_page_directory: + .skip 4096 +boot_page_table1: + .skip 4096 + + /* The linker script specifies _start as the entry point to the kernel and the bootloader will jump to this position once the kernel has been loaded. */ .section .multiboot.text, "ax" -.extern kernel_constructors -.extern kernel_main .global _start .type _start, @function _start: - mov $stack_bottom, %ebp + # Physical address of boot_page_table1. + movl $(boot_page_table1 - 0xC0000000), %edi + # First address to map is address 0. + # TODO: Start at the first kernel page instead. Alternatively map the first + # 1 MiB as it can be generally useful, and there's no need to + # specially map the VGA buffer. + movl $0, %esi + # Map 1023 pages. The 1024th will be the VGA text buffer. + movl $1023, %ecx + +1: + # Only map the kernel. + cmpl $_kernel_start, %esi + jl 2f + cmpl $(_kernel_end - 0xC0000000), %esi + jge 3f + + # Map physical address as "present, writable". Note that this maps + # .text and .rodata as writable. Mind security and map them as non-writable. + movl %esi, %edx + orl $0x003, %edx + movl %edx, (%edi) + +2: + # Size of page is 4096 bytes. + addl $4096, %esi + # Size of entries in boot_page_table1 is 4 bytes. + addl $4, %edi + # Loop to the next entry if we haven't finished. + loop 1b + +3: + # Map VGA video memory to 0xC03FF000 as "present, writable". + movl $(0x000B8000 | 0x003), boot_page_table1 - 0xC0000000 + 1023 * 4 + + # The page table is used at both page directory entry 0 (virtually from 0x0 + # to 0x3FFFFF) (thus identity mapping the kernel) and page directory entry + # 768 (virtually from 0xC0000000 to 0xC03FFFFF) (thus mapping it in the + # higher half). The kernel is identity mapped because enabling paging does + # not change the next instruction, which continues to be physical. The CPU + # would instead page fault if there was no identity mapping. + + # Map the page table to both virtual addresses 0x00000000 and 0xC0000000. + movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 0 + movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 768 * 4 + + # Set cr3 to the address of the boot_page_directory. + movl $(boot_page_directory - 0xC0000000), %ecx + movl %ecx, %cr3 + + # Enable paging and the write-protect bit. + movl %cr0, %ecx + orl $0x80010000, %ecx + movl %ecx, %cr0 + + # Jump to higher half with an absolute jump. + lea (kinit), %ecx + jmp *%ecx + +.section .text +.extern kernel_constructors +.extern kernel_main +kinit: + # At this point, paging is fully set up and enabled. + + # Unmap the identity mapping as it is now unnecessary. + movl $0, boot_page_directory + 0 + + # Reload crc3 to force a TLB flush so the changes to take effect. + movl %cr3, %ecx + movl %ecx, %cr3 + + #mov $stack_bottom, %ebp mov $stack_top, %esp # point the stack pointer to the stack /* @@ -90,8 +168,3 @@ _start: hang: hlt jmp hang -/* -Set the size of the _start symbol to the current location '.' minus its start. -This is useful when debugging or when you implement call tracing. -*/ -.size _start, . - _start -- cgit v1.2.1