M15: interrupts for PCI devices (ECAM config space + MSI)
Two parts, both blockers for real PCI drivers. ECAM config space per function: enumeratePci now gives every pci_device its own 4 KiB configuration window as resource 0. A claimed PCI driver mmio_maps that to reach its command register, BARs, and — the point — its capability list (MSI/MSI-X, PCIe extended caps), with no new syscall. Verified in the discovery test (QEMU q35's functions each carry it). MSI: msi_bind(device_id, endpoint) -> address (rax), data (rdx) allocates a per-device edge-triggered vector, binds it to the endpoint, and returns the (address, data) the driver programs into its own MSI capability. Unlike irq_bind there's no GSI, no I/O APIC entry, no sharing, and no ack cycle — dispatch recognises an MSI vector (vector_gsi == none, msi_bound set), EOIs, and notifies. Owner-keyed release drops the binding on exit. Legacy INTx (_PRT parsing + shared lines) is deliberately skipped; MSI is the real answer. QEMU's HPET has no MSI, so the new `msi` test proves the vector-routing path with a self-IPI (new apic.selfIpi) standing in for the device's MSI write: bind a vector, fire it, the bound endpoint is notified. The msi_bind syscall wraps irq.msiBind with the claim check and lands its first real use with the first PCI driver. Suite 39/39 plus host tests.
This commit is contained in:
@@ -159,6 +159,7 @@ fn system_call(state: *architecture.CpuState) void {
|
||||
.system_spawn => systemSpawn(state),
|
||||
.dma_alloc => systemDmaAlloc(state),
|
||||
.dma_free => systemDmaFree(state),
|
||||
.msi_bind => systemMsiBind(state),
|
||||
_ => fail(state),
|
||||
}
|
||||
}
|
||||
@@ -419,6 +420,29 @@ fn systemIrqBind(state: *architecture.CpuState) void {
|
||||
architecture.setSystemCallResult(state, 0);
|
||||
}
|
||||
|
||||
/// msi_bind(device_id, endpoint_handle) -> address (rax), data (rdx): set up
|
||||
/// Message-Signalled Interrupts for a claimed device. The kernel allocates a vector,
|
||||
/// binds it to `endpoint`, and hands back the (address, data) the driver programs into
|
||||
/// its own MSI capability (found via its ECAM config space, resource 0). Unlike
|
||||
/// `irq_bind` there is no GSI, no sharing, and no ack — MSI is edge-triggered. The
|
||||
/// claim is the capability. See docs/driver-model.md (M15).
|
||||
fn systemMsiBind(state: *architecture.CpuState) void {
|
||||
const device_id = architecture.systemCallArg(state, 0);
|
||||
const t = scheduler.current();
|
||||
if (t.aspace == 0) return fail(state);
|
||||
const owner = devices_broker.ownerOf(device_id) orelse return fail(state);
|
||||
if (owner != t.id) return fail(state); // not claimed by this process
|
||||
const endpoint = ipc.resolveHandle(t, architecture.systemCallArg(state, 1)) orelse return failErr(state, ipc.EBADF);
|
||||
|
||||
const flags = sync.enter();
|
||||
defer sync.leave(flags);
|
||||
const vector = irq.msiBind(endpoint, t.id) catch return fail(state);
|
||||
// x86 MSI: the address routes to the BSP's LAPIC (physical destination, fixed
|
||||
// delivery — dest field 0); the data carries the vector.
|
||||
architecture.setSystemCallResult(state, abi.msi_address_base);
|
||||
architecture.setSystemCallResult2(state, vector);
|
||||
}
|
||||
|
||||
/// irq_ack(device_id, resource_index) -> 0/-1: re-arm a bound IRQ. The ISR left the line
|
||||
/// masked (it could not quiet the device — that's this driver's job), so nothing
|
||||
/// more arrives until the driver says it has serviced the hardware.
|
||||
|
||||
Reference in New Issue
Block a user