Kernel: Unify IDT and GDT code between x86_64 and x86_32

The code is pretty much the same, so there are just couple macros
differiating initialization.
This commit is contained in:
2024-03-26 13:59:09 +02:00
parent af050cc729
commit 1943c3e7a1
12 changed files with 335 additions and 147 deletions

View File

@@ -1,21 +0,0 @@
#include <kernel/GDT.h>
namespace Kernel
{
GDT* GDT::create()
{
ASSERT_NOT_REACHED();
}
void GDT::write_entry(uint8_t, uint32_t, uint32_t, uint8_t, uint8_t)
{
ASSERT_NOT_REACHED();
}
void GDT::write_tss()
{
ASSERT_NOT_REACHED();
}
}

View File

@@ -1,31 +0,0 @@
#include <kernel/IDT.h>
namespace Kernel
{
IDT* IDT::create()
{
ASSERT_NOT_REACHED();
}
[[noreturn]] void IDT::force_triple_fault()
{
ASSERT_NOT_REACHED();
}
void IDT::register_irq_handler(uint8_t, Interruptable*)
{
ASSERT_NOT_REACHED();
}
void IDT::register_interrupt_handler(uint8_t, void (*)())
{
ASSERT_NOT_REACHED();
}
void IDT::register_syscall_handler(uint8_t, void (*)())
{
ASSERT_NOT_REACHED();
}
}

View File

@@ -0,0 +1,134 @@
isr_stub:
ud2
pusha
call cpp_isr_handler
popa
iret
irq_stub:
ud2
pusha
call cpp_irq_handler
popa
iret
// arguments in EAX, EBX, ECX, EDX, ESI, EDI
.global syscall_asm
syscall_asm:
ud2
pusha
pushl %esp
addl $36, (%esp)
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
call cpp_syscall_handler
addl $60, %esp
popl %edi
popl %esi
popl %ebp
addl $4, %esp
popl %ebx
popl %edx
popl %ecx
addl $4, %esp
iret
.macro isr n
.global isr\n
isr\n:
pushl $0
pushl $\n
jmp isr_stub
.endm
.macro isr_err n
.global isr\n
isr\n:
pushl $\n
jmp isr_stub
.endm
.macro irq n
.global irq\n
irq\n:
pushl $0
pushl $\n
jmp irq_stub
.endm
isr 0
isr 1
isr 2
isr 3
isr 4
isr 5
isr 6
isr 7
isr_err 8
isr 9
isr_err 10
isr_err 11
isr_err 12
isr_err 13
isr_err 14
isr 15
isr 16
isr_err 17
isr 18
isr 19
isr 20
isr 21
isr 22
isr 23
isr 24
isr 25
isr 26
isr 27
isr 28
isr 29
isr 30
isr 31
irq 0
irq 1
irq 2
irq 3
irq 4
irq 5
irq 6
irq 7
irq 8
irq 9
irq 10
irq 11
irq 12
irq 13
irq 14
irq 15
irq 16
irq 17
irq 18
irq 19
irq 20
irq 21
irq 22
irq 23
irq 24
irq 25
irq 26
irq 27
irq 28
irq 29
irq 30
irq 31
irq 32

View File

@@ -1,56 +0,0 @@
#include <kernel/GDT.h>
#include <kernel/Debug.h>
#include <string.h>
namespace Kernel
{
GDT* GDT::create()
{
auto* gdt = new GDT();
ASSERT(gdt);
gdt->write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
gdt->write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
gdt->write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
gdt->write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xA); // user code
gdt->write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
gdt->write_tss();
return gdt;
}
void GDT::write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
{
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
uint8_t idx = offset / sizeof(SegmentDescriptor);
auto& desc = m_gdt[idx];
desc.base1 = (base >> 0) & 0xFFFF;
desc.base2 = (base >> 16) & 0xFF;
desc.base3 = (base >> 24) & 0xFF;
desc.limit1 = (limit >> 0) & 0xFFFF;
desc.limit2 = (limit >> 16) & 0x0F;
desc.access = access & 0xFF;
desc.flags = flags & 0x0F;
}
void GDT::write_tss()
{
memset(&m_tss, 0x00, sizeof(TaskStateSegment));
m_tss.iopb = sizeof(TaskStateSegment);
uint64_t base = reinterpret_cast<uint64_t>(&m_tss);
write_entry(0x28, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
auto& desc = m_gdt[0x30 / sizeof(SegmentDescriptor)];
desc.low = base >> 32;
desc.high = 0;
}
}

View File

