From ed67adc9c9c6dad56c96000c0bf2aab8b7ab332e Mon Sep 17 00:00:00 2001 From: Xnoe Date: Fri, 26 Nov 2021 20:20:15 +0000 Subject: [PATCH] Updated kernel to now have its stack past 0xc0000000. Implemented context switching. --- src/boot_stage2/main.c | 4 ++- src/kernel/entry.asm | 1 + src/kernel/global.cpp | 1 + src/kernel/global.h | 2 ++ src/kernel/idt.cpp | 81 ++++++++++++++++++++++++++++++++++++++++-- src/kernel/idt.h | 9 ++--- src/kernel/kernel.cpp | 9 ++++- src/kernel/kernel.h | 8 ++--- src/kernel/kmain.cpp | 11 ++++-- src/kernel/memory.cpp | 17 +++++---- src/kernel/memory.h | 9 ++--- src/kernel/process.cpp | 80 ++++++++++++++++++++++++++++++++++++++--- src/kernel/process.h | 5 ++- 13 files changed, 206 insertions(+), 31 deletions(-) diff --git a/src/boot_stage2/main.c b/src/boot_stage2/main.c index 666aaff..a19227b 100644 --- a/src/boot_stage2/main.c +++ b/src/boot_stage2/main.c @@ -167,7 +167,7 @@ void main() { mark_unavailble(0xc0502000, 0x1000, vm_bitmap); mark_unavailble(0xc0600000, 0x20000, vm_bitmap); mark_unavailble(0xc0620000, 0x20000, vm_bitmap); - mark_unavailble(0x8a000, 0x6000, vm_bitmap); + mark_unavailble(0xc1000000, 0x6000, vm_bitmap); // Map the bitmap map_many_4k_phys_to_virt(0x100000, 0xc0600000, kernel_page_directory, kernel_page_tables, 32); @@ -175,7 +175,9 @@ void main() { map_many_4k_phys_to_virt(0x522000, 0xc0620000, kernel_page_directory, kernel_page_tables, 32); map_4k_phys_to_virt(0x8000, 0x8000, kernel_page_directory, kernel_page_tables); + // Map the stack map_many_4k_phys_to_virt(0x8a000, 0x8a000, kernel_page_directory, kernel_page_tables, 6); + map_many_4k_phys_to_virt(0x8a000, 0xc1000000, kernel_page_directory, kernel_page_tables, 6); load_file("KERNEL BIN", kernel_location); diff --git a/src/kernel/entry.asm b/src/kernel/entry.asm index 446a4bc..9b5da86 100644 --- a/src/kernel/entry.asm +++ b/src/kernel/entry.asm @@ -1,6 +1,7 @@ [BITS 32] _start: + mov esp, 0xc1005ffc jmp main extern main \ No newline at end of file diff --git a/src/kernel/global.cpp b/src/kernel/global.cpp index 4d42b26..4570113 100644 --- a/src/kernel/global.cpp +++ b/src/kernel/global.cpp @@ -3,6 +3,7 @@ namespace Global { Allocator* allocator = 0; Kernel* kernel = 0; + Process* currentProc = 0; } void* operator new (uint32_t size) { diff --git a/src/kernel/global.h b/src/kernel/global.h index 78fe794..e6ed2f4 100644 --- a/src/kernel/global.h +++ b/src/kernel/global.h @@ -5,10 +5,12 @@ class Kernel; class Allocator; +class Process; namespace Global { extern Allocator* allocator; extern Kernel* kernel; + extern Process* currentProc; } void* operator new (uint32_t size); diff --git a/src/kernel/idt.cpp b/src/kernel/idt.cpp index 73cbcfe..9d0641c 100644 --- a/src/kernel/idt.cpp +++ b/src/kernel/idt.cpp @@ -22,14 +22,89 @@ __attribute__((interrupt)) void interrupt_20(interrupt_frame* frame) { __attribute__((interrupt)) void page_fault(interrupt_frame* frame, uint32_t err_code) { uint32_t problem_address; asm("mov %%cr2, %0" : "=a" (problem_address) :); - printf("Page Fault at %x\n", problem_address); - asm("hlt"); + printf("(EIP %x): Page Fault at %x\n", frame->eip, problem_address); + while (1) asm("hlt"); } __attribute__((interrupt)) void ignore_interrupt(interrupt_frame* frame) { outb(0x20, 0x20); } +__attribute__((interrupt)) void gpf(interrupt_frame* frame, uint32_t err_code) { + printf("General Protection Fault %d\n", err_code); + while (1) asm("hlt"); +} + +__attribute__((interrupt)) void context_switch(interrupt_frame* frame) { + asm ("cli"); // Disable interrupts whilst handling the context switch. + asm ("pusha"); // Push registers to the stack + + Process* currentProc = Global::currentProc; + Process* nextProc = 0; + if (currentProc) { + xnoe::linkedlist* processes = &Global::kernel->processes; + + // This cursed bit of code first determines if the processes list is longer than 1 and if it is + // - Determines if it has 2 or more elements + // - If it has two, swap the first and last, update prev and next of each to be null or the other item + // - If it has more than two, swap the first and last, then swap their next and prevs, and set the + // other value to null + if (processes->start->next != 0) { + if (processes->end->prev == processes->start) { + xnoe::linkedlistelem* tmp = processes->start; + processes->start = processes->end; + processes->end = tmp; + + processes->start->prev = 0; + processes->end->next = 0; + processes->end->prev = processes->start; + processes->start->next = processes->end; + } else { + xnoe::linkedlistelem* tmp = processes->start; + processes->start = processes->end; + processes->end = tmp; + + processes->start->next = processes->end->next; + processes->end->prev = processes->start->prev; + + processes->start->prev = 0; + processes->end->next = 0; + + processes->start->next->prev = processes->start; + processes->end->prev->next = processes->end; + } + + // Get the next process. + nextProc = processes->start->elem; + } + + Global::currentProc = nextProc; + + uint32_t cESP; + asm volatile ("mov %%esp, %0" : "=a" (cESP) :); + currentProc->esp = cESP; // Store the current ESP of the current process process. + + // Select the next processes page directory + asm volatile ("mov %0, %%cr3" : : "r" (nextProc->PD->phys_addr)); + // Restore ESP of the new process. + asm volatile ("mov %0, %%esp" : : "m" (Global::kernel->processes.start->elem->esp)); + // Restore registers + asm ("popa"); + + // Clear the garbage that was on the stack from previous switch_context call. + asm ("add $44, %esp"); + + // Pop EBP + asm ("pop %ebp"); + + // Re-enable interrupts. + asm ("sti"); + + // Manually perform iret. + asm ("iret"); + } +} + void init_idt() { idt_desc desc = {.size = 256 * sizeof(GateEntry) - 1, .offset = (uint32_t)idt}; asm volatile("lidt %0" : : "m" (desc)); @@ -37,7 +112,9 @@ void init_idt() { set_entry(i, 0x08, &ignore_interrupt, 0x8E); set_entry(0x20, 0x08, &interrupt_20, 0x8E); + set_entry(0xD, 0x08, &gpf, 0x8E); set_entry(0xE, 0x08, &page_fault, 0x8E); + set_entry(0x80, 0x08, &context_switch, 0x8E); outb(0x20, 0x11); outb(0xA0, 0x11); diff --git a/src/kernel/idt.h b/src/kernel/idt.h index 52c5fc9..4df1f3d 100644 --- a/src/kernel/idt.h +++ b/src/kernel/idt.h @@ -3,13 +3,14 @@ #include "types.h" #include "screenstuff.h" +#include "global.h" +#include "kernel.h" struct interrupt_frame { - uint16_t ip; + uint32_t eip; uint16_t cs; - uint16_t flags; - uint16_t sp; - uint16_t ss; + uint16_t _ignored0; + uint32_t eflags; }; extern void load_idt(); void set_entry(uint8_t interrupt_number, uint16_t code_segment, void* handler, uint8_t type); diff --git a/src/kernel/kernel.cpp b/src/kernel/kernel.cpp index 4333bf2..01c24fe 100644 --- a/src/kernel/kernel.cpp +++ b/src/kernel/kernel.cpp @@ -6,6 +6,10 @@ Kernel::Kernel(PageDirectory* page_directory, PageMap* phys, PageMap* virt, uint this->currentPID = 1; Global::allocator = this; Global::kernel = this; + + Global::currentProc = 0; + + this->processes.append(this); } void Kernel::init_kernel() { @@ -13,8 +17,11 @@ void Kernel::init_kernel() { } Process* Kernel::createProcess() { - Process* p = new Process(currentPID); + Process* p = new Process(currentPID, this->PD, 0xc0000000); this->pid_map->set(currentPID, p); currentPID++; + + this->processes.append(p); + return p; } \ No newline at end of file diff --git a/src/kernel/kernel.h b/src/kernel/kernel.h index aa98294..c020359 100644 --- a/src/kernel/kernel.h +++ b/src/kernel/kernel.h @@ -6,12 +6,12 @@ #include "global.h" class Kernel : public Process { -private: +public: uint32_t currentPID; -public: - xnoe::hashtable* pid_map; // Map of PIDs -> Process*s - uint32_t current_PID; + xnoe::hashtable* pid_map; // Map of PIDs -> Process*s + + xnoe::linkedlist processes; Kernel(PageDirectory* page_directory, PageMap* phys, PageMap* virt, uint32_t virt_alloc_base); diff --git a/src/kernel/kmain.cpp b/src/kernel/kmain.cpp index a5fbbf6..444ca35 100644 --- a/src/kernel/kmain.cpp +++ b/src/kernel/kmain.cpp @@ -47,10 +47,15 @@ int main() { term->printf("KERNEL OK!\n"); - kernel.createProcess(); + Process* p = kernel.createProcess(); - term->deactivate(); - term2->activate(); + //term->deactivate(); + //term2->activate(); + + Global::currentProc = &kernel; + asm ("int $0x80"); + + term->printf("\n\nIf you are reading this, the XnoeOS kernel successfully switched contexts to another process, and then switched contexts back to the kernel. Therefore we can conclude that context switching works."); while (1); diff --git a/src/kernel/memory.cpp b/src/kernel/memory.cpp index 3e8dc4c..a576aa1 100644 --- a/src/kernel/memory.cpp +++ b/src/kernel/memory.cpp @@ -88,8 +88,10 @@ PageTable::PageTable(uint32_t phys, uint32_t virt) { } PageTable::PageTable(){ - virt_addr = new PTE[1024]; - phys_addr = Global::allocator->virtual_to_physical(virt_addr); + virt_addr = new PTE[2048]; + while ((uint32_t)this->virt_addr & 0xfff || (uint32_t)this->virt_addr % 0x4) + this->virt_addr++; + phys_addr = (Global::allocator->virtual_to_physical(virt_addr)) >> 12; page_table = (PTE*)virt_addr; } @@ -143,8 +145,11 @@ PageDirectory::PageDirectory(PDE* page_directory, uint32_t phys_addr, uint32_t o PageDirectory::PageDirectory() { this->page_tables = (PageTable*)this->__page_tables; - this->page_directory = new PDE[1024]; + this->page_directory = new PDE[2048]; + while ((uint32_t)this->page_directory & 0xfff || (uint32_t)this->page_directory % 0x4) + this->page_directory++; memset((uint8_t*)this->page_tables, sizeof(PageTable) * 1024, 0); + memset((uint8_t*)this->page_directory, sizeof(PDE) * 1024, 0); this->phys_addr = Global::allocator->virtual_to_physical(this->page_directory); } @@ -180,11 +185,11 @@ void PageDirectory::unmap(uint32_t virt) { uint32_t PageDirectory::virtual_to_physical(uint32_t virt) { split_addr* split = (split_addr*)&virt; - return page_tables[split->pd_index].get_physical_address(split->pt_index); + return page_tables[split->pd_index].get_physical_address(split->pt_index) + split->page_offset; } void PageDirectory::select() { - asm volatile("mov %0, %%eax; mov %%eax, %%cr3" : : "m" (phys_addr)); + asm volatile("mov %0, %%cr3" : : "r" (phys_addr)); } PageMap* Allocator::phys; @@ -205,7 +210,6 @@ Allocator::Allocator(PageDirectory* page_directory, PageMap* virt, uint32_t virt } void* Allocator::allocate(uint32_t size) { - asm("mov $0x0, 0x8a000"); uint32_t count = (size + (4096 - size % 4096)) / 4096; uint32_t virt_addr = virt->find_next_available_from(this->virt_alloc_base, count); @@ -214,7 +218,6 @@ void* Allocator::allocate(uint32_t size) { for (int i=0; iphys->find_next_available_from(0); this->phys->mark_unavailable(phys_addr); - asm("mov $0xdead, 0x8a000"); this->PD->map(phys_addr, virt_addr + 4096 * i); } diff --git a/src/kernel/memory.h b/src/kernel/memory.h index 2a8b476..a57708c 100644 --- a/src/kernel/memory.h +++ b/src/kernel/memory.h @@ -51,13 +51,14 @@ struct PageTable { class PageDirectory { private: - PDE* page_directory; uint8_t __page_tables[sizeof(PageTable) * 1024]; PageTable* page_tables; +public: uint32_t phys_addr; -public: + PDE* page_directory; + PageDirectory(PDE* page_directories, uint32_t phys_addr, uint32_t offset); PageDirectory(); @@ -74,10 +75,10 @@ protected: static PageMap* phys; PageMap* virt; - PageDirectory* PD; - uint32_t virt_alloc_base; public: + PageDirectory* PD; + Allocator(PageDirectory* page_directory, PageMap* phys, PageMap* virt, uint32_t virt_alloc_base); Allocator(PageDirectory* page_directory, PageMap* virt, uint32_t virt_alloc_base); virtual void* allocate(uint32_t size); diff --git a/src/kernel/process.cpp b/src/kernel/process.cpp index 760d8e4..9ea3e91 100644 --- a/src/kernel/process.cpp +++ b/src/kernel/process.cpp @@ -26,11 +26,80 @@ Process::Process(uint32_t PID) : Allocator(new PageDirectory, new PageMap, 0) { this->PID = PID; this->page_remaining = 0; - this->last_page_pointer = virt_alloc_base; - this->stack = new uint8_t[0x8000]; + this->last_page_pointer = 0; + this->stack = this->allocate(0x8000); +} + +Process::Process(uint32_t PID, PageDirectory* inherit, uint32_t inheritBase) +: Allocator(new PageDirectory, new PageMap, 0) { + this->PID = PID; + this->page_remaining = 0; + this->last_page_pointer = 0; + + for (int index = inheritBase >> 22; index < 1024; index++) + this->PD->page_directory[index] = inherit->page_directory[index]; + + this->stack = this->allocate(0x8000); + + uint32_t pCR3; + asm ("mov %%cr3, %0" : "=a" (pCR3) :); + this->PD->select(); + + // We also need to initialise ESP and the stack + uint32_t* stack32 = ((uint32_t)this->stack + 0x8000); + printf("stack32: %x\n", stack32); + stack32--; + *stack32 = 0x0; // EFLAGS + stack32--; + *stack32 = 8; // CS 0x08 + stack32--; + *stack32 = 0x14; // Execution will begin from 0x14 + + stack32--; + *stack32 = ((uint32_t)this->stack + 0x8000); // EBP + + stack32 -= 11; + + stack32--; + *stack32 = 0; // EAX + stack32--; + *stack32 = 0; // ECX + stack32--; + *stack32 = 0; // EDX + stack32--; + *stack32 = 0; // EBX + stack32--; + *stack32 = 0; // ESP + stack32--; + *stack32 = (uint32_t)this->stack + 0x7ffc; // EBP + stack32--; + *stack32 = 0; // ESI + stack32--; + *stack32 = 0; // EDI + + this->esp = stack32; + printf("this->esp: %x\n", stack32); + + /*((uint8_t*)this->stack)[0] = 0xe9; + ((uint8_t*)this->stack)[1] = 0xfb; + ((uint8_t*)this->stack)[2] = 0xff; + ((uint8_t*)this->stack)[3] = 0xff; + ((uint8_t*)this->stack)[4] = 0xff;*/ + + ((uint8_t*)this->stack)[0] = 0xcd; + ((uint8_t*)this->stack)[1] = 0x80; + + asm ("mov %0, %%cr3" : : "r" (pCR3)); } void* Process::allocate(uint32_t size) { + bool switched_PD = false; + uint32_t pCR3; + asm ("mov %%cr3, %0" : "=a" (pCR3) :); + if (Global::currentProc != this) { + switched_PD = true; + this->PD->select(); + } void* ptr; // Determine if there's enough space to just allocate what's been requested if (size < this->page_remaining) { @@ -46,7 +115,7 @@ void* Process::allocate(uint32_t size) { uint32_t pages = size / 4096; uint32_t remainder = 4096 - (size % 4096); - ptr = Allocator::allocate(size); + ptr = this->Allocator::allocate(size); // Update local values this->last_page_pointer = ptr + pages * 4096; @@ -61,6 +130,9 @@ void* Process::allocate(uint32_t size) { ptr += elem_size; } + + asm ("mov %0, %%cr3" : : "r" (pCR3)); + return ptr; } @@ -68,7 +140,7 @@ void Process::deallocate(uint32_t virt_addr) { xnoe::Maybe*> alloc_tracker = this->get_alloc_tracker(virt_addr); if (alloc_tracker.is_ok()) { AllocTracker* ac = &alloc_tracker.get()->elem; - ac->alloc_count -= 1; + ac->alloc_count--; if (ac->alloc_count == 0) { void* base = ac->page_base; uint32_t count = ac->page_size; diff --git a/src/kernel/process.h b/src/kernel/process.h index 2cf3ce8..c2b72a5 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -10,7 +10,7 @@ #include "global.h" struct AllocTracker { - void* page_base; + void* page_base; uint32_t page_size; uint32_t alloc_count; @@ -32,8 +32,11 @@ private: xnoe::Maybe*> get_alloc_tracker(uint32_t address); public: + uint32_t esp; + Process(uint32_t PID, void* stack, PageDirectory* page_directory, PageMap* phys, PageMap* virt, uint32_t virt_alloc_base); Process(uint32_t PID); + Process(uint32_t PID, PageDirectory* inherit, uint32_t inheritBase); void* allocate(uint32_t size) override; void deallocate(uint32_t virt_addr) override;