diff options
Diffstat (limited to 'kernel/cpu/idt.c')
-rw-r--r-- | kernel/cpu/idt.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/kernel/cpu/idt.c b/kernel/cpu/idt.c new file mode 100644 index 0000000..abcafad --- /dev/null +++ b/kernel/cpu/idt.c @@ -0,0 +1,195 @@ +#include <cpu/idt.h> +#include <sched/scheduler.h> +#include <stdio.h> + +#define MASTER_PIC_COMMAND_PORT 0x20 +#define MASTER_PIC_DATA_PORT 0x21 +#define SLAVE_PIC_COMMAND_PORT 0xA0 +#define SLAVE_PIC_DATA_PORT 0xA1 +#define KEYBOARD_DATA_PORT 0x60 +#define KEYBOARD_STATUS_PORT 0x64 +#define KERNEL_CODE_SEGMENT_OFFSET GDT_ENTRY_SIZE *GDT_KERNEL_CODE_SEGMENT + +#define IDT_MAX_ENTRY 256 + +struct IDT_Descriptor { + uint16_t low_offset; + uint16_t code_segment_selector; + uint8_t zero; // Always should be zero + uint8_t type_attribute; + uint16_t high_offset; +} __attribute__((packed)) __attribute__((aligned(4))); + +struct IDT_Pointer { + uint16_t size; + struct IDT_Descriptor **interrupt_table; +} __attribute__((packed)); + +struct IDT_Descriptor IDT_Entry[IDT_MAX_ENTRY]; +struct IDT_Pointer idtr; + +extern void load_idtr(void *idtr); + +void format_descriptor(uint32_t offset, uint16_t code_segment, + uint8_t type_attribute, + struct IDT_Descriptor *descriptor) { + descriptor->low_offset = offset & 0xFFFF; + descriptor->high_offset = offset >> 16; + descriptor->type_attribute = type_attribute; + descriptor->code_segment_selector = code_segment; + descriptor->zero = 0; +} + +void install_handler(void (*handler_function)(), uint16_t type_attribute, + uint8_t entry) { + format_descriptor((uint32_t)handler_function, KERNEL_CODE_SEGMENT_OFFSET, + type_attribute, &IDT_Entry[entry]); +} + +__attribute__((no_caller_saved_registers)) void EOI(uint8_t irq) { + if (irq > 7) + outb(SLAVE_PIC_COMMAND_PORT, 0x20); + + outb(MASTER_PIC_COMMAND_PORT, 0x20); +} + +__attribute__((interrupt)) void general_protection_fault(registers_t *regs) { + klog("General Protetion Fault", 0x1); + kprintf(" Error Code: %x\n", regs->error_code); + kprintf("Instruction Pointer: %x\n", regs->eip); + dump_backtrace(12); + asm("hlt"); + for (;;) + ; + EOI(0xD - 8); +} + +__attribute__((interrupt)) void double_fault(registers_t *regs) { + (void)regs; + klog("DOUBLE FAULT", LOG_ERROR); + asm("hlt"); + for (;;) + ; +} +__attribute__((interrupt)) void page_fault(registers_t *regs) { + if (0xFFFFDEAD == regs->eip) { + asm("sti"); + for (;;) + switch_task(); + } + klog("Page Fault", LOG_ERROR); + if (get_current_task()) { + kprintf("PID: %x\n", get_current_task()->pid); + kprintf("Name: %s\n", get_current_task()->program_name); + } + kprintf("Error Code: %x\n", regs->error_code); + kprintf("Instruction Pointer: %x\n", regs->eip); + + if (regs->error_code & (1 << 0)) + kprintf("page-protection violation\n"); + else + kprintf("non-present page\n"); + + if (regs->error_code & (1 << 1)) + kprintf("write access\n"); + else + kprintf("read access\n"); + + if (regs->error_code & (1 << 2)) + kprintf("CPL = 3\n"); + + if (regs->error_code & (1 << 4)) + kprintf("Attempted instruction fetch\n"); + + dump_backtrace(5); + asm("hlt"); + for (;;) + ; +} + +static inline void io_wait(void) { outb(0x80, 0); } + +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + +void PIC_remap(int offset) { + unsigned char a1, a2; + a1 = inb(MASTER_PIC_DATA_PORT); + a2 = inb(SLAVE_PIC_DATA_PORT); + + // Send ICW1 and tell the PIC that we will issue a ICW4 + // Since there is no ICW1_SINGLE sent to indicate it is cascaded + // it means we will issue a ICW3. + outb(MASTER_PIC_COMMAND_PORT, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(SLAVE_PIC_COMMAND_PORT, ICW1_INIT | ICW1_ICW4); + io_wait(); + + // As a part of ICW2 this sends the offsets(the position to put IRQ0) of the + // vector tables. + outb(MASTER_PIC_DATA_PORT, offset); + io_wait(); + outb(SLAVE_PIC_DATA_PORT, offset + 0x8); + io_wait(); + + // This tells the master on which lines it is having slaves + outb(MASTER_PIC_DATA_PORT, 4); + io_wait(); + // This tells the slave the cascading identity. + outb(SLAVE_PIC_DATA_PORT, 2); + io_wait(); + + outb(MASTER_PIC_DATA_PORT, ICW4_8086); + io_wait(); + outb(SLAVE_PIC_DATA_PORT, ICW4_8086); + io_wait(); + + outb(MASTER_PIC_DATA_PORT, a1); + outb(SLAVE_PIC_DATA_PORT, a2); +} + +void IRQ_set_mask(unsigned char IRQline) { + uint16_t port; + uint8_t value; + port = (IRQline < 8) ? MASTER_PIC_DATA_PORT : SLAVE_PIC_DATA_PORT; + if (IRQline >= 8) + IRQline -= 8; + value = inb(port) | (1 << IRQline); + outb(port, value); +} + +void IRQ_clear_mask(unsigned char IRQline) { + uint16_t port; + uint8_t value; + port = (IRQline < 8) ? MASTER_PIC_DATA_PORT : SLAVE_PIC_DATA_PORT; + if (IRQline >= 8) { + IRQline -= 8; + } + value = inb(port) & ~(1 << IRQline); + outb(port, value); +} + +void idt_init(void) { + install_handler(page_fault, INT_32_INTERRUPT_GATE(0x0), 0xE); + install_handler(double_fault, INT_32_INTERRUPT_GATE(0x0), 0x8); + install_handler(general_protection_fault, INT_32_INTERRUPT_GATE(0x0), 0xD); + + PIC_remap(0x20); + IRQ_clear_mask(0xb); + IRQ_set_mask(0xe); + IRQ_set_mask(0xf); + IRQ_clear_mask(2); + + idtr.interrupt_table = (struct IDT_Descriptor **)&IDT_Entry; + idtr.size = (sizeof(struct IDT_Descriptor) * IDT_MAX_ENTRY) - 1; + load_idtr(&idtr); +} |