Utility to choose good IRQ numbers

This commit is contained in:
Thomas Oltmann 2026-07-08 21:52:16 +02:00
parent ac00d1c031
commit 2abd221789
5 changed files with 57 additions and 6 deletions

View file

@ -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

View file

@ -4,6 +4,8 @@
#include <stdbool.h>
#include <stdint.h>
#define LAPIC_SPURIOUS_VECTOR_NUMBER 0xFF
void lapic_init(void);
unsigned lapic_get_id(void);
void lapic_eoi(void);

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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);