@@ -1,398 +0,0 @@
#include <BAN/Array.h>
#include <BAN/Errors.h>
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
#include <kernel/InterruptStack.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Panic.h>
#include <kernel/Process.h>
#include <kernel/Scheduler.h>
#include <kernel/Timer/PIT.h>
#define ISR_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
#define IRQ_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31) X(32)
namespace Kernel
{
struct Registers
{
uint64_t rsp;
uint64_t rip;
uint64_t rflags;
uint64_t cr4;
uint64_t cr3;
uint64_t cr2;
uint64_t cr0;
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rsi;
uint64_t rdi;
uint64_t rbp;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
};
#define X(num) 1 +
static BAN::Array<Interruptable*, IRQ_LIST_X 0> s_interruptables;
#undef X
enum ISR
{
DivisionError,
Debug,
NonMaskableInterrupt,
Breakpoint,
Overflow,
BoundRangeException,
InvalidOpcode,
DeviceNotAvailable,
DoubleFault,
CoprocessorSegmentOverrun,
InvalidTSS,
SegmentNotPresent,
StackSegmentFault,
GeneralProtectionFault,
PageFault,
UnknownException0x0F,
x87FloatingPointException,
AlignmentCheck,
MachineCheck,
SIMDFloatingPointException,
VirtualizationException,
ControlProtectionException,
UnknownException0x16,
UnknownException0x17,
UnknownException0x18,
UnknownException0x19,
UnknownException0x1A,
UnknownException0x1B,
HypervisorInjectionException,
VMMCommunicationException,
SecurityException,
UnkownException0x1F,
};
struct PageFaultError
{
union
{
uint32_t raw;
struct
{
uint32_t present : 1;
uint32_t write : 1;
uint32_t userspace : 1;
uint32_t reserved_write : 1;
uint32_t instruction : 1;
uint32_t protection_key : 1;
uint32_t shadow_stack : 1;
uint32_t reserved1 : 8;
uint32_t sgx_violation : 1;
uint32_t reserved2 : 16;
};
};
};
static_assert(sizeof(PageFaultError) == 4);
static const char* isr_exceptions[] =
{
"Division Error",
"Debug",
"Non-maskable Interrupt",
"Breakpoint",
"Overflow",
"Bound Range Exception",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack-Segment Fault",
"General Protection Fault",
"Page Fault",
"Unknown Exception 0x0F",
"x87 Floating-Point Exception",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Unknown Exception 0x16",
"Unknown Exception 0x17",
"Unknown Exception 0x18",
"Unknown Exception 0x19",
"Unknown Exception 0x1A",
"Unknown Exception 0x1B",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Unkown Exception 0x1F",
};
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs)
{
if (g_paniced)
{
dprintln("Processor {} halted", Processor::current_id());
InterruptController::get().broadcast_ipi();
asm volatile("cli; 1: hlt; jmp 1b");
}
#if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace)
Thread::current().save_sse();
#endif
pid_t tid = Scheduler::current_tid();
pid_t pid = tid ? Process::current().pid() : 0;
if (tid)
{
Thread::current().set_return_sp(interrupt_stack.sp);
Thread::current().set_return_ip(interrupt_stack.ip);
if (isr == ISR::PageFault)
{
// Check if stack is OOB
auto& stack = Thread::current().stack();
auto& istack = Thread::current().interrupt_stack();
if (stack.vaddr() < interrupt_stack.sp && interrupt_stack.sp <= stack.vaddr() + stack.size())
; // using normal stack
else if (istack.vaddr() < interrupt_stack.sp && interrupt_stack.sp <= istack.vaddr() + istack.size())
; // using interrupt stack
else
{
derrorln("Stack pointer out of bounds!");
derrorln("rip {H}", interrupt_stack.ip);
derrorln("rsp {H}, stack {H}->{H}, istack {H}->{H}",
interrupt_stack.sp,
stack.vaddr(), stack.vaddr() + stack.size(),
istack.vaddr(), istack.vaddr() + istack.size()
);
Thread::current().handle_signal(SIGKILL);
goto done;
}
// Try demand paging on non present pages
PageFaultError page_fault_error;
page_fault_error.raw = error;
if (!page_fault_error.present)
{
asm volatile("sti");
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2);
asm volatile("cli");
if (!result.is_error() && result.value())
goto done;
if (result.is_error())
{
dwarnln("Demand paging: {}", result.error());
Thread::current().handle_signal(SIGKILL);
goto done;
}
}
}
#if __enable_sse
else if (isr == ISR::DeviceNotAvailable)
{
asm volatile(
"movq %cr0, %rax;"
"andq $~(1 << 3), %rax;"
"movq %rax, %cr0;"
);
if (auto* current = &Thread::current(); current != Thread::sse_thread())
{
if (auto* sse = Thread::sse_thread())
sse->save_sse();
current->load_sse();
}
goto done;
}
#endif
}
if (PageTable::current().get_page_flags(interrupt_stack.ip & PAGE_ADDR_MASK) & PageTable::Flags::Present)
{
auto* machine_code = (const uint8_t*)interrupt_stack.ip;
dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
machine_code[0],
machine_code[1],
machine_code[2],
machine_code[3],
machine_code[4],
machine_code[5],
machine_code[6],
machine_code[7]
);
}
dwarnln(
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
"Register dump\r\n"
"rax=0x{16H}, rbx=0x{16H}, rcx=0x{16H}, rdx=0x{16H}\r\n"
"rsp=0x{16H}, rbp=0x{16H}, rdi=0x{16H}, rsi=0x{16H}\r\n"
"rip=0x{16H}, rflags=0x{16H}\r\n"
"cr0=0x{16H}, cr2=0x{16H}, cr3=0x{16H}, cr4=0x{16H}",
isr_exceptions[isr], error, pid, tid,
regs->rax, regs->rbx, regs->rcx, regs->rdx,
regs->rsp, regs->rbp, regs->rdi, regs->rsi,
regs->rip, regs->rflags,
regs->cr0, regs->cr2, regs->cr3, regs->cr4
);
if (isr == ISR::PageFault)
PageTable::current().debug_dump();
Debug::dump_stack_trace();
if (tid && Thread::current().is_userspace())
{
// TODO: Confirm and fix the exception to signal mappings
int signal = 0;
switch (isr)
{
case ISR::DivisionError:
case ISR::SIMDFloatingPointException:
case ISR::x87FloatingPointException:
signal = SIGFPE;
break;
case ISR::AlignmentCheck:
signal = SIGBUS;
break;
case ISR::InvalidOpcode:
signal = SIGILL;
break;
case ISR::PageFault:
signal = SIGSEGV;
break;
default:
dwarnln("Unhandled exception");
signal = SIGABRT;
break;
}
Thread::current().handle_signal(signal);
}
else
{
panic("Unhandled exception");
}
ASSERT(Thread::current().state() != Thread::State::Terminated);
done:
return;
}
extern "C" void cpp_irq_handler(uint64_t irq, InterruptStack& interrupt_stack)
{
if (g_paniced)
{
dprintln("Processor {} halted", Processor::current_id());
InterruptController::get().broadcast_ipi();
asm volatile("cli; 1: hlt; jmp 1b");
}
if (Scheduler::current_tid())
{
Thread::current().set_return_sp(interrupt_stack.sp);
Thread::current().set_return_ip(interrupt_stack.ip);
}
if (!InterruptController::get().is_in_service(irq))
dprintln("spurious irq 0x{2H}", irq);
else
{
InterruptController::get().eoi(irq);
if (irq == IRQ_IPI)
Scheduler::get().reschedule();
else if (auto* handler = s_interruptables[irq])
handler->handle_irq();
else
dprintln("no handler for irq 0x{2H}", irq);
}
Scheduler::get().reschedule_if_idling();
ASSERT(Thread::current().state() != Thread::State::Terminated);
}
void IDT::register_interrupt_handler(uint8_t index, void (*handler)())
{
auto& descriptor = m_idt[index];
descriptor.offset1 = (uint16_t)((uint64_t)handler >> 0);
descriptor.offset2 = (uint16_t)((uint64_t)handler >> 16);
descriptor.offset3 = (uint32_t)((uint64_t)handler >> 32);
descriptor.selector = 0x08;
descriptor.IST = 0;
descriptor.flags = 0x8E;
}
void IDT::register_syscall_handler(uint8_t index, void (*handler)())
{
register_interrupt_handler(index, handler);
m_idt[index].flags = 0xEE;
}
void IDT::register_irq_handler(uint8_t irq, Interruptable* interruptable)
{
if (irq > s_interruptables.size())
Kernel::panic("Trying to assign handler for irq {} while only {} are supported", irq, s_interruptables.size());
s_interruptables[irq] = interruptable;
}
#define X(num) extern "C" void isr ## num();
ISR_LIST_X
#undef X
#define X(num) extern "C" void irq ## num();
IRQ_LIST_X
#undef X
extern "C" void syscall_asm();
IDT* IDT::create()
{
auto* idt = new IDT();
ASSERT(idt);
memset(idt->m_idt.data(), 0x00, 0x100 * sizeof(GateDescriptor));
#define X(num) idt->register_interrupt_handler(num, isr ## num);
ISR_LIST_X
#undef X
#define X(num) idt->register_interrupt_handler(IRQ_VECTOR_BASE + num, irq ## num);
IRQ_LIST_X
#undef X
idt->register_syscall_handler(0x80, syscall_asm);
return idt;
}
[[noreturn]] void IDT::force_triple_fault()
{
// load 0 sized IDT and trigger an interrupt to force triple fault
Processor::set_interrupt_state(InterruptState::Disabled);
Processor::idt().m_idtr.size = 0;
Processor::idt().load();
asm volatile("int $0x00");
ASSERT_NOT_REACHED();
}
}