From 67984807977c77130b647c180c8489b2a6540ffe Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 23 Oct 2019 15:40:32 +0300 Subject: [PATCH] Better IRQ handling --- doc/x86/irq.md | 23 ++++++ etc/make/amd64/platform.mk | 2 + include/sys/amd64/asm/asm_irq.h | 32 ++++++++ include/sys/amd64/hw/idt.h | 14 ++++ include/sys/amd64/hw/ioapic.h | 3 + include/sys/amd64/hw/irq.h | 13 ++++ include/sys/amd64/hw/ps2.h | 3 + sys/amd64/hw/idt.c | 20 +---- sys/amd64/hw/ioapic.c | 44 +++++------ sys/amd64/hw/irq.c | 128 ++++++++++++++++++++++++++++++++ sys/amd64/hw/irqs_s.S | 63 +++++----------- sys/amd64/hw/ps2.c | 19 +++++ sys/amd64/hw/rs232.c | 33 ++++---- sys/amd64/kernel.c | 4 +- 14 files changed, 298 insertions(+), 103 deletions(-) create mode 100644 doc/x86/irq.md create mode 100644 include/sys/amd64/hw/irq.h create mode 100644 include/sys/amd64/hw/ps2.h create mode 100644 sys/amd64/hw/irq.c create mode 100644 sys/amd64/hw/ps2.c diff --git a/doc/x86/irq.md b/doc/x86/irq.md new file mode 100644 index 0000000..118334f --- /dev/null +++ b/doc/x86/irq.md @@ -0,0 +1,23 @@ +Full IRQ routing? +================= + +External IRQ sources: +* Legacy IRQ lines +* PCI IRQ pins (INTA#/INTB#/INTC#/INTD#) + +Each of these is routed to a single GSI (IRQ of I/O APIC), +which in turn is mapped to a single interrupt vector in any of the Local APICs + +Mapping legacy IRQ to a handler: +If I/O APIC is available +1. Get 8259 IRQ -> I/O APIC Line mapping +2. Allocate an interrupt vector/reuse shared vector +3. Add a mapping in I/O APIC +4. Add entry to IRQ handlers list +5. Unmask the GSI +Otherwise: +1. Add entry `irq_number` to IRQ handlers list + +I/O APIC init: +1. Assign I/O APIC address variable +2. Relocate existing legacy handlers to I/O APIC diff --git a/etc/make/amd64/platform.mk b/etc/make/amd64/platform.mk index 92cfb30..a843bf8 100644 --- a/etc/make/amd64/platform.mk +++ b/etc/make/amd64/platform.mk @@ -39,6 +39,8 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \ $(O)/sys/amd64/hw/ide/ahci.o \ $(O)/sys/amd64/hw/ide/ide.o \ $(O)/sys/amd64/hw/pci/pcidb.o \ + $(O)/sys/amd64/hw/ps2.o \ + $(O)/sys/amd64/hw/irq.o \ $(ACPICA_OBJS) \ $(O)/sys/amd64/acpi_osl_mem.o \ $(O)/sys/amd64/acpi_osl_printf.o \ diff --git a/include/sys/amd64/asm/asm_irq.h b/include/sys/amd64/asm/asm_irq.h index dfcd5e8..eef72b6 100644 --- a/include/sys/amd64/asm/asm_irq.h +++ b/include/sys/amd64/asm/asm_irq.h @@ -6,6 +6,24 @@ #if defined(__ASM__) .extern local_apic +.extern irq_handle + +// TODO: make this use less memory +.macro irq_entry, n +.global amd64_irq\n +amd64_irq\n: + cli + + irq_push_ctx + + movq $\n, %rdi + call irq_handle + + irq_eoi_lapic 0 + irq_pop_ctx + + iretq +.endm .macro irq_trace, n #if defined(AMD64_TRACE_IRQ) @@ -101,4 +119,18 @@ // Externs for C code extern void amd64_irq0(); extern void amd64_irq1(); +extern void amd64_irq2(); +extern void amd64_irq3(); +extern void amd64_irq4(); +extern void amd64_irq5(); +extern void amd64_irq6(); +extern void amd64_irq7(); +extern void amd64_irq8(); +extern void amd64_irq9(); +extern void amd64_irq10(); +extern void amd64_irq11(); +extern void amd64_irq12(); +extern void amd64_irq13(); +extern void amd64_irq14(); +extern void amd64_irq15(); #endif diff --git a/include/sys/amd64/hw/idt.h b/include/sys/amd64/hw/idt.h index 672ea69..1953432 100644 --- a/include/sys/amd64/hw/idt.h +++ b/include/sys/amd64/hw/idt.h @@ -1,6 +1,18 @@ #pragma once #include "sys/types.h" +#define IDT_FLG_TASK32 0x5 +#define IDT_FLG_INT16 0x6 +#define IDT_FLG_TRAP16 0x7 +#define IDT_FLG_INT32 0xE +#define IDT_FLG_TRAP32 0xF +#define IDT_FLG_SS (1 << 4) +#define IDT_FLG_R0 (0 << 5) +#define IDT_FLG_R1 (1 << 5) +#define IDT_FLG_R2 (2 << 5) +#define IDT_FLG_R3 (3 << 5) +#define IDT_FLG_P (1 << 7) + struct amd64_idtr { uint16_t size; uintptr_t offset; @@ -8,4 +20,6 @@ struct amd64_idtr { extern const struct amd64_idtr amd64_idtr; +void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags); + void amd64_idt_init(void); diff --git a/include/sys/amd64/hw/ioapic.h b/include/sys/amd64/hw/ioapic.h index ca84042..2e69c8e 100644 --- a/include/sys/amd64/hw/ioapic.h +++ b/include/sys/amd64/hw/ioapic.h @@ -37,6 +37,9 @@ void amd64_ioapic_write(uint8_t reg, uint32_t v); void amd64_ioapic_set(uintptr_t addr); void amd64_ioapic_int_src_override(uint8_t bus, uint8_t src, uint32_t no, uint16_t flags); +void amd64_ioapic_map_gsi(uint8_t gsi, uint8_t lapic, uint8_t vector); +void amd64_ioapic_unmask(uint8_t gsi); + //// Generic IRQ stuff // A route exists (LNKx) but is not mapped to any IRQ at the moment #define PCI_IRQ_NO_ROUTE 0xFFFFFFFF diff --git a/include/sys/amd64/hw/irq.h b/include/sys/amd64/hw/irq.h new file mode 100644 index 0000000..25a49fc --- /dev/null +++ b/include/sys/amd64/hw/irq.h @@ -0,0 +1,13 @@ +#pragma once +#include "sys/types.h" + +#define IRQ_LEG_KEYBOARD 1 +#define IRQ_LEG_COM1_3 4 + +typedef int (*irq_handler_t) (void); + +int irq_add_handler(uint8_t gsi, irq_handler_t handler); +int irq_add_leg_handler(uint8_t leg_irq, irq_handler_t handler); + +void irq_enable_ioapic_mode(void); +void irq_init(void); diff --git a/include/sys/amd64/hw/ps2.h b/include/sys/amd64/hw/ps2.h new file mode 100644 index 0000000..be630a6 --- /dev/null +++ b/include/sys/amd64/hw/ps2.h @@ -0,0 +1,3 @@ +#pragma once + +void ps2_init(void); diff --git a/sys/amd64/hw/idt.c b/sys/amd64/hw/idt.c index 93cf3a4..fb54a17 100644 --- a/sys/amd64/hw/idt.c +++ b/sys/amd64/hw/idt.c @@ -1,20 +1,8 @@ #include "sys/amd64/hw/idt.h" -#include "sys/amd64/asm/asm_irq.h" +#include "sys/amd64/hw/irq.h" #include "sys/string.h" #include "sys/types.h" -#define IDT_FLG_TASK32 0x5 -#define IDT_FLG_INT16 0x6 -#define IDT_FLG_TRAP16 0x7 -#define IDT_FLG_INT32 0xE -#define IDT_FLG_TRAP32 0xF -#define IDT_FLG_SS (1 << 4) -#define IDT_FLG_R0 (0 << 5) -#define IDT_FLG_R1 (1 << 5) -#define IDT_FLG_R2 (2 << 5) -#define IDT_FLG_R3 (3 << 5) -#define IDT_FLG_P (1 << 7) - extern uintptr_t amd64_exception_vectors[32]; static struct amd64_idt_entry { @@ -32,7 +20,7 @@ const struct amd64_idtr amd64_idtr = { .offset = (uintptr_t) idt }; -static void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags) { +void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags) { idt[idx].base_lo = base & 0xFFFF; idt[idx].base_hi = (base >> 16) & 0xFFFF; idt[idx].base_ex = (base >> 32) & 0xFFFFFFFF; @@ -47,8 +35,8 @@ void amd64_idt_init(void) { for (size_t i = 0; i < 32; ++i) { amd64_idt_set(i, amd64_exception_vectors[i], 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); } - amd64_idt_set(32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); - amd64_idt_set(33, (uintptr_t) amd64_irq1, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + + irq_init(); asm volatile ("lidt amd64_idtr(%rip)"); } diff --git a/sys/amd64/hw/ioapic.c b/sys/amd64/hw/ioapic.c index ef4b71e..78fe692 100644 --- a/sys/amd64/hw/ioapic.c +++ b/sys/amd64/hw/ioapic.c @@ -1,4 +1,5 @@ #include "sys/amd64/hw/ioapic.h" +#include "sys/amd64/hw/irq.h" #include "sys/string.h" #include "sys/mm.h" #include "sys/debug.h" @@ -95,33 +96,28 @@ void amd64_ioapic_set(uintptr_t addr) { // low & 0xF); } - // Test: set IRQ1 -> LAPIC 0:1 and unmask it - uint32_t low = amd64_ioapic_read(0x10 + 1 * 2); - uint32_t high = amd64_ioapic_read(0x11 + 1 * 2); + irq_enable_ioapic_mode(); +} + +void amd64_ioapic_map_gsi(uint8_t gsi, uint8_t lapic, uint8_t vector) { + uint32_t low = amd64_ioapic_read((gsi * 2) + 0x10); + uint32_t high = amd64_ioapic_read((gsi * 2) + 0x11); + + low &= ~0xFF; + low |= vector; + + high = IOAPIC_REDIR_DST_SET(lapic); + + amd64_ioapic_write((gsi * 2) + 0x10, low); + amd64_ioapic_write((gsi * 2) + 0x11, high); +} + +void amd64_ioapic_unmask(uint8_t gsi) { + uint32_t low = amd64_ioapic_read((gsi * 2) + 0x10); - low &= ~0x7; low &= ~IOAPIC_REDIR_MSK; - low |= 0x1 + 0x20; - high = IOAPIC_REDIR_DST_SET(0); - - amd64_ioapic_write(0x10 + 1 * 2, low); - amd64_ioapic_write(0x11 + 1 * 2, high); - - // TODO: find IRQ mappings corresponding to legacy irq 1/4 - low = amd64_ioapic_read(0x10 + 4 * 2); - high = amd64_ioapic_read(0x11 + 4 * 2); - - low &= ~0x7; - low &= ~IOAPIC_REDIR_MSK; - - low |= 0x1 + 0x20; - high = IOAPIC_REDIR_DST_SET(0); - - amd64_ioapic_write(0x10 + 4 * 2, low); - amd64_ioapic_write(0x11 + 4 * 2, high); - - // TODO: unconfigured PCI IRQs may be mapped here + amd64_ioapic_write((gsi * 2) + 0x10, low); } #define PCI_LINK_MAX_POSSIBLE_IRQS 16 diff --git a/sys/amd64/hw/irq.c b/sys/amd64/hw/irq.c new file mode 100644 index 0000000..8f4ba24 --- /dev/null +++ b/sys/amd64/hw/irq.c @@ -0,0 +1,128 @@ +#include "sys/amd64/asm/asm_irq.h" +#include "sys/amd64/hw/ioapic.h" +#include "sys/amd64/hw/irq.h" +#include "sys/amd64/hw/idt.h" +#include "sys/string.h" +#include "sys/assert.h" +#include "sys/panic.h" +#include "sys/debug.h" + +#define IRQ_MAX 16 // Maximum assignable IRQ vectors +#define IRQ_MAX_HANDLERS 4 // Maximum handlers per IRQ vector (sharing) + +static irq_handler_t handlers[IRQ_MAX * IRQ_MAX_HANDLERS] = {0}; +static uint64_t vector_bitmap[4] = {0}; +static int ioapic_available = 0; + +static int irq_alloc_vector(uint8_t *vector) { + for (size_t i = 1; i < IRQ_MAX; ++i) { + size_t idx = i / 64; + size_t bit = 1ULL << (i % 64); + + if (!(vector_bitmap[idx] & bit)) { + *vector = i; + vector_bitmap[idx] |= bit; + return 0; + } + } + + return -1; +} + +void irq_handle(uintptr_t n) { + _assert(n && n < IRQ_MAX); + + irq_handler_t *list = &handlers[IRQ_MAX_HANDLERS * n]; + for (size_t i = 0; i < IRQ_MAX_HANDLERS; ++i) { + if (list[i] && (list[i]() == 0)) { + return; + } + } +} + +int irq_add_handler(uint8_t gsi, irq_handler_t handler) { + _assert(gsi != 0 && gsi < IRQ_MAX); + _assert(ioapic_available); + + + uint8_t vector; + if (irq_alloc_vector(&vector)) { + return -1; + } + kdebug("I/O APIC: Mapping GSI%u -> Vector %d:%d\n", gsi, 0, vector); + + irq_handler_t *list = &handlers[IRQ_MAX_HANDLERS * vector]; + for (size_t i = 0; i < IRQ_MAX_HANDLERS; ++i) { + if (!list[i]) { + list[i] = handler; + + amd64_ioapic_map_gsi(gsi, 0, vector + 0x20); + amd64_ioapic_unmask(gsi); + + return 0; + } + } + + return -1; +} + +int irq_add_leg_handler(uint8_t leg_irq, irq_handler_t handler) { + _assert(leg_irq != 0 && leg_irq < IRQ_MAX); + + if (ioapic_available) { + // Find out the route (TODO) + uint8_t gsi = leg_irq; + + return irq_add_handler(gsi, handler); + } else { + irq_handler_t *list = &handlers[IRQ_MAX_HANDLERS * leg_irq]; + + for (size_t i = 0; i < IRQ_MAX_HANDLERS; ++i) { + if (!list[i]) { + list[i] = handler; + return 0; + } + } + + return -1; + } +} + +void irq_enable_ioapic_mode(void) { + ioapic_available = 1; + // Copy current table + irq_handler_t handlers_old[16 * IRQ_MAX_HANDLERS]; + + memcpy(handlers_old, handlers, 16 * IRQ_MAX_HANDLERS * sizeof(irq_handler_t)); + memset(handlers, 0, 16 * IRQ_MAX_HANDLERS * sizeof(irq_handler_t)); + + // All legacy IRQs + for (size_t i = 0; i < 16; ++i) { + irq_handler_t *handler_list = &handlers_old[i * IRQ_MAX_HANDLERS]; + + for (size_t j = 0; j < IRQ_MAX_HANDLERS; ++j) { + if (handler_list[j]) { + irq_add_leg_handler(i, handlers_old[i * IRQ_MAX_HANDLERS + j]); + } + } + } +} + +void irq_init(void) { + amd64_idt_set(32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(33, (uintptr_t) amd64_irq1, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(34, (uintptr_t) amd64_irq2, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(35, (uintptr_t) amd64_irq3, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(36, (uintptr_t) amd64_irq4, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(37, (uintptr_t) amd64_irq5, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(38, (uintptr_t) amd64_irq6, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(39, (uintptr_t) amd64_irq7, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(40, (uintptr_t) amd64_irq8, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(41, (uintptr_t) amd64_irq9, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(42, (uintptr_t) amd64_irq10, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(43, (uintptr_t) amd64_irq11, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(44, (uintptr_t) amd64_irq12, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(45, (uintptr_t) amd64_irq13, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(46, (uintptr_t) amd64_irq14, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(47, (uintptr_t) amd64_irq15, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); +} diff --git a/sys/amd64/hw/irqs_s.S b/sys/amd64/hw/irqs_s.S index a0efe07..e91cc1e 100644 --- a/sys/amd64/hw/irqs_s.S +++ b/sys/amd64/hw/irqs_s.S @@ -4,51 +4,22 @@ .section .text -// TODO: move all this to some kind of generic handler -// This is keyboard -// Can be either entered from a legacy PIC IRQ -// Or is mapped in I/O APIC -.global amd64_irq1 -amd64_irq1: - cli - - irq_push_ctx - movq %rsp, %rbp - - // The ISR is shared between legacy IRQs 1 and 4, so - // check if it's keyboard or serial input on COM1 - // Check PS/2 keyboard - inb $0x64, %al - test $(1 << 0), %al - jz 1f - - // Read from keyboard buffer - inb $0x60, %al - movb %al, irq1_key(%rip) - - jmp 2f -1: - - movq $0x3F8, %rdi - call rs232_irq - test %rax, %rax - jz 2f - - // Only trace IRQ if it's unhandled - irq_trace 1 -2: - irq_eoi_lapic 1 - - irq_pop_ctx - - iretq - - -_amd64_irq1_unhandled: - .string "Something unknown triggered ISR 0x21\n" -_amd64_serial: - .string "Serial status: %02x\n" - +// 33 +irq_entry 1 +irq_entry 2 +irq_entry 3 +irq_entry 4 +irq_entry 5 +irq_entry 6 +irq_entry 7 +irq_entry 8 +irq_entry 9 +irq_entry 10 +irq_entry 11 +irq_entry 12 +irq_entry 13 +irq_entry 14 +irq_entry 15 .global amd64_kstack_canary_invalid #if defined(AMD64_STACK_CTX_CANARY) @@ -68,5 +39,5 @@ amd64_kstack_canary_invalid: jmp 1b _msg0: - .string "Stack fuckup detected: halting\n" + .string "Stack fuckup detected: halting\n" #endif diff --git a/sys/amd64/hw/ps2.c b/sys/amd64/hw/ps2.c new file mode 100644 index 0000000..482e900 --- /dev/null +++ b/sys/amd64/hw/ps2.c @@ -0,0 +1,19 @@ +#include "sys/amd64/hw/ps2.h" +#include "sys/amd64/hw/irq.h" +#include "sys/amd64/hw/io.h" + +int ps2_irq_keyboard(void) { + uint8_t st = inb(0x64); + + if (!(st & 1)) { + return -1; + } + + inb(0x60); + + return 0; +} + +void ps2_init(void) { + irq_add_leg_handler(IRQ_LEG_KEYBOARD, ps2_irq_keyboard); +} diff --git a/sys/amd64/hw/rs232.c b/sys/amd64/hw/rs232.c index 21ddccb..dee6ef5 100644 --- a/sys/amd64/hw/rs232.c +++ b/sys/amd64/hw/rs232.c @@ -1,4 +1,5 @@ #include "sys/amd64/hw/rs232.h" +#include "sys/amd64/hw/irq.h" #include "sys/amd64/hw/io.h" #include "sys/debug.h" @@ -31,6 +32,21 @@ void rs232_send(uint16_t port, char c) { outb(port, c); } +static int rs232_irq(void) { + int has_data = 0; + uint8_t s; + + while ((s = inb(RS232_COM0 + RS232_LSR)) & RS232_LSR_DR) { + has_data = 1; + uint8_t c = inb(RS232_COM0); + + // Act as echo for now + rs232_send(RS232_COM0, c); + } + + return !has_data; +} + void rs232_init(uint16_t port) { // Disable interrupts outb(port + RS232_IER, 0x00); @@ -48,19 +64,6 @@ void rs232_init(uint16_t port) { // Enable receive interrupts outb(port + RS232_IER, RS232_IER_RCE); -} - -int rs232_irq(uint16_t port) { - int has_data = 0; - uint8_t s; - - while ((s = inb(port + RS232_LSR)) & RS232_LSR_DR) { - has_data = 1; - uint8_t c = inb(port); - - // Act as echo for now - rs232_send(port, c); - } - - return !has_data; + + irq_add_leg_handler(IRQ_LEG_COM1_3, rs232_irq); } diff --git a/sys/amd64/kernel.c b/sys/amd64/kernel.c index 67503d4..cab67f4 100644 --- a/sys/amd64/kernel.c +++ b/sys/amd64/kernel.c @@ -9,15 +9,15 @@ #include "sys/amd64/hw/apic.h" #include "sys/amd64/hw/pci/pci.h" #include "sys/amd64/hw/rs232.h" +#include "sys/amd64/hw/ps2.h" #include "sys/panic.h" #include "sys/assert.h" -#include "acpi.h" - static multiboot_info_t *multiboot_info; void kernel_main(struct amd64_loader_data *data) { // Reinitialize RS232 properly + ps2_init(); rs232_init(RS232_COM0); data = (struct amd64_loader_data *) MM_VIRTUALIZE(data);