From 170f3c0fe5def46c4d287a385e2354536fc5f693 Mon Sep 17 00:00:00 2001 From: Aqua-sama <aqua@iserlohn-fortress.net> Date: Mon, 8 Mar 2021 22:39:16 +0200 Subject: Map kernel to higher half --- src/boot.S | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++----- src/gdt.cc | 11 +++++-- src/kernel.cc | 8 ++--- src/scheduler.cc | 4 +-- 4 files changed, 95 insertions(+), 17 deletions(-) (limited to 'src') 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 diff --git a/src/gdt.cc b/src/gdt.cc index cb210a2..0712187 100644 --- a/src/gdt.cc +++ b/src/gdt.cc @@ -6,12 +6,17 @@ using seg = GDT::SegmentDescriptor; static_assert(sizeof(GDT::Pointer) == 6); constexpr uint32_t null_sz = 0; -constexpr uint32_t kseg_sz = 64 * 1024 * 1024 + 0xfff; + +/* TODO kcode should only contain the .text segment + * TODO kdata should contain the other segments + * */ +constexpr uint32_t kseg_start = 0; +constexpr uint32_t kseg_sz = 0xffffffff; static GDT::SegmentDescriptor segments[256]{ [GDT::null0] = seg::make<null_sz>(0, {}), - [GDT::kcode] = seg::make<kseg_sz>(0, {.r_w = true, .exe = true, .segment = true, .present = true}), - [GDT::kdata] = seg::make<kseg_sz>(0, {.r_w = true, .segment = true, .present = true}), + [GDT::kcode] = seg::make<kseg_sz>(kseg_start, {.r_w = true, .exe = true, .segment = true, .present = true}), + [GDT::kdata] = seg::make<kseg_sz>(kseg_start, {.r_w = true, .segment = true, .present = true}), }; GDT::GDT() { diff --git a/src/kernel.cc b/src/kernel.cc index 372b5bb..33282db 100644 --- a/src/kernel.cc +++ b/src/kernel.cc @@ -24,10 +24,10 @@ typedef void (*constructor)(); extern "C" { -constructor start_ctors; +constructor begin_ctors; constructor end_ctors; void kernel_constructors() { - for (constructor* i = &start_ctors; i != &end_ctors; ++i) (*i)(); + for (constructor* i = &begin_ctors; i != &end_ctors; ++i) (*i)(); } void dump_multiboot(uint32_t mb_magic, uint32_t mb_addr); @@ -43,9 +43,9 @@ void kernel_main([[maybe_unused]] uint32_t mb_magic, [[maybe_unused]] uint32_t m if constexpr (video0_console) Console::set(&video0); #endif - // printk("Hello, kernel World!\n"); + printk("Hello, kernel World!\n"); - // dump_multiboot(mb_magic, mb_addr); + dump_multiboot(mb_magic, mb_addr + 0xc0000000); // dump_gdt(); GDT gdt; diff --git a/src/scheduler.cc b/src/scheduler.cc index 4de9fff..cce15a8 100644 --- a/src/scheduler.cc +++ b/src/scheduler.cc @@ -19,8 +19,8 @@ Scheduler::Scheduler(uint16_t cs) : InterruptHandler(0x00) { printk("add_task: ", add_task(stack_b, cs, task_b), '\n'); printk("add_task: ", add_task(stack_a, cs, task_a), '\n'); - printk("task0 eip: ", uhex{tasks[0]->eip}, '\n'); - printk("task1 eip: ", uhex{tasks[1]->eip}, '\n'); + printk("task0 eip: ", uhex{tasks[1]->eip}, '\n'); + printk("task1 eip: ", uhex{tasks[2]->eip}, '\n'); } uint16_t Scheduler::add_task(uint8_t* stack, uint16_t cs, void (*entry)()) { -- cgit v1.2.1