diff options
Diffstat (limited to 'src/kernel/arch/i386/interrupts')
-rw-r--r-- | src/kernel/arch/i386/interrupts/idt.c | 75 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/idt.h | 3 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/isr.c | 24 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/isr.h | 15 |
4 files changed, 117 insertions, 0 deletions
diff --git a/src/kernel/arch/i386/interrupts/idt.c b/src/kernel/arch/i386/interrupts/idt.c new file mode 100644 index 0000000..5573928 --- /dev/null +++ b/src/kernel/arch/i386/interrupts/idt.c @@ -0,0 +1,75 @@ +#include <kernel/arch/i386/gdt.h> +#include <kernel/arch/i386/interrupts/idt.h> +#include <kernel/arch/i386/interrupts/isr.h> +#include <kernel/panic.h> +#include <stdbool.h> +#include <stdint.h> + +struct idt_entry { + uint16_t offset_low ; + uint16_t code_seg ; + uint8_t zero ; // unused, has to be 0 + uint8_t type : 4; // 16/32 bit, task/interrupt/task gate + uint8_t storage : 1; // 0 for interrupt/trap gates + uint8_t ring : 2; + uint8_t present : 1; + uint16_t offset_high ; +} __attribute__((packed)); + +// is exactly the same as lgdt_arg, i should combine them into a single struct +// later +struct lidt_arg { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + + +static struct idt_entry IDT[256]; +static struct lidt_arg lidt_arg; + +static inline void idt_add(uint8_t num, bool user, void (*isr)); +static void idt_prepare(); +static void idt_load(); +static void idt_test(); + + +static inline void idt_add(uint8_t num, bool user, void (*isr)) { + uintptr_t offset = (uintptr_t) isr; + + IDT[num] = (struct idt_entry) { + .offset_low = offset, + .offset_high = offset >> 16, + .code_seg = SEG_r0code << 3, + .zero = 0, + .present = 1, + .ring = user ? 3 : 0, + .storage = 0, + .type = 0xE, // 32-bit interrupt gate + }; +} + +static void idt_prepare() { + for (int i = 0; i < 256; i++) + IDT[i].present = 0; + + idt_add(0x08, false, isr_double_fault); + idt_add(0x0d, false, isr_general_protection_fault); + idt_add(0x34, false, isr_test_interrupt); +} + +static void idt_load() { + lidt_arg.limit = sizeof(IDT) - 1; + lidt_arg.base = (uintptr_t) &IDT; + asm("lidt (%0)" : : "r" (&lidt_arg) : "memory"); +} + +static void idt_test() { + asm("int $0x34" : : : "memory"); + if (!isr_test_interrupt_called) panic(); +} + +void idt_init() { + idt_prepare(); + idt_load(); + idt_test(); +} diff --git a/src/kernel/arch/i386/interrupts/idt.h b/src/kernel/arch/i386/interrupts/idt.h new file mode 100644 index 0000000..5627657 --- /dev/null +++ b/src/kernel/arch/i386/interrupts/idt.h @@ -0,0 +1,3 @@ +#pragma once + +void idt_init(); diff --git a/src/kernel/arch/i386/interrupts/isr.c b/src/kernel/arch/i386/interrupts/isr.c new file mode 100644 index 0000000..d2e3d0f --- /dev/null +++ b/src/kernel/arch/i386/interrupts/isr.c @@ -0,0 +1,24 @@ +#include <kernel/arch/i386/interrupts/isr.h> +#include <kernel/arch/log.h> +#include <kernel/panic.h> +#include <stdbool.h> +#include <stdint.h> + +bool isr_test_interrupt_called = false; + +__attribute__((interrupt)) +void isr_double_fault(struct interrupt_frame *frame) { + log_const("#DF"); + panic(); +} + +__attribute__((interrupt)) +void isr_general_protection_fault(struct interrupt_frame *frame) { + log_const("#GP"); + panic(); +} + +__attribute__((interrupt)) +void isr_test_interrupt(struct interrupt_frame *frame) { + isr_test_interrupt_called = true; +} diff --git a/src/kernel/arch/i386/interrupts/isr.h b/src/kernel/arch/i386/interrupts/isr.h new file mode 100644 index 0000000..150fc46 --- /dev/null +++ b/src/kernel/arch/i386/interrupts/isr.h @@ -0,0 +1,15 @@ +#pragma once +#include <stdbool.h> + +struct interrupt_frame; + +extern bool isr_test_interrupt_called; // used in the self-test in idt.c + +__attribute__((interrupt)) +void isr_double_fault(struct interrupt_frame *frame); + +__attribute__((interrupt)) +void isr_general_protection_fault(struct interrupt_frame *frame); + +__attribute__((interrupt)) +void isr_test_interrupt(struct interrupt_frame *frame); |