diff options
Diffstat (limited to 'src/pOS/arch')
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/asm/gdt.s | 15 | ||||
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/asm/idt.s | 5 | ||||
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/gdt.cpp | 56 | ||||
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/idt.cpp | 35 | ||||
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/irq.cpp | 86 | ||||
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/isrs.cpp | 95 | ||||
-rw-r--r-- | src/pOS/arch/x86/kernel/interrupts/pic.cpp | 33 |
7 files changed, 325 insertions, 0 deletions
diff --git a/src/pOS/arch/x86/kernel/interrupts/asm/gdt.s b/src/pOS/arch/x86/kernel/interrupts/asm/gdt.s new file mode 100644 index 0000000..9480ef1 --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/asm/gdt.s @@ -0,0 +1,15 @@ +[GLOBAL gdt_flush] + +gdt_flush: + mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. + lgdt [eax] ; Load the new GDT pointer + + mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment + mov ds, ax ; Load all data segment selectors + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp 0x08:.flush ; 0x08 is the offset to our code segment +.flush: + ret diff --git a/src/pOS/arch/x86/kernel/interrupts/asm/idt.s b/src/pOS/arch/x86/kernel/interrupts/asm/idt.s new file mode 100644 index 0000000..f6bd58e --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/asm/idt.s @@ -0,0 +1,5 @@ +[GLOBAL idt_flush] +idt_flush: + mov eax, [esp + 4] + lidt [eax] + ret diff --git a/src/pOS/arch/x86/kernel/interrupts/gdt.cpp b/src/pOS/arch/x86/kernel/interrupts/gdt.cpp new file mode 100644 index 0000000..0774421 --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/gdt.cpp @@ -0,0 +1,56 @@ +#include <kernel/interrupts/gdt.h> + +gdt_entry_t GDT::gdt_entries[GDT_NUM_DESCRIPTORS]; +gdt_ptr_t gdt_ptr; + +int GDT::init(void) { + memset(gdt_entries, 0, sizeof(gdt_entries)); + + gdt_ptr.limit = sizeof(gdt_entries) - 1; + gdt_ptr.base = (uint32_t)gdt_entries; + + // NULL Segment + set_entry(0, 0, 0, 0, 0); + /* Kernel code, access(9A = 1 00 1 1 0 1 0) + 1 present + 00 ring 0 + 1 always 1 + 1 code segment + 0 can be executed by ring lower or equal to DPL, + 1 code segment is readable + 0 access bit, always 0, cpu set this to 1 when accessing this sector*/ + + set_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); + /* Kernel data, access(92 = 1 00 1 0 0 1 0) + Only differ at the fifth bit(counting from least insignificant bit), 0 means it's a data segment.*/ + + set_entry(2, 0, 0xFFFFFFFF, 0x92, 0xCF); + // User code and data segments, only differ in ring number(ring 3) + set_entry(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); + set_entry(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); + + gdt_flush((uint32_t)(&gdt_ptr)); + + return 0; +} + +int GDT::set_entry(int index, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { + gdt_entry_t& entry = gdt_entries[index]; + + // Low 16 bits, middle 8 bits and high 8 bits of base + entry.base_low = base & 0xFFFF; + entry.base_middle = (base >> 16) & 0xFF; + entry.base_high = (base >> 24 & 0xFF); + + /* 16 bits and high 4 bits of limit, since the high 4 bits of limits is between granularity and access, and we don't have 4 bit variable, + low 4 bits of granularity actually represents high 4 bits of limits. It's weird, I know.*/ + entry.limit_low = limit & 0xFFFF; + entry.granularity = (limit >> 16) & 0x0F; + + entry.access = access; + + // Only need the high 4 bits of gran + entry.granularity = entry.granularity | (gran & 0xF0); + + return 0; +} diff --git a/src/pOS/arch/x86/kernel/interrupts/idt.cpp b/src/pOS/arch/x86/kernel/interrupts/idt.cpp new file mode 100644 index 0000000..b44d570 --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/idt.cpp @@ -0,0 +1,35 @@ +#include <kernel/interrupts/idt.h> + +idt_entry_t IDT::idt_entries[NUM_IDT_ENTRIES]; +idt_ptr_t idt_ptr; + +int IDT::init(void) { + memset(idt_entries, 0, sizeof(idt_entries)); + + idt_ptr.base = (uint32_t)idt_entries; + idt_ptr.limit = sizeof(idt_entries) - 1; + + PIC::init(); + + IRQ::install(); + + idt_flush((uint32_t)&(idt_ptr)); + asm volatile("sti"); + + return 0; +} + +int IDT::set_entry(int index, uint32_t base, uint16_t sel, uint8_t flags) { + idt_entry_t& entry = idt_entries[index]; + + entry.base_lo = base & 0xFFFF; + entry.base_hi = (base >> 16) & 0xFFFF; + + entry.always0 = 0; + + entry.sel = sel; + + entry.flags = flags | 0x60; + + return 0; +} diff --git a/src/pOS/arch/x86/kernel/interrupts/irq.cpp b/src/pOS/arch/x86/kernel/interrupts/irq.cpp new file mode 100644 index 0000000..70a577d --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/irq.cpp @@ -0,0 +1,86 @@ +#include <kernel/interrupts/irq.h> + +static void* irq_routines[16] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int IRQ::install_handler(int irq, void (*handler)(struct regs *r)) +{ + irq_routines[irq] = reinterpret_cast<void *&>(handler); + + return 0; +} + +int IRQ::uninstall_handler(int irq) +{ + irq_routines[irq] = 0; + + return 0; +} + +/* Remaps the IRQs so they are usable */ +int IRQ::remap(void) +{ + System::outb(0x20, 0x11); + System::outb(0xA0, 0x11); + System::outb(0x21, 0x20); + System::outb(0xA1, 0x28); + System::outb(0x21, 0x04); + System::outb(0xA1, 0x02); + System::outb(0x21, 0x01); + System::outb(0xA1, 0x01); + System::outb(0x21, 0x0); + System::outb(0xA1, 0x0); + + return 0; +} + +int IRQ::install(void) +{ + + remap(); + + /* Do with macro */ + IDT::set_entry(32, (unsigned)irq0, 0x08, 0x8E); + IDT::set_entry(33, (unsigned)irq1, 0x08, 0x8E); + IDT::set_entry(34, (unsigned)irq2, 0x08, 0x8E); + IDT::set_entry(35, (unsigned)irq3, 0x08, 0x8E); + IDT::set_entry(36, (unsigned)irq4, 0x08, 0x8E); + IDT::set_entry(37, (unsigned)irq5, 0x08, 0x8E); + IDT::set_entry(38, (unsigned)irq6, 0x08, 0x8E); + IDT::set_entry(39, (unsigned)irq7, 0x08, 0x8E); + + IDT::set_entry(40, (unsigned)irq8, 0x08, 0x8E); + IDT::set_entry(41, (unsigned)irq9, 0x08, 0x8E); + IDT::set_entry(42, (unsigned)irq10, 0x08, 0x8E); + IDT::set_entry(43, (unsigned)irq11, 0x08, 0x8E); + IDT::set_entry(44, (unsigned)irq12, 0x08, 0x8E); + IDT::set_entry(45, (unsigned)irq13, 0x08, 0x8E); + IDT::set_entry(46, (unsigned)irq14, 0x08, 0x8E); + IDT::set_entry(47, (unsigned)irq15, 0x08, 0x8E); + + return 0; +} + +extern "C" void irq_handler(struct regs *r) +{ + void (*handler)(struct regs *r); + + handler = reinterpret_cast<void (*)(struct regs *r)>(irq_routines[r->int_no - 32]); + if (handler) + { + handler(r); + } + + /* If the IDT entry that was invoked was greater than 40 + * (meaning IRQ8 - 15), then we need to send an EOI to + * the slave controller */ + if (r->int_no >= 40) + { + System::outb(0xA0, 0x20); + } + + /* EOI to the master interrupt controller */ + System::outb(0x20, 0x20); +} diff --git a/src/pOS/arch/x86/kernel/interrupts/isrs.cpp b/src/pOS/arch/x86/kernel/interrupts/isrs.cpp new file mode 100644 index 0000000..22935c9 --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/isrs.cpp @@ -0,0 +1,95 @@ +#include <kernel/interrupts/isrs.h> + +void ISRS::install(void) +{ + /* Do with macro */ + IDT::set_entry(0, (unsigned)isr0, 0x08, 0x8E); + IDT::set_entry(1, (unsigned)isr1, 0x08, 0x8E); + IDT::set_entry(2, (unsigned)isr2, 0x08, 0x8E); + IDT::set_entry(3, (unsigned)isr3, 0x08, 0x8E); + IDT::set_entry(4, (unsigned)isr4, 0x08, 0x8E); + IDT::set_entry(5, (unsigned)isr5, 0x08, 0x8E); + IDT::set_entry(6, (unsigned)isr6, 0x08, 0x8E); + IDT::set_entry(7, (unsigned)isr7, 0x08, 0x8E); + + IDT::set_entry(8, (unsigned)isr8, 0x08, 0x8E); + IDT::set_entry(9, (unsigned)isr9, 0x08, 0x8E); + IDT::set_entry(10, (unsigned)isr10, 0x08, 0x8E); + IDT::set_entry(11, (unsigned)isr11, 0x08, 0x8E); + IDT::set_entry(12, (unsigned)isr12, 0x08, 0x8E); + IDT::set_entry(13, (unsigned)isr13, 0x08, 0x8E); + IDT::set_entry(14, (unsigned)isr14, 0x08, 0x8E); + IDT::set_entry(15, (unsigned)isr15, 0x08, 0x8E); + + IDT::set_entry(16, (unsigned)isr16, 0x08, 0x8E); + IDT::set_entry(17, (unsigned)isr17, 0x08, 0x8E); + IDT::set_entry(18, (unsigned)isr18, 0x08, 0x8E); + IDT::set_entry(19, (unsigned)isr19, 0x08, 0x8E); + IDT::set_entry(20, (unsigned)isr20, 0x08, 0x8E); + IDT::set_entry(21, (unsigned)isr21, 0x08, 0x8E); + IDT::set_entry(22, (unsigned)isr22, 0x08, 0x8E); + IDT::set_entry(23, (unsigned)isr23, 0x08, 0x8E); + + IDT::set_entry(24, (unsigned)isr24, 0x08, 0x8E); + IDT::set_entry(25, (unsigned)isr25, 0x08, 0x8E); + IDT::set_entry(26, (unsigned)isr26, 0x08, 0x8E); + IDT::set_entry(27, (unsigned)isr27, 0x08, 0x8E); + IDT::set_entry(28, (unsigned)isr28, 0x08, 0x8E); + IDT::set_entry(29, (unsigned)isr29, 0x08, 0x8E); + IDT::set_entry(30, (unsigned)isr30, 0x08, 0x8E); + IDT::set_entry(31, (unsigned)isr31, 0x08, 0x8E); +} + +/* This is a simple string array. It contains the message that +* corresponds to each and every exception. We get the correct +* message by accessing like: +* exception_message[interrupt_number] */ +const char *exception_messages[32] = +{ + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +extern "C" void fault_handler(struct regs *r) +{ + if (r->int_no < 32) + { + REG::print_regs(r); + printf("%s", exception_messages[r->int_no]); + printf(" Exception. System Halted!\n"); + System::idle_loop(); + } +} diff --git a/src/pOS/arch/x86/kernel/interrupts/pic.cpp b/src/pOS/arch/x86/kernel/interrupts/pic.cpp new file mode 100644 index 0000000..c540176 --- /dev/null +++ b/src/pOS/arch/x86/kernel/interrupts/pic.cpp @@ -0,0 +1,33 @@ +#include <kernel/interrupts/pic.h> + +int PIC::init(void) { + /* ICW1 */ + System::outb(PIC1_COMMAND, ICW1); + System::outb(PIC2_COMMAND, ICW1); + + /* ICW2, irq 0 to 7 is mapped to 0x20 to 0x27, irq 8 to F is mapped to 28 to 2F */ + System::outb(PIC1_DATA, 0x20); + System::outb(PIC2_DATA, 0x28); + + /* ICW3, connect master pic with slave pic */ + System::outb(PIC1_DATA, 0x4); + System::outb(PIC2_DATA, 0x2); + + /* ICW4, set x86 mode */ + System::outb(PIC1_DATA, 1); + System::outb(PIC2_DATA, 1); + + /* Clear the mask register */ + System::outb(PIC1_DATA, 0); + System::outb(PIC2_DATA, 0); + + return 0; +} + +int PIC::irq_ack(uint8_t irq) { + if(irq >= 0x28) + System::outb(PIC2, PIC_EOI); + System::outb(PIC1, PIC_EOI); + + return 0; +} |