about summary refs log tree commit diff
path: root/src/pOS/arch
diff options
context:
space:
mode:
Diffstat (limited to 'src/pOS/arch')
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/asm/gdt.s15
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/asm/idt.s5
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/gdt.cpp56
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/idt.cpp35
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/irq.cpp86
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/isrs.cpp95
-rw-r--r--src/pOS/arch/x86/kernel/interrupts/pic.cpp33
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;
+}