#pragma once // Hardware interrupt registration. // // Until this existed, trap() was a switch statement listing every interrupt // the kernel knew about, and adding a device meant editing the trap handler. // That is fine while the devices are the four the PC has always had at fixed // pins; it stops being fine the moment a device's IRQ is whatever the BIOS // decided to put in its PCI config space this boot. A driver cannot write // `case T_IRQ0 + IRQ_XHCI:` because there is no such constant -- it only // learns its line at probe time. // // So: a line has a chain of handlers, a driver adds itself to one at probe // time, and enabling the line is a consequence of registering rather than a // separate call somebody can forget. // // Chains matter more than they look. PCI interrupt pins are *shared*: four // INTx lines are routed to a handful of IOAPIC inputs, so two unrelated // devices routinely land on the same one. A handler must therefore check // whether its own device actually raised the interrupt and do nothing if it // did not. // Lines 0-23 are the IOAPIC's pins. 24-29 are MSI vectors, which are not pins // at all -- a device writes the vector number to the local APIC directly -- // and are dispatched through the same table so that a handler cannot tell // which kind it is on. 30 and 31 are the LAPIC's error and spurious vectors. // Everything stops below vector 64, which is T_SYSCALL: a line there would // let a user-mode `int $0x40` dispatch an interrupt and EOI one that is not. #define NIRQ_IOAPIC 24 #define IRQ_MSI_FIRST 24 #define IRQ_MSI_LAST 29 #define NIRQ 31 #define NCHAIN 4 // handlers that may share one line typedef void (*irq_handler_t)(void *arg); // Returns 0, or -1 if the line is out of range, reserved, or full. // Registering enables the line; the last unregister masks it again. // // `level_low` selects PCI's signalling (level-triggered, active low) rather // than ISA's (edge-triggered, active high). Getting this wrong does not fail // cleanly: an edge-configured level interrupt fires once and then wedges, // because the device keeps the line asserted and no further edge ever comes. // // It is a mode, not a flag. 0 and 1 are the two above, which is all a PCI or // ISA device needs; the other two are what ACPI can say about a line -- q35's // PCI links and most machines' SCI are level-triggered *active high*, and // programming one active low leaves the pin reading as permanently asserted. #define IRQ_EDGE_HIGH 0 // ISA #define IRQ_LEVEL_LOW 1 // PCI INTx #define IRQ_LEVEL_HIGH 2 #define IRQ_EDGE_LOW 3 #define IRQ_IS_LEVEL(m) ((m) == IRQ_LEVEL_LOW || (m) == IRQ_LEVEL_HIGH) // OR'd into the mode by a handler whose line is *meant* to be silent: the // ACPI SCI fires for a power button or a lid, and on a desktop that is never. // Without it the poller below took two seconds of silence as a misrouted line // and called the handler every tick for the rest of the boot. A line is only // exempt while every handler on it says so -- a PCI device sharing the SCI's // pin still gets polled. #define IRQ_QUIET 0x10 int irq_register(int irq, irq_handler_t fn, void *arg, const char *name, int level_low); void irq_unregister(int irq, irq_handler_t fn, void *arg); int irq_handler_count(int irq); // Called from trap() with the IOAPIC pin number, not the vector. void irq_dispatch(int irq); void irq_init(void); // An MSI vector for pci_irq_attach(). -1 when all six are in use. int irq_alloc_msi(void); void irq_free_msi(int irq); // The thread that polls PCI lines which never interrupt. After sinit(). void irq_poll_start(void); int irq_info(char *buf, int cap); // /proc/interrupts