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