- schinit, schinitd and vgpud build every message from pieces, one
write() each, so a job starting on another CPU landed mid-line:
"schinitd: starting vgpud: os.schos.resolvconfno virtio-gpu on this
machine". They now collect a line and write it at the newline;
resolvconf's verbose line is one write with its prefix.
- The ACPI SCI registers IRQ_QUIET. A desktop's SCI never fires, and the
poller took two seconds of silence for a misrouted line ("irq 9:
nothing has arrived on it") and called the handler every tick. A line
is only exempt while every handler on it is quiet.
- The loader claims each segment from where the last one's claim ended,
so two segments sharing a page no longer ask for it twice. When a
claim is still refused, it names the memory-map entries in the way.
- Stale divesymlink() declaration gone; the kernel.md struct table
re-measured (inode, buf and file pools grow from slabs now).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
77 lines
3.6 KiB
C
77 lines
3.6 KiB
C
#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
|