Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Kernel: [#69] Graceful double-fault handling
Browse files Browse the repository at this point in the history
  • Loading branch information
byteduck committed Jan 7, 2024
1 parent 2c38dc2 commit 7e1eeb3
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 55 deletions.
9 changes: 4 additions & 5 deletions kernel/asm/tasking.s
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@ preempt_asm:
push esi
push edi
push ebp
mov ebp, esp
mov eax, [ebp+24] ;old_esp
mov ecx, [ebp+28] ;new_esp
mov edx, [ebp+32] ;new_cr3
mov eax, [esp+24] ;old_esp
mov ecx, [esp+28] ;new_esp
mov edx, [esp+32] ;new_cr3
mov [eax], esp
mov cr3, edx
mov esp, [ecx]
Expand Down Expand Up @@ -58,4 +57,4 @@ proc_first_preempt:
out 0xA0, al
pop eax
fninit
iret
iret
33 changes: 32 additions & 1 deletion kernel/interrupt/isr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
#include <kernel/arch/i386/registers.h>

namespace Interrupt {
TSS fault_tss;

[[noreturn]] void double_fault();

void isr_init(){
idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
Expand All @@ -40,7 +44,8 @@ namespace Interrupt {
idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);
idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
// Special case for double-fault; we want to use a separate TSS so we can be sure we have a clean stack to work with.
idt_set_gate(8, 0, 0x30, 0x85);
idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
Expand All @@ -64,6 +69,32 @@ namespace Interrupt {
idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);

// Setup the double-fault TSS and a stack for it
memset(&fault_tss, 0, sizeof(TSS));
fault_tss.ss0 = 0x10;
fault_tss.cs = 0x08;
fault_tss.ss = 0x10;
fault_tss.ds = 0x10;
fault_tss.es = 0x10;
fault_tss.fs = 0x10;
fault_tss.gs = 0x10;
fault_tss.ss = 0x10;
fault_tss.eflags = 0x2;
fault_tss.cr3 = MM.kernel_page_directory.entries_physaddr();
fault_tss.esp0 = MM.inst().alloc_kernel_stack_region(PAGE_SIZE * 16)->end();
fault_tss.esp = fault_tss.esp0;
fault_tss.eip = (size_t) double_fault;
}

[[noreturn]] void double_fault() {
PANIC_NOHLT("DOUBLE_FAULT", "A double fault occurred. Something has gone horribly wrong.");
if (!MM.kernel_page_directory.is_mapped(TaskManager::tss.esp + sizeof(void*), false)) {
printf("Looks like a stack overflow occurred in the kernel. Hold on, this is gonna be a doozy:\n");
}
KernelMapper::print_stacktrace(TaskManager::tss.ebp);
asm volatile("cli; hlt");
while(1);
}

void handle_fault(const char* err, const char* panic_msg, uint32_t sig, ISRRegisters* regs) {
Expand Down
2 changes: 2 additions & 0 deletions kernel/interrupt/isr.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include <kernel/kstd/kstddef.h>
#include <kernel/arch/i386/registers.h>
#include <kernel/tasking/TSS.h>

#define FAULT_KERNEL_READ 0
#define FAULT_KERNEL_READ_GPF 1
Expand All @@ -32,6 +33,7 @@
#define FAULT_USER_WRITE_GPF 7

namespace Interrupt {
extern TSS fault_tss;
extern "C" void isr0();
extern "C" void isr1();
extern "C" void isr2();
Expand Down
2 changes: 1 addition & 1 deletion kernel/kmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ int kmain(uint32_t mbootptr){
struct multiboot_info mboot_header = parse_mboot(mbootptr);
CommandLine cmd_line(mboot_header);
Memory::load_gdt();
Interrupt::init();
MemoryManager::inst().setup_paging();
Interrupt::init();
VMWare::detect();
Device::init();

Expand Down
20 changes: 15 additions & 5 deletions kernel/kstd/kstdio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ void vprintf(const char* fmt, va_list argp){

bool panicked = false;

[[noreturn]] void PANIC(const char* error, const char* msg, ...){
void panic_inner(const char* error, const char* msg, va_list list) {
TaskManager::enter_critical();
Interrupt::NMIDisabler disabler;

Expand Down Expand Up @@ -182,10 +182,7 @@ bool panicked = false;
else
printf("[No thread info]\n");

va_list list;
va_start(list, msg);
vprintf(msg, list);
va_end(list);

//Printing the stacktrace may panic if done early in kernel initialization. Don't print stacktrace in a nested panic.
#ifdef DEBUG
Expand All @@ -195,7 +192,20 @@ bool panicked = false;
KernelMapper::print_stacktrace();
}
#endif
}

void PANIC_NOHLT(const char *error, const char *msg, ...) {
va_list list;
va_start(list, msg);
panic_inner(error, msg, list);
va_end(list);
}

[[noreturn]] void PANIC(const char* error, const char* msg, ...) {
va_list list;
va_start(list, msg);
panic_inner(error, msg, list);
va_end(list);
asm volatile("cli; hlt");
while(1);
}
Expand All @@ -210,4 +220,4 @@ void setup_tty() {
tty_desc = kstd::make_shared<FileDescriptor>(tty);
tty_desc->set_options(O_WRONLY);
did_setup_tty = true;
}
}
1 change: 1 addition & 0 deletions kernel/kstd/kstdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ void serial_putch(char c);
void vprintf(const char* fmt, va_list list);
void printf(const char* fmt, ...);
void print(const char* str);
void PANIC_NOHLT(const char *error, const char *msg, ...);
[[noreturn]] void PANIC(const char *error, const char *msg, ...);
void clearScreen();
void setup_tty();
11 changes: 11 additions & 0 deletions kernel/memory/MemoryManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,17 @@ kstd::Arc<VMRegion> MemoryManager::alloc_kernel_region(size_t size) {
return res.value();
}

kstd::Arc<VMRegion> MemoryManager::alloc_kernel_stack_region(size_t size) {
auto do_alloc = [&]() -> ResultRet<kstd::Arc<VMRegion>> {
auto object = TRY(AnonymousVMObject::alloc(size, "kernel_stack"));
return TRY(m_kernel_space->map_object_with_sentinel(object, VMProt::RW));
};
auto res = do_alloc();
if(res.is_error())
PANIC("ALLOC_KERNEL_REGION_FAIL", "Could not allocate a new anonymous memory region for the kernel.");
return res.value();
}

kstd::Arc<VMRegion> MemoryManager::alloc_dma_region(size_t size) {
auto do_alloc = [&]() -> ResultRet<kstd::Arc<VMRegion>> {
auto object = TRY(AnonymousVMObject::alloc_contiguous(size, "dma"));
Expand Down
9 changes: 8 additions & 1 deletion kernel/memory/MemoryManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ class MemoryManager {
*/
kstd::Arc<VMRegion> alloc_kernel_region(size_t size);

/**
* Allocates a new stack region in kernel space.
* This will allocate sentinel pages on either size of the region which will trigger a fault when accessed.
* @param size The minimum size, in bytes, of the new region.
*/
kstd::Arc<VMRegion> alloc_kernel_stack_region(size_t size);

/**
* Allocates a new contiguous anonymous region in kernel space.
* @param size The minimum size, in bytes, of the new region.
Expand Down Expand Up @@ -243,4 +250,4 @@ void liballoc_lock();
void liballoc_unlock();
void* liballoc_alloc(int);
void liballoc_afteralloc(void* ptr_alloced);
void liballoc_free(void*,int);
void liballoc_free(void*,int);
14 changes: 14 additions & 0 deletions kernel/memory/VMSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ ResultRet<kstd::Arc<VMRegion>> VMSpace::map_object(kstd::Arc<VMObject> object, V
return vmRegion;
}

ResultRet<kstd::Arc<VMRegion>> VMSpace::map_object_with_sentinel(kstd::Arc<VMObject> object, VMProt prot) {
// Create and map the region
VMSpaceRegion* region = TRY(alloc_space(object->size() + PAGE_SIZE * 2));
auto vmRegion = kstd::make_shared<VMRegion>(
object,
self(),
VirtualRange {region->start + PAGE_SIZE, object->size()},
0,
prot);
region->vmRegion = vmRegion.get();
m_page_directory.map(*vmRegion);
return vmRegion;
}

ResultRet<kstd::Arc<VMRegion>> VMSpace::map_stack(kstd::Arc<VMObject> object, VMProt prot) {
LOCK(m_lock);

Expand Down
8 changes: 8 additions & 0 deletions kernel/memory/VMSpace.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ class VMSpace: public kstd::ArcSelf<VMSpace> {
*/
ResultRet<kstd::Arc<VMRegion>> map_object(kstd::Arc<VMObject> object, VMProt prot, VirtualRange range = VirtualRange::null, VirtualAddress object_start = 0);

/**
* Allocates a new region for the given object with sentinel pages on either side.
* @param object The object to allocate a region for.
* @param prot The protection to use.
* @return The newly created region.
*/
ResultRet<kstd::Arc<VMRegion>> map_object_with_sentinel(kstd::Arc<VMObject> object, VMProt prot);

/**
* Allocates a new region for the given object near the end of the memory space.
* @param object The object to allocate a region for.
Expand Down
58 changes: 24 additions & 34 deletions kernel/memory/gdt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

#include <kernel/kstd/kstddef.h>
#include <kernel/memory/gdt.h>
#include <kernel/tasking/TSS.h>
#include <kernel/tasking/TaskManager.h>
#include <kernel/kstd/cstring.h>
#include <kernel/interrupt/isr.h>

Memory::GDTEntry gdt[GDT_ENTRIES];
Memory::GDTPointer gp;
Expand Down Expand Up @@ -49,51 +49,41 @@ void Memory::gdt_set_gate(uint32_t num, uint32_t limit, uint32_t base, bool read
gdt[num].access.bits.ring = ring;
}

void Memory::setup_tss(){
uint32_t base = (uint32_t) &TaskManager::tss;
uint32_t limit = sizeof(TaskManager::tss) - 1;
void Memory::setup_tss(int slot, TSS& tss){
uint32_t base = (uint32_t) &tss;
uint32_t limit = sizeof(tss);

// Now, add our TSS descriptor's address to the GDT.
gdt[5].limit_low = limit & 0xFFFFu;
gdt[5].base_low = (base & 0xFFFFu);
gdt[5].base_middle = (base >> 16u) & 0xFFu;
gdt[5].base_high = (base >> 24u) & 0xFFu;
gdt[5].access.bits.accessed = true; //This indicates it's a TSS and not a LDT. This is a changed meaning
gdt[5].access.bits.read_write = false; //This indicates if the TSS is busy or not. 0 for not busy
gdt[5].access.bits.direction = false; //always 0 for TSS
gdt[5].access.bits.executable = true; //For TSS this is 1 for 32bit usage, or 0 for 16bit.
gdt[5].access.bits.type = false; //indicate it is a TSS
gdt[5].access.bits.ring = 3; //same meaning
gdt[5].access.bits.present = true; //same meaning
gdt[5].flags_and_limit.bits.limit_high = (limit >> 16u) & 0xFu; //isolate top nibble
gdt[5].flags_and_limit.bits.zero = 0;
gdt[5].flags_and_limit.bits.size = false; //should leave zero according to manuals. No effect
gdt[5].flags_and_limit.bits.granularity = false; //so that our computed GDT limit is in bytes, not pages

memset(&TaskManager::tss, 0, sizeof(TSS));

TaskManager::tss.ss0 = 0x10;

TaskManager::tss.cs = 0x0b;
TaskManager::tss.ss = 0x13;
TaskManager::tss.ds = 0x13;
TaskManager::tss.es = 0x13;
TaskManager::tss.fs = 0x13;
TaskManager::tss.gs = 0x13;
gdt[slot].limit_low = limit & 0xFFFFu;
gdt[slot].base_low = (base & 0xFFFFu);
gdt[slot].base_middle = (base >> 16u) & 0xFFu;
gdt[slot].base_high = (base >> 24u) & 0xFFu;
gdt[slot].access.bits.accessed = true; //This indicates it's a TSS and not a LDT. This is a changed meaning
gdt[slot].access.bits.read_write = false; //This indicates if the TSS is busy or not. 0 for not busy
gdt[slot].access.bits.direction = false; //always 0 for TSS
gdt[slot].access.bits.executable = true; //For TSS this is 1 for 32bit usage, or 0 for 16bit.
gdt[slot].access.bits.type = false; //indicate it is a TSS
gdt[slot].access.bits.ring = 0; //same meaning
gdt[slot].access.bits.present = true; //same meaning
gdt[slot].flags_and_limit.bits.limit_high = (limit >> 16u) & 0xFu; //isolate top nibble
gdt[slot].flags_and_limit.bits.zero = 0;
gdt[slot].flags_and_limit.bits.size = false; //should leave zero according to manuals. No effect
gdt[slot].flags_and_limit.bits.granularity = false; //so that our computed GDT limit is in bytes, not pages
}

void Memory::load_gdt(){
gp.limit = (sizeof(GDTEntry) * GDT_ENTRIES) - 1;
gp.base = (uint32_t)&gdt;
gp.limit = (sizeof(GDTEntry) * GDT_ENTRIES);
gp.base = (uint32_t) &gdt;

gdt_set_gate(0, 0, 0, false, false, false, 0, false); //Null
gdt_set_gate(1, 0xFFFFF, 0, true, true, true, 0); //Kernel Code
gdt_set_gate(2, 0xFFFFF, 0, true, false, true, 0); //Kernel Data
gdt_set_gate(3, 0xFFFFF, 0, true, true, true, 3); //User code
gdt_set_gate(4, 0xFFFFF, 0, true, false, true, 3); //User data

setup_tss();
setup_tss(5, TaskManager::tss);
setup_tss(6, Interrupt::fault_tss);

gdt_flush();
asm volatile("ltr %0": : "r"((uint16_t)0x2B));
asm volatile("ltr %0": : "r"((uint16_t)0x28));
}
5 changes: 3 additions & 2 deletions kernel/memory/gdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
#pragma once

#include <kernel/kstd/types.h>
#include <kernel/tasking/TSS.h>

#define GDT_ENTRIES 6
#define GDT_ENTRIES 7

namespace Memory {
union GDTEntryAccessByte {
Expand Down Expand Up @@ -63,7 +64,7 @@ namespace Memory {

void gdt_set_gate(uint32_t num, uint32_t limit, uint32_t base, bool read_write, bool executable, bool type, uint8_t ring, bool present = true, bool accessed = false);

void setup_tss();
void setup_tss(int slot, TSS& tss);
extern "C" void load_gdt();
extern "C" void gdt_flush();
}
14 changes: 12 additions & 2 deletions kernel/tasking/TaskManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ void TaskManager::init(){
KLog::dbg("TaskManager", "Initializing tasking...");
processes = new kstd::vector<Process*>();

//Setup TSS
memset(&tss, 0, sizeof(TSS));
tss.ss0 = 0x10;
tss.cs = 0x0b;
tss.ss = 0x13;
tss.ds = 0x13;
tss.es = 0x13;
tss.fs = 0x13;
tss.gs = 0x13;

//Create kernel process
kernel_process = Process::create_kernel("[kernel]", kidle);
processes->push_back(kernel_process);
Expand Down Expand Up @@ -368,11 +378,11 @@ void TaskManager::preempt(){

Processor::save_fpu_state((void*&) old_thread->fpu_state);
old_thread.reset();

preempt_asm(old_esp, new_esp, cur_thread->page_directory()->entries_physaddr());
Processor::load_fpu_state((void*&) cur_thread->fpu_state);
}


preempt_finish();
}

Expand All @@ -381,4 +391,4 @@ void TaskManager::preempt_finish() {
g_tasking_lock.release();
leave_critical();
cur_thread->handle_pending_signal();
}
}
Loading

0 comments on commit 7e1eeb3

Please sign in to comment.