From 2abd22178923ec225c4426dbfae3737b0011ec25 Mon Sep 17 00:00:00 2001 From: Thomas Oltmann Date: Wed, 8 Jul 2026 21:52:16 +0200 Subject: [PATCH] Utility to choose good IRQ numbers --- include/cpu.h | 11 ++++++++++- include/x86_64/apic.h | 2 ++ src/x86_64/cpu.c | 33 +++++++++++++++++++++++++++++++++ src/x86_64/lapic.c | 2 +- src/x86_64/ps2_driver.c | 15 +++++++++++---- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/include/cpu.h b/include/cpu.h index 60087c2..b22dc40 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -17,7 +17,6 @@ unsigned int cpu_get_core_id(void); void write_gs_base(uint64_t base); -#define LINEBUF_SIZE 256 struct cpu_info { // first member of struct is pointer to itself, for easier loading struct cpu_info* cpu_info; @@ -35,4 +34,14 @@ static inline struct cpu_info *local_cpu_data(void) { void interrupt_handler_register(unsigned int vector, void (*handler)(void)); +/** Choose one of the 223 interrupt numbers available for Hardware IRQs. + * This function is useful for drivers that can control the IRQ numbers they are assigned + * (e.g. hardware using MSI, MSI-X or the IOAPIC). + * Interrupt numbers are chosen based on a hash of the arguments given to the function. + * This ensures evenly spread interrupt numbers, that are stable across + * multiple boots, kernel changes, and different computers. + */ +unsigned int choose_irq_vector(const char *system_name, + uint64_t instance_idx, uint64_t subinstance_idx); + #endif diff --git a/include/x86_64/apic.h b/include/x86_64/apic.h index be7ef74..473b1e7 100644 --- a/include/x86_64/apic.h +++ b/include/x86_64/apic.h @@ -4,6 +4,8 @@ #include #include +#define LAPIC_SPURIOUS_VECTOR_NUMBER 0xFF + void lapic_init(void); unsigned lapic_get_id(void); void lapic_eoi(void); diff --git a/src/x86_64/cpu.c b/src/x86_64/cpu.c index 6277636..0e49ec0 100644 --- a/src/x86_64/cpu.c +++ b/src/x86_64/cpu.c @@ -620,3 +620,36 @@ void cpu_init_ap_1(uint8_t core_id) { main(); PANIC("return from main?"); } + +/* Fowler-Noll-Vo Hash Function (Alternate) */ + +#define FNV32_PRIME 16777619u +#define FNV32_OFFSET_BASIS 2166136261u + +static void +hash32_fnv1a(uint32_t *hash, const void *d, unsigned n) +{ + const uint8_t *c = d; + uint32_t h = *hash; + for (unsigned i = 0; i < n; i++) { + h ^= *c; + h *= FNV32_PRIME; + c++; + } + *hash = h; +} + +unsigned int choose_irq_vector(const char *system_name, + uint64_t instance_idx, uint64_t subinstance_idx) +{ + uint32_t hash = FNV32_OFFSET_BASIS; + hash32_fnv1a(&hash, system_name, strlen(system_name)); + hash32_fnv1a(&hash, &instance_idx, sizeof instance_idx); + hash32_fnv1a(&hash, &subinstance_idx, sizeof subinstance_idx); + hash &= 0xFF; + // TODO this biases IRQ vectors into specific numbers, fix it + if (hash < 32) hash += 32; + if (hash == LAPIC_SPURIOUS_VECTOR_NUMBER) hash ^= 1; + return hash; +} + diff --git a/src/x86_64/lapic.c b/src/x86_64/lapic.c index 8dd43f8..5a39014 100644 --- a/src/x86_64/lapic.c +++ b/src/x86_64/lapic.c @@ -58,7 +58,7 @@ void lapic_init(void) { ASSERT((LAPIC_ADDR->lapic_version.value & 0xff) <= 0x15); - LAPIC_ADDR->spurious_vector.value = SPURIOUS_VECTOR_APIC_ENABLE | 0xff; + LAPIC_ADDR->spurious_vector.value = SPURIOUS_VECTOR_APIC_ENABLE | LAPIC_SPURIOUS_VECTOR_NUMBER; wbinvd(); } diff --git a/src/x86_64/ps2_driver.c b/src/x86_64/ps2_driver.c index 34769cd..5dede0f 100644 --- a/src/x86_64/ps2_driver.c +++ b/src/x86_64/ps2_driver.c @@ -382,13 +382,20 @@ void ps2_init() { } /* register interrupt handlers */ - interrupt_handler_register(101, port1_handler); - interrupt_handler_register(102, port2_handler); + + unsigned port1_irq = choose_irq_vector("ps2", 0, 1); + unsigned port2_irq = choose_irq_vector("ps2", 0, 2); + + printlinef("port1_irq = %d", port1_irq); + printlinef("port2_irq = %d", port2_irq); + + interrupt_handler_register(port1_irq, port1_handler); + interrupt_handler_register(port2_irq, port2_handler); bool success; - success = ioapic_configure_irq(1, 101 | IORED_DELIVERY_NORMAL | ((uint64_t)lapic_get_id() << IORED_DESTINATION_SHIFT)); + success = ioapic_configure_irq(1, port1_irq | IORED_DELIVERY_NORMAL | ((uint64_t)lapic_get_id() << IORED_DESTINATION_SHIFT)); ASSERT(success); - success = ioapic_configure_irq(12, 102 | IORED_DELIVERY_NORMAL | ((uint64_t)lapic_get_id() << IORED_DESTINATION_SHIFT)); + success = ioapic_configure_irq(12, port2_irq | IORED_DELIVERY_NORMAL | ((uint64_t)lapic_get_id() << IORED_DESTINATION_SHIFT)); ASSERT(success); ps2_cmd_with_data(CMD_TO_CNTRL_OUTPUT, 0x03);