diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 89c601ee..9cf0febf 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -26,6 +26,9 @@ if("${ARCH}" MATCHES "x86_64") src/arch/x86_64/CPU.cpp src/arch/x86_64/Timer.cpp src/arch/x86_64/Thread.cpp + src/arch/x86_64/init/GDT.cpp + src/arch/x86_64/init/IDT.cpp + src/arch/x86_64/init/PIC.cpp ) endif() diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 0338fd96..56606059 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -2,12 +2,10 @@ #include "Log.h" #include "arch/Timer.h" #include "arch/x86_64/CPU.h" -#include "arch/x86_64/IO.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #include #include -#include #include #include #include @@ -16,280 +14,11 @@ extern "C" void enable_sse(); extern "C" void enable_write_protect(); extern "C" void enable_nx(); -// FIXME: Split this into separate files? - -// GDT code and definitions - -struct [[gnu::packed]] GDTR -{ - u16 size; - u64 offset; -}; - -static_assert(sizeof(GDTR) == 10UL); - -struct [[gnu::packed]] GDTEntry -{ - u16 limit0; - u16 base0; - u8 base1; - u8 access; - u8 limit1_flags; - u8 base2; -}; - -static_assert(sizeof(GDTEntry) == 8UL); - -struct [[gnu::packed]] HighGDTEntry -{ - u32 base_high; - u32 reserved; -}; - -static_assert(sizeof(HighGDTEntry) == 8UL); - -struct [[gnu::packed]] TSS -{ - u32 reserved0; - u64 rsp[3]; - u64 reserved1; - u64 ist[7]; - u64 reserved2; - u16 reserved3; - u16 iomap_base; -}; - -static_assert(sizeof(TSS) == 104UL); - -struct [[gnu::packed]] alignas(4096) GlobalDescriptorTable -{ - GDTEntry null; - GDTEntry kernel_code; - GDTEntry kernel_data; - GDTEntry user_code; - GDTEntry user_data; - GDTEntry tss; - HighGDTEntry tss2; -}; - -static TSS task_state_segment; - -static GlobalDescriptorTable gdt = {{0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00}, - {0xffff, 0x0000, 0x00, 0x9a, 0xaf, 0x00}, - {0xffff, 0x0000, 0x00, 0x92, 0xcf, 0x00}, - {0xffff, 0x0000, 0x00, 0xfa, 0xaf, 0x00}, - {0xffff, 0x0000, 0x00, 0xf2, 0xcf, 0x00}, - {0x0000, 0x0000, 0x00, 0xe9, 0x0f, 0x00}, - {0x00000000, 0x00000000}}; - -extern "C" void load_gdt(GDTR* gdtr); -extern "C" void load_tr(int segment); - -static void set_base(GDTEntry* entry, u32 base) -{ - entry->base0 = (base & 0xFFFF); - entry->base1 = (base >> 16) & 0xFF; - entry->base2 = (u8)((base >> 24) & 0xFF); -} - -static void set_limit(GDTEntry* entry, u32 limit) -{ - expect(limit <= 0xFFFFF, "Limit too big for a GDT entry"); - entry->limit0 = limit & 0xFFFF; - entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF); -} - -static void set_tss_base(GDTEntry* tss1, HighGDTEntry* tss2, u64 addr) -{ - set_base(tss1, addr & 0xffffffff); - tss2->base_high = (u32)(addr >> 32); -} - -static void setup_tss() -{ - memset(&task_state_segment, 0, sizeof(TSS)); - task_state_segment.iomap_base = sizeof(TSS); - set_tss_base(&gdt.tss, &gdt.tss2, (u64)&task_state_segment); - set_limit(&gdt.tss, sizeof(TSS) - 1); -} - -static void setup_gdt() -{ - static GDTR gdtr; - gdtr.offset = (u64)&gdt; - gdtr.size = sizeof(GlobalDescriptorTable); - setup_tss(); - load_gdt(&gdtr); - load_tr(0x2b); -} - -// PIC code - -#define PIC1_COMMAND 0x20 -#define PIC1_DATA 0x21 -#define PIC2_COMMAND 0xA0 -#define PIC2_DATA 0xA1 -#define PIC_EOI 0x20 - -#define ICW1_INIT 0x10 -#define ICW1_ICW4 0x01 -#define ICW4_8086 0x01 - -#define io_delay() IO::outb(0x80, 0) - -static void remap_pic() -{ - IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); - io_delay(); - IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); - io_delay(); - - IO::outb(PIC1_DATA, 0x20); - io_delay(); - - IO::outb(PIC2_DATA, 0x28); - io_delay(); - - IO::outb(PIC1_DATA, 4); - io_delay(); - IO::outb(PIC2_DATA, 2); - io_delay(); - - IO::outb(PIC1_DATA, ICW4_8086); - io_delay(); - IO::outb(PIC2_DATA, ICW4_8086); - io_delay(); - - IO::outb(PIC1_DATA, 0b11111110); - io_delay(); - IO::outb(PIC2_DATA, 0b11111111); -} - -static void pic_eoi(unsigned char irq) -{ - if (irq >= 8) IO::outb(PIC2_COMMAND, PIC_EOI); - IO::outb(PIC1_COMMAND, PIC_EOI); -} - -static void pic_eoi(Registers* regs) -{ - pic_eoi((unsigned char)(regs->error)); // On IRQs, the error code is the IRQ number -} - -// IDT code and definitions - -struct IDTEntry -{ - u16 offset0; - u16 selector; - u8 ist; - u8 type_attr; - u16 offset1; - u32 offset2; - u32 ignore; - void set_offset(u64 offset); - u64 get_offset() const; -}; - -static_assert(sizeof(IDTEntry) == 16UL); - -void IDTEntry::set_offset(u64 offset) -{ - offset0 = (u16)(offset & 0x000000000000ffff); - offset1 = (u16)((offset & 0x00000000ffff0000) >> 16); - offset2 = (u32)((offset & 0xffffffff00000000) >> 32); -} - -u64 IDTEntry::get_offset() const -{ - u64 offset = 0; - offset |= (u64)offset0; - offset |= (u64)offset1 << 16; - offset |= (u64)offset2 << 32; - return offset; -} - -static IDTEntry idt[256]; - -#define IDT_TA_InterruptGate 0b10001110 -#define IDT_TA_UserInterruptGate 0b11101110 -#define IDT_TA_TrapGate 0b10001111 - -struct [[gnu::packed]] IDTR -{ - u16 limit; - u64 offset; -}; - -static_assert(sizeof(IDTR) == 10UL); - -static void idt_add_handler(short num, void* handler, u8 type_attr) -{ - check(handler != nullptr); - expect(num < 256, "IDT can only hold up to 256 entries"); - IDTEntry* const entry_for_handler = &idt[num]; - entry_for_handler->selector = 0x08; - entry_for_handler->type_attr = type_attr; - entry_for_handler->set_offset((u64)handler); -} - -#define INT(x) extern "C" void _isr##x() -#define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate) -#define IRQ(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_InterruptGate) - -INT(0); -INT(1); -INT(2); -INT(3); -INT(4); -INT(5); -INT(6); -INT(7); -INT(8); -INT(10); -INT(11); -INT(12); -INT(13); -INT(14); -INT(16); -INT(17); -INT(18); -INT(19); -INT(20); -INT(21); -INT(32); - -static void setup_idt() -{ - memset(idt, 0, sizeof(idt)); - - TRAP(0); - TRAP(1); - TRAP(2); - TRAP(3); - TRAP(4); - TRAP(5); - TRAP(6); - TRAP(7); - TRAP(8); - TRAP(10); - TRAP(11); - TRAP(12); - TRAP(13); - TRAP(14); - TRAP(16); - TRAP(17); - TRAP(18); - TRAP(19); - TRAP(20); - TRAP(21); - IRQ(32); - - static IDTR idtr; - idtr.limit = 0x0FFF; - idtr.offset = (u64)idt; - asm volatile("lidt %0" : : "m"(idtr)); -} +extern void setup_gdt(); +extern void remap_pic(); +extern void pic_eoi(unsigned char irq); +extern void pic_eoi(Registers* regs); +extern void setup_idt(); // Interrupt handling diff --git a/kernel/src/arch/x86_64/CPU.h b/kernel/src/arch/x86_64/CPU.h index a06de7b9..c5747521 100644 --- a/kernel/src/arch/x86_64/CPU.h +++ b/kernel/src/arch/x86_64/CPU.h @@ -7,4 +7,19 @@ struct Registers // Saved CPU registers for x86-64 u64 rbp, rdi, rsi, rdx, rcx, rbx, rax; u64 isr, error; u64 rip, cs, rflags, rsp, ss; -}; \ No newline at end of file +}; + +struct [[gnu::packed]] TSS +{ + u32 reserved0; + u64 rsp[3]; + u64 reserved1; + u64 ist[7]; + u64 reserved2; + u16 reserved3; + u16 iomap_base; +}; + +static_assert(sizeof(TSS) == 104UL); + +extern TSS task_state_segment; \ No newline at end of file diff --git a/kernel/src/arch/x86_64/init/GDT.cpp b/kernel/src/arch/x86_64/init/GDT.cpp new file mode 100644 index 00000000..ca48ef5c --- /dev/null +++ b/kernel/src/arch/x86_64/init/GDT.cpp @@ -0,0 +1,94 @@ +#include "arch/x86_64/CPU.h" +#include +#include +#include + +struct [[gnu::packed]] GDTR +{ + u16 size; + u64 offset; +}; + +static_assert(sizeof(GDTR) == 10UL); + +struct [[gnu::packed]] GDTEntry +{ + u16 limit0; + u16 base0; + u8 base1; + u8 access; + u8 limit1_flags; + u8 base2; +}; + +static_assert(sizeof(GDTEntry) == 8UL); + +struct [[gnu::packed]] HighGDTEntry +{ + u32 base_high; + u32 reserved; +}; + +static_assert(sizeof(HighGDTEntry) == 8UL); + +struct [[gnu::packed]] alignas(4096) GlobalDescriptorTable +{ + GDTEntry null; + GDTEntry kernel_code; + GDTEntry kernel_data; + GDTEntry user_code; + GDTEntry user_data; + GDTEntry tss; + HighGDTEntry tss2; +}; + +TSS task_state_segment; + +static GlobalDescriptorTable gdt = {{0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00}, + {0xffff, 0x0000, 0x00, 0x9a, 0xaf, 0x00}, + {0xffff, 0x0000, 0x00, 0x92, 0xcf, 0x00}, + {0xffff, 0x0000, 0x00, 0xfa, 0xaf, 0x00}, + {0xffff, 0x0000, 0x00, 0xf2, 0xcf, 0x00}, + {0x0000, 0x0000, 0x00, 0xe9, 0x0f, 0x00}, + {0x00000000, 0x00000000}}; + +extern "C" void load_gdt(GDTR* gdtr); +extern "C" void load_tr(int segment); + +static void set_base(GDTEntry* entry, u32 base) +{ + entry->base0 = (base & 0xFFFF); + entry->base1 = (base >> 16) & 0xFF; + entry->base2 = (u8)((base >> 24) & 0xFF); +} + +static void set_limit(GDTEntry* entry, u32 limit) +{ + expect(limit <= 0xFFFFF, "Limit too big for a GDT entry"); + entry->limit0 = limit & 0xFFFF; + entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF); +} + +static void set_tss_base(GDTEntry* tss1, HighGDTEntry* tss2, u64 addr) +{ + set_base(tss1, addr & 0xffffffff); + tss2->base_high = (u32)(addr >> 32); +} + +static void setup_tss() +{ + memset(&task_state_segment, 0, sizeof(TSS)); + task_state_segment.iomap_base = sizeof(TSS); + set_tss_base(&gdt.tss, &gdt.tss2, (u64)&task_state_segment); + set_limit(&gdt.tss, sizeof(TSS) - 1); +} + +void setup_gdt() +{ + static GDTR gdtr; + gdtr.offset = (u64)&gdt; + gdtr.size = sizeof(GlobalDescriptorTable); + setup_tss(); + load_gdt(&gdtr); + load_tr(0x2b); +} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/init/IDT.cpp b/kernel/src/arch/x86_64/init/IDT.cpp new file mode 100644 index 00000000..eb1d01e6 --- /dev/null +++ b/kernel/src/arch/x86_64/init/IDT.cpp @@ -0,0 +1,116 @@ +#include +#include +#include + +struct IDTEntry +{ + u16 offset0; + u16 selector; + u8 ist; + u8 type_attr; + u16 offset1; + u32 offset2; + u32 ignore; + void set_offset(u64 offset); + u64 get_offset() const; +}; + +static_assert(sizeof(IDTEntry) == 16UL); + +void IDTEntry::set_offset(u64 offset) +{ + offset0 = (u16)(offset & 0x000000000000ffff); + offset1 = (u16)((offset & 0x00000000ffff0000) >> 16); + offset2 = (u32)((offset & 0xffffffff00000000) >> 32); +} + +u64 IDTEntry::get_offset() const +{ + u64 offset = 0; + offset |= (u64)offset0; + offset |= (u64)offset1 << 16; + offset |= (u64)offset2 << 32; + return offset; +} + +static IDTEntry idt[256]; + +#define IDT_TA_InterruptGate 0b10001110 +#define IDT_TA_UserInterruptGate 0b11101110 +#define IDT_TA_TrapGate 0b10001111 + +struct [[gnu::packed]] IDTR +{ + u16 limit; + u64 offset; +}; + +static_assert(sizeof(IDTR) == 10UL); + +static void idt_add_handler(short num, void* handler, u8 type_attr) +{ + check(handler != nullptr); + expect(num < 256, "IDT can only hold up to 256 entries"); + IDTEntry* const entry_for_handler = &idt[num]; + entry_for_handler->selector = 0x08; + entry_for_handler->type_attr = type_attr; + entry_for_handler->set_offset((u64)handler); +} + +#define INT(x) extern "C" void _isr##x() +#define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate) +#define IRQ(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_InterruptGate) + +INT(0); +INT(1); +INT(2); +INT(3); +INT(4); +INT(5); +INT(6); +INT(7); +INT(8); +INT(10); +INT(11); +INT(12); +INT(13); +INT(14); +INT(16); +INT(17); +INT(18); +INT(19); +INT(20); +INT(21); +INT(32); + +void setup_idt() +{ + memset(idt, 0, sizeof(idt)); + + TRAP(0); + TRAP(1); + TRAP(2); + TRAP(3); + TRAP(4); + TRAP(5); + TRAP(6); + TRAP(7); + TRAP(8); + TRAP(10); + TRAP(11); + TRAP(12); + TRAP(13); + TRAP(14); + TRAP(16); + TRAP(17); + TRAP(18); + TRAP(19); + TRAP(20); + TRAP(21); + IRQ(32); + + static IDTR idtr; + idtr.limit = 0x0FFF; + idtr.offset = (u64)idt; + asm volatile("lidt %0" : : "m"(idtr)); +} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/init/PIC.cpp b/kernel/src/arch/x86_64/init/PIC.cpp new file mode 100644 index 00000000..c7fd1068 --- /dev/null +++ b/kernel/src/arch/x86_64/init/PIC.cpp @@ -0,0 +1,54 @@ +#include "arch/x86_64/CPU.h" +#include "arch/x86_64/IO.h" +#include + +#define PIC1_COMMAND 0x20 +#define PIC1_DATA 0x21 +#define PIC2_COMMAND 0xA0 +#define PIC2_DATA 0xA1 +#define PIC_EOI 0x20 + +#define ICW1_INIT 0x10 +#define ICW1_ICW4 0x01 +#define ICW4_8086 0x01 + +#define io_delay() IO::outb(0x80, 0) + +void remap_pic() +{ + IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + io_delay(); + IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_delay(); + + IO::outb(PIC1_DATA, 0x20); + io_delay(); + + IO::outb(PIC2_DATA, 0x28); + io_delay(); + + IO::outb(PIC1_DATA, 4); + io_delay(); + IO::outb(PIC2_DATA, 2); + io_delay(); + + IO::outb(PIC1_DATA, ICW4_8086); + io_delay(); + IO::outb(PIC2_DATA, ICW4_8086); + io_delay(); + + IO::outb(PIC1_DATA, 0b11111110); + io_delay(); + IO::outb(PIC2_DATA, 0b11111111); +} + +void pic_eoi(unsigned char irq) +{ + if (irq >= 8) IO::outb(PIC2_COMMAND, PIC_EOI); + IO::outb(PIC1_COMMAND, PIC_EOI); +} + +void pic_eoi(Registers* regs) +{ + pic_eoi((unsigned char)(regs->error)); // On IRQs, the error code is the IRQ number +} \ No newline at end of file