From: Duje Mihanović Date: Mon, 13 Jun 2022 09:41:50 +0000 (+0200) Subject: Rework interrupt handling X-Git-Url: https://git.dujemihanovic.xyz/%7B%7B%20%24style.RelPermalink%20%7D%7D?a=commitdiff_plain;h=a16345de748dc4ea0c692e24f016bb0d783ff5dd;p=nameless-os.git Rework interrupt handling This new system should, among other things, allow for more flexibility than the previous one. TODO: Define interrupt stack frame --- diff --git a/Makefile b/Makefile index 7d10f97..ae1a716 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,13 @@ export AS = yasm +ASFLAGS = -f elf -g dwarf2 export CC = i686-elf-gcc QEMU = qemu-system-i386 -monitor stdio export GIT_REV = $(shell git describe --long HEAD) -CFLAGS = -std=gnu89 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\" +CFLAGS = -std=gnu11 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\" -KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/drivers/input/ps2.o kernel/kernel.o +KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/interrupt.o kernel/arch/x86/irq/stub.o kernel/drivers/input/ps2.o kernel/kernel.o BOOTLOADER_OBJ = boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN default: kernel/kernel.elf bootloader @@ -36,12 +37,6 @@ boot/x86/disk.img: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN bo kernel/kernel.bin: ${KERNEL_OBJ} kernel/linker.ld $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -kernel/entry.o: kernel/entry.s - $(AS) -f elf $< -o $@ - -kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c - $(CC) $(CFLAGS) -mgeneral-regs-only -c $< -o $@ - kernel/drivers/input/ps2.o: kernel/drivers/input/ps2.c kernel/kernel.o: kernel/kernel.c diff --git a/boot/x86/Makefile b/boot/x86/Makefile index 1de8199..877930a 100644 --- a/boot/x86/Makefile +++ b/boot/x86/Makefile @@ -1,3 +1,5 @@ +AS = yasm +ASFLAGS = -f bin default: mbr vbr-fat32 stage3/LOADER.BIN mbr: mbr.s diff --git a/include/arch/x86/irq/idt.h b/include/arch/x86/irq/idt.h index daf586f..501e5cd 100644 --- a/include/arch/x86/irq/idt.h +++ b/include/arch/x86/irq/idt.h @@ -21,7 +21,7 @@ struct idtr { uint32_t base; /* starting address of IDT */ } __attribute__((packed)); -extern void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl); +extern void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, void (*offset)(void), uint8_t type, uint8_t dpl); extern inline void load_idt(struct idtr idtr); extern inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt); diff --git a/include/arch/x86/irq/interrupt.h b/include/arch/x86/irq/interrupt.h new file mode 100644 index 0000000..fbecdd8 --- /dev/null +++ b/include/arch/x86/irq/interrupt.h @@ -0,0 +1,6 @@ +#ifndef X86_INTERRUPT_H +#define X86_INTERRUPT_H + +extern int register_interrupt(int irq, int (*handler)(void)); + +#endif diff --git a/kernel/arch/x86/irq/idt.c b/kernel/arch/x86/irq/idt.c index 54e8612..3871751 100644 --- a/kernel/arch/x86/irq/idt.c +++ b/kernel/arch/x86/irq/idt.c @@ -7,17 +7,17 @@ inline void load_idt(struct idtr idtr) asm volatile ("lidt %0": : "m" (idtr)); } -void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl) +void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, void (*offset)(void), uint8_t type, uint8_t dpl) { struct idt_descriptor *descriptor = &idt[vector]; - descriptor->offset_1 = offset & 0xFFFF; + descriptor->offset_1 = (uint32_t) offset & 0xFFFF; descriptor->segsel = segment; descriptor->unused = 0; descriptor->type = type; descriptor->dpl = dpl; descriptor->present = 1; - descriptor->offset_2 = offset >> 16; + descriptor->offset_2 = (uint32_t) offset >> 16; } inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt) diff --git a/kernel/arch/x86/irq/interrupt.c b/kernel/arch/x86/irq/interrupt.c new file mode 100644 index 0000000..b2abef9 --- /dev/null +++ b/kernel/arch/x86/irq/interrupt.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +/* This table will hold pointers to our interrupt handlers. */ +static int (*int_handler_table[256])(void); + +void int_handler(int interrupt) +{ + if (int_handler_table[interrupt] == NULL) { + kprint("WARNING: Unhandled interrupt ", 0); + kprintb(interrupt); + kprint(" occurred!\n", 0); + } else { + int ret = (*int_handler_table[interrupt])(); + if (ret) { + kprint("WARNING: Error while handling interrupt ", 0); + kprintb(interrupt); + kprint("!\n", 0); + } + } + if (interrupt >= 0x20) { + pic_send_eoi(interrupt - 0x20); + } +} + +int register_interrupt(int irq, int (*handler)(void)) +{ + int_handler_table[irq] = handler; + if (irq >= 32 && irq < 48) { + pic_unmask(irq-32); + } + return 0; +} diff --git a/kernel/arch/x86/irq/sample_handler.c b/kernel/arch/x86/irq/sample_handler.c deleted file mode 100644 index 72a86c6..0000000 --- a/kernel/arch/x86/irq/sample_handler.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include -#include - -int was_released = 0; -int is_caps = 0; - -typedef uint32_t uword_t; - -struct interrupt_frame { - uword_t ip; - uword_t cs; - uword_t flags; -}; - -struct abort_frame; - -__attribute__((interrupt)) -void keyb_handler(struct interrupt_frame *frame) -{ - pic_send_eoi(1); - uint8_t scancode = inb(PS2_DATA_PORT); - if (was_released) { - was_released = 0; - return; - } - - if (scancode == 0xf0) { - was_released = 1; - uint8_t scancode = inb(PS2_DATA_PORT); - if (scancode == 0x12) { - is_caps = 0; - } - return; - } else { - if (scancode == 0x12) { - is_caps = 1; - return; - } - if (!is_caps) { - kprintc(scancodes[scancode], 0); - } else { - kprintc(scancodes[scancode] - ('a'-'A'), 0); - } - } -} - - -__attribute__((interrupt)) -void double_fault(struct abort_frame *frame) -{ - *(volatile uint32_t *) (0xb8000) = 0xcf28cf3a; -halt: - asm volatile ("cli; hlt"); - goto halt; -} - diff --git a/kernel/arch/x86/irq/stub.s b/kernel/arch/x86/irq/stub.s new file mode 100644 index 0000000..c15969a --- /dev/null +++ b/kernel/arch/x86/irq/stub.s @@ -0,0 +1,41 @@ +; Stubs for interrupt handlers + +bits 32 +section .text + +extern int_handler + +int_common: + cld + call int_handler + add esp, 4 + popad + iretd + +%macro INTERRUPT 1 +global int%1 +int_%1: + pushad + push dword %1 + jmp int_common +%endmacro + +%assign i 0 +%rep 48 +INTERRUPT i +%assign i i+1 +%endrep + +; Define flat table containing addresses of handlers +section .rodata +global _int_handler_table +_int_handler_table: + %macro INT 1 + dd int_%1 + %endmacro + + %assign i 0 + %rep 48 + INT i + %assign i i+1 + %endrep diff --git a/kernel/drivers/input/ps2.c b/kernel/drivers/input/ps2.c index e765b24..ab4be5c 100644 --- a/kernel/drivers/input/ps2.c +++ b/kernel/drivers/input/ps2.c @@ -2,11 +2,43 @@ #include #include #include +#include + +static int was_released = 0, is_caps = 0; + +int ps2_keyb_handler() +{ + uint8_t scancode = inb(PS2_DATA_PORT); + if (was_released) { + was_released = 0; + return 0; + } + + if (scancode == 0xf0) { + was_released = 1; + uint8_t scancode = inb(PS2_DATA_PORT); + if (scancode == 0x12) { + is_caps = 0; + } + return 0; + } else { + if (scancode == 0x12) { + is_caps = 1; + return 0; + } + if (!is_caps) { + kprintc(scancodes[scancode], 0); + } else { + kprintc(scancodes[scancode] - ('a'-'A'), 0); + } + } + return 0; +} int ps2_initialize() { uint8_t ccb, is_2channel, port_1_test, port_2_test; - + kprint("ps2: Begin initializing PS/2 controller\n", 0); outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_DISABLE); ps2_input_wait(); @@ -21,7 +53,7 @@ int ps2_initialize() CLEAR(ccb, 0); CLEAR(ccb, 1); CLEAR(ccb, 6); - + ps2_input_wait(); outb(PS2_CMD_STS_PORT, PS2_CMD_WRITE_CCB); ps2_input_wait(); @@ -34,7 +66,7 @@ int ps2_initialize() kprint("ps2: Controller self test failed, exiting!\n", 0); return -1; }; - + ps2_input_wait(); outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_ENABLE); ps2_input_wait(); @@ -55,7 +87,7 @@ int ps2_initialize() ps2_input_wait(); outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_1_TEST); ps2_output_wait(); - + if (inb(PS2_DATA_PORT) != 0) { port_1_test = 0; kprint("ps2: Port 1 test failed!\n", 0); @@ -87,7 +119,7 @@ int ps2_initialize() if (port_1_test) { uint8_t resp; - + do { ps2_input_wait(); outb(PS2_DATA_PORT, PS2_DEV_RESET); @@ -144,6 +176,8 @@ int ps2_initialize() ps2_input_wait(); outb(PS2_DATA_PORT, ccb); + if (dev_1_test) register_interrupt(33, &ps2_keyb_handler); + if (dev_2_test && dev_1_test) return 1; else return 0; } diff --git a/kernel/kernel.c b/kernel/kernel.c index 3f6c256..2af3372 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -2,13 +2,13 @@ #include #include #include +#include #include #include -extern void double_fault(struct abort_frame *frame); -extern void keyb_handler(struct interrupt_frame *frame); struct idt_descriptor idt[256] __attribute__((aligned(0x10))); struct idtr idtr __attribute__((aligned(0x10))); +extern void (*_int_handler_table[48])(void); void kmain(void) { @@ -17,6 +17,16 @@ void kmain(void) kprint("Welcome to Nameless OS!\nRunning revision: ", 0); kprint(GIT_COMMIT, 0); kprint("\n", 0); + kprint("Preparing IDT...\n", 0); + for (int i=0; i<48; i++) { + idt_set_descriptor(idt, i, 0x8, _int_handler_table[i], IDT_INTERRUPT_GATE, 0); + } + kprint("IDT prepared, loading...\n", 0); + populate_idtr(&idtr, idt); + load_idt(idtr); + kprint("Setting up interrupts...\n", 0); + pic_init(0x20, 0x28); + pic_mask_all(); ps2_success = ps2_initialize(); switch (ps2_success) { case -1: @@ -28,16 +38,6 @@ void kmain(void) case 1: kprint("Found two working PS/2 devices\n", 0); } - kprint("Preparing IDT...\n", 0); - idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_GATE, 0x0); - idt_set_descriptor(idt, 0x21, 0x8, (uint32_t) keyb_handler, IDT_INTERRUPT_GATE, 0x0); - kprint("IDT prepared, loading...\n", 0); - populate_idtr(&idtr, idt); - load_idt(idtr); - kprint("IDT loaded, enabling interrupts...\n", 0); - pic_init(0x20, 0x28); - pic_mask_all(); - pic_unmask(1); asm volatile ("sti"); kprint("All done\n", 0); while(1);