diff --git a/build.zig b/build.zig index 7fd9782..b2b65dc 100644 --- a/build.zig +++ b/build.zig @@ -428,9 +428,9 @@ pub fn build(b: *std.Build) void { // Typed volatile MMIO register access + memory-ordering barriers, for drivers on // top of an mmio_map grant. Depends only on `builtin` (arch-conditional barriers); - // no target set, so it inherits each driver's. See library/mmio/mmio.zig. + // no target set, so it inherits each driver's. See library/device/mmio/mmio.zig. const mmio_module = b.addModule("mmio", .{ - .root_source_file = b.path("library/mmio/mmio.zig"), + .root_source_file = b.path("library/device/mmio/mmio.zig"), }); // A device driver's view of its claimed PCI function: config-space header fields, BAR @@ -969,7 +969,7 @@ pub fn build(b: *std.Build) void { "library/device/acpi/aml/aml.zig", // AML parse + interpret, incl. Notify dispatch (M21) "library/device/usb/usb-abi.zig", // wire sizes + bit packings + set-up packet encodings "library/device/usb/usb-ids.zig", // class/subclass/protocol code assignments - "library/mmio/mmio.zig", // barriers assemble + registers round-trip + "library/device/mmio/mmio.zig", // barriers assemble + registers round-trip "system/drivers/ps2-bus/scancode.zig", // set-2 decode + keyboard state machine "system/drivers/ps2-bus/mouse-packet.zig", // 3-byte mouse packet assembly "system/drivers/usb-hid/hid-report.zig", // HID boot-report keyboard/mouse decode diff --git a/docs/README.md b/docs/README.md index e1f1570..5581d5e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -245,10 +245,9 @@ system/ → /system danos's own internals (the self-representation) fat.zig, engine.zig, on-disk.zig) library/ → /lib libraries, one sub-directory each runtime/ the danos-native runtime + file API (fs) — the stable application ABI - mmio/ volatile register access + memory barriers - device/ device code by domain — model/ pci/ usb/ acpi/ — each a + device/ device code by domain — mmio/ model/ pci/ usb/ acpi/ — each a shareable data module (device-abi, pci-class, usb-abi/ids, - acpi-ids) plus a logic module (pci, usb, aml) + acpi-ids) plus a logic module (mmio, pci, usb, aml) protocol/ driver↔service wire contracts (vfs block display scanout input power device-manager usb-transfer), one module per directory boot/ → /boot the loaders diff --git a/docs/danos-file-system-hierarchy-FSH.md b/docs/danos-file-system-hierarchy-FSH.md index d4b1b43..7c6e887 100644 --- a/docs/danos-file-system-hierarchy-FSH.md +++ b/docs/danos-file-system-hierarchy-FSH.md @@ -87,7 +87,7 @@ A block driver is now **writable, but not yet memory-safe.** Every storage contr worth naming is a bus master: it is programmed by handing it the physical address of a descriptor ring and left to read and write memory on its own. That ring is exactly what **`dma_alloc`** now provides — physically contiguous, pinned, uncacheable, with its -physical address disclosed — and **`/lib/mmio`**'s barriers order the descriptor writes +physical address disclosed — and **`/lib/device/mmio`**'s barriers order the descriptor writes against the doorbell, and **`msi_bind`** delivers completions. So an AHCI or NVMe driver can be written today (the M14/M15 work in [driver-model.md](driver-model.md); the earlier "cannot host a block driver at all" is no longer true). diff --git a/docs/driver-model.md b/docs/driver-model.md index 85479f4..45af635 100644 --- a/docs/driver-model.md +++ b/docs/driver-model.md @@ -106,9 +106,9 @@ module outlived it, which is rather the point.) The pattern generalises directly ``` library/ runtime/ module "runtime" — syscalls, ipc, lifecycle, memory, threads, log, fs - mmio/ module "mmio" — volatile register access + barriers [M14] device/ device code grouped by domain; each domain splits into a shareable data module (enums/wire types, std-only) and a logic module (mmio/IPC) + mmio/ module "mmio" — typed volatile register access + barriers [M14] model/ module "device-abi" — DeviceDescriptor, DeviceClass, ResourceKind pci/ "pci-class" (data) + "pci" — config/BAR/capability walk (Function) usb/ "usb-abi" + "usb-ids" (data) + "usb" — descriptors, control/interrupt/bulk client @@ -160,12 +160,12 @@ class driver, the device manager, or the kernel may share them freely. `callCap` and `replyWait(..., send_cap)`, and class drivers consume them now: the PS/2 keyboard and mouse drivers attach to ps2-bus this way, and `runtime.usb` / `runtime.input` open their per-device and subscription channels with `callCap`. -- **M14** — DMA memory + the memory-ordering layer. `/lib/mmio` gives drivers typed - volatile access and `mb`/`rmb`/`wmb` (per-arch); `dma_alloc`/`dma_free` grant +- **M14** — DMA memory + the memory-ordering layer. `/lib/device/mmio` gives drivers typed + volatile access and `memoryBarrier`/`readMemoryBarrier`/`writeMemoryBarrier` (per-arch); `dma_alloc`/`dma_free` grant physically-contiguous, pinned, uncacheable, reclaim-on-teardown buffers with the physical address exposed (`pmm.allocContiguous`, a DMA arena, `mapUserDmaInto`). `dma_below_4g` caps the address for legacy engines; `dma_write_combining` is accepted - but falls back to coherent until PAT is programmed. The bus drivers use `/lib/mmio`, + but falls back to coherent until PAT is programmed. The bus drivers use `/lib/device/mmio`, and `dma_alloc` has real consumers now: the xHCI driver's rings and contexts, usb-storage's command/status wrappers, virtio-gpu's virtqueue, and the fat service's bounce buffer. @@ -252,7 +252,7 @@ const dev_ep = ipc.callCap(h, // ... mint a per-device endpoint, ## M14 — DMA memory and the memory-ordering contract, for HCDs ✅ done -*Implemented: `/lib/mmio` (typed volatile access + `mb`/`rmb`/`wmb`, per-arch) and +*Implemented: `/lib/device/mmio` (typed volatile access + `memoryBarrier`/`readMemoryBarrier`/`writeMemoryBarrier`, per-arch) and `dma_alloc`/`dma_free` (contiguous, pinned, uncacheable, reclaim-on-teardown, physical address exposed). `dma_write_combining` still falls back to coherent — real WC needs PAT, a small follow-up. The rest of this section is the original design note.* @@ -294,23 +294,23 @@ doorbell.* = i; // volatile store to UC MMIO // nothing stops the compiler reordering these; the device reads a stale descriptor ``` -So the rules, which belong in `library/mmio.zig` and behind `arch`: +So the rules, which belong in `library/device/mmio/mmio.zig` and behind `arch`: | Situation | Required | |---|---| | MMIO register read/write | `mmio.read` / `mmio.write` (volatile) | -| Fill DMA descriptor, then ring doorbell | `wmb()` between them | -| Woken by IRQ, then read what the device wrote | `rmb()` before the read | -| MMIO write that must complete before the next read | `mb()` | +| Fill DMA descriptor, then ring doorbell | `writeMemoryBarrier()` between them | +| Woken by IRQ, then read what the device wrote | `readMemoryBarrier()` before the read | +| MMIO write that must complete before the next read | `memoryBarrier()` | And the per-arch lowering — the reason this must be an `arch` primitive and not a sprinkling of `asm volatile`: | | x86_64 | aarch64 | |---|---|---| -| `mb()` | `mfence` | `dsb sy` | -| `rmb()` | `lfence` | `dsb ld` | -| `wmb()` | `sfence` | `dsb st` | +| `memoryBarrier()` | `mfence` | `dsb sy` | +| `readMemoryBarrier()` | `lfence` | `dsb ld` | +| `writeMemoryBarrier()` | `sfence` | `dsb st` | | DMA cache coherency | coherent; nothing to do | **not guaranteed**; needs non-cacheable buffers or cache maintenance | x86 is forgiving here — TSO plus strong-uncacheable MMIO means you usually get away @@ -318,7 +318,7 @@ with a compiler barrier alone. ARM is not, and [vision.md](vision.md) makes ARM condition. Build the abstraction while there is one caller to fix. (Zig note: `@fence` was **removed in 0.16**. Use `@atomicRmw(..., .seq_cst)` for a full -barrier, or per-arch inline asm — which is what `library/mmio.zig` should hide.) +barrier, or per-arch inline asm — which is what `library/device/mmio/mmio.zig` should hide.) ## M15 — interrupts for PCI devices ✅ done (MSI) diff --git a/docs/drivers.md b/docs/drivers.md index 18bef11..d91ffe8 100644 --- a/docs/drivers.md +++ b/docs/drivers.md @@ -290,8 +290,8 @@ Several things this list used to warn about are now available (see [driver-model.md](driver-model.md)): **port I/O** (`io_read`/`io_write`, claim-gated by the device's `io_port` resource — direct ring-3 `in`/`out` is still a #GP, so a PS/2 or 16550 driver goes through these), **DMA memory** (`dma_alloc`: contiguous, pinned, -uncacheable, physical address exposed), **memory barriers** (`library/mmio`'s -`mb`/`rmb`/`wmb`, imported as the `mmio` module), **fault isolation** (a ring-3 fault kills only the faulting +uncacheable, physical address exposed), **memory barriers** (`library/device/mmio`'s +`memoryBarrier`/`readMemoryBarrier`/`writeMemoryBarrier`, imported as the `mmio` module), **fault isolation** (a ring-3 fault kills only the faulting process — `killCurrentProcess` — and the machine keeps running, [resilience](resilience.md)), and **reclaim + restart on death** (every path out of a process releases its claims and IRQ/MSI bindings — `releaseAllOwnedBy`, diff --git a/library/mmio/mmio.zig b/library/device/mmio/mmio.zig similarity index 60% rename from library/mmio/mmio.zig rename to library/device/mmio/mmio.zig index 1b0534d..d02e385 100644 --- a/library/mmio/mmio.zig +++ b/library/device/mmio/mmio.zig @@ -1,4 +1,4 @@ -//! /lib/mmio — typed volatile MMIO register access, plus the memory-ordering +//! /lib/device/mmio — typed volatile MMIO register access, plus the memory-ordering //! barriers a device driver needs. Used by drivers on top of an `mmio_map` grant. //! //! **`volatile` is not a barrier.** In Zig it means only: don't elide this access, and @@ -11,13 +11,13 @@ //! doorbell.* = i; // volatile store to UC MMIO //! // nothing orders these; the device can read a stale descriptor //! -//! Put a `wmb()` between them. The barriers lower per-architecture — which is the whole -//! reason they are a named primitive and not scattered `asm volatile`: +//! Put a `writeMemoryBarrier()` between them. The barriers lower per-architecture — which +//! is the whole reason they are a named primitive and not scattered `asm volatile`: //! -//! x86_64 aarch64 -//! mb() mfence dsb sy -//! rmb() lfence dsb ld -//! wmb() sfence dsb st +//! x86_64 aarch64 +//! memoryBarrier() mfence dsb sy +//! readMemoryBarrier() lfence dsb ld +//! writeMemoryBarrier() sfence dsb st //! //! x86 is forgiving (TSO + strong-uncacheable MMIO), so a compiler barrier usually //! suffices; ARM is not, and ARM is the win condition (docs/vision.md) — so the @@ -29,52 +29,52 @@ const builtin = @import("builtin"); /// Read a register of type `T` at absolute virtual address `addr` — a location inside /// a device's `mmio_map` grant. `volatile`: never elided, never reordered against /// another volatile access. -pub inline fn read(comptime T: type, addr: usize) T { +pub inline fn readRegister(comptime T: type, addr: usize) T { return @as(*const volatile T, @ptrFromInt(addr)).*; } /// Write `value` of type `T` to the register at absolute virtual address `addr`. -pub inline fn write(comptime T: type, addr: usize, value: T) void { +pub inline fn writeRegister(comptime T: type, addr: usize, value: T) void { @as(*volatile T, @ptrFromInt(addr)).* = value; } -/// Full barrier: all loads and stores before it are globally visible before any after -/// it. Use when an MMIO write must complete before a following read. -pub inline fn mb() void { +/// Full memory barrier: all loads and stores before it are globally visible before any +/// after it. Use when an MMIO write must complete before a following read. +pub inline fn memoryBarrier() void { switch (builtin.target.cpu.arch) { .x86_64 => asm volatile ("mfence" ::: .{ .memory = true }), .aarch64 => asm volatile ("dsb sy" ::: .{ .memory = true }), - else => @compileError("mmio.mb: unsupported architecture"), + else => @compileError("mmio.memoryBarrier: unsupported architecture"), } } -/// Read barrier: loads before it complete before loads after it. Use after an IRQ +/// Read memory barrier: loads before it complete before loads after it. Use after an IRQ /// wake, before reading what the device wrote to shared memory. -pub inline fn rmb() void { +pub inline fn readMemoryBarrier() void { switch (builtin.target.cpu.arch) { .x86_64 => asm volatile ("lfence" ::: .{ .memory = true }), .aarch64 => asm volatile ("dsb ld" ::: .{ .memory = true }), - else => @compileError("mmio.rmb: unsupported architecture"), + else => @compileError("mmio.readMemoryBarrier: unsupported architecture"), } } -/// Write barrier: stores before it become visible before stores after it. Use between -/// filling a DMA descriptor in RAM and ringing the device's doorbell. -pub inline fn wmb() void { +/// Write memory barrier: stores before it become visible before stores after it. Use +/// between filling a DMA descriptor in RAM and ringing the device's doorbell. +pub inline fn writeMemoryBarrier() void { switch (builtin.target.cpu.arch) { .x86_64 => asm volatile ("sfence" ::: .{ .memory = true }), .aarch64 => asm volatile ("dsb st" ::: .{ .memory = true }), - else => @compileError("mmio.wmb: unsupported architecture"), + else => @compileError("mmio.writeMemoryBarrier: unsupported architecture"), } } test "barriers emit and registers round-trip through a RAM cell" { // The barriers must at least assemble for the host arch; ordering can't be unit // tested, but a missing/mistyped mnemonic is caught here. - wmb(); - rmb(); - mb(); + writeMemoryBarrier(); + readMemoryBarrier(); + memoryBarrier(); var cell: u64 = 0; - write(u64, @intFromPtr(&cell), 0xDEAD_BEEF); - try @import("std").testing.expectEqual(@as(u64, 0xDEAD_BEEF), read(u64, @intFromPtr(&cell))); + writeRegister(u64, @intFromPtr(&cell), 0xDEAD_BEEF); + try @import("std").testing.expectEqual(@as(u64, 0xDEAD_BEEF), readRegister(u64, @intFromPtr(&cell))); } diff --git a/library/device/pci/pci.zig b/library/device/pci/pci.zig index bb494b3..24140e0 100644 --- a/library/device/pci/pci.zig +++ b/library/device/pci/pci.zig @@ -32,23 +32,23 @@ pub const Function = struct { } pub fn vendorId(self: *const Function) u16 { - return mmio.read(u16, self.config + pci_class.config_vendor_id); + return mmio.readRegister(u16, self.config + pci_class.config_vendor_id); } pub fn deviceId(self: *const Function) u16 { - return mmio.read(u16, self.config + pci_class.config_device_id); + return mmio.readRegister(u16, self.config + pci_class.config_device_id); } pub fn command(self: *const Function) u16 { - return mmio.read(u16, self.config + pci_class.config_command); + return mmio.readRegister(u16, self.config + pci_class.config_command); } pub fn status(self: *const Function) u16 { - return mmio.read(u16, self.config + pci_class.config_status); + return mmio.readRegister(u16, self.config + pci_class.config_status); } /// Set Memory-Space + Bus-Master enable in the command register. Firmware often leaves /// a secondary display's decode off; a bus-mastering device must enable both. pub fn enableMemoryAndBusMaster(self: *const Function) void { const at = self.config + pci_class.config_command; - mmio.write(u16, at, mmio.read(u16, at) | pci_class.command_memory_and_bus_master); + mmio.writeRegister(u16, at, mmio.readRegister(u16, at) | pci_class.command_memory_and_bus_master); } /// Decode BAR `bar` (0..5) and map it: read the BAR register, reject I/O-space BARs, @@ -60,11 +60,11 @@ pub const Function = struct { if (bar >= 6) return null; if (self.bar_virtual[bar] != 0) return self.bar_virtual[bar]; - const low = mmio.read(u32, self.config + pci_class.config_bar0 + @as(usize, bar) * 4); + const low = mmio.readRegister(u32, self.config + pci_class.config_bar0 + @as(usize, bar) * 4); if (low & pci_class.bar_io_space != 0) return null; // an I/O-space BAR var base: u64 = low & pci_class.bar_memory_base_mask; if ((low & pci_class.bar_type_mask) == pci_class.bar_type_64bit) { // 64-bit: high half is the next dword - const high = mmio.read(u32, self.config + pci_class.config_bar0 + (@as(usize, bar) + 1) * 4); + const high = mmio.readRegister(u32, self.config + pci_class.config_bar0 + (@as(usize, bar) + 1) * 4); base |= @as(u64, high) << 32; } @@ -82,7 +82,7 @@ pub const Function = struct { pub fn capabilities(self: *const Function) CapabilityIterator { const present = self.status() & pci_class.status_capabilities_list != 0; const first = if (present) - mmio.read(u8, self.config + pci_class.config_capabilities_pointer) & pci_class.capability_pointer_mask + mmio.readRegister(u8, self.config + pci_class.config_capabilities_pointer) & pci_class.capability_pointer_mask else 0; return .{ .config = self.config, .cursor = first }; @@ -90,7 +90,7 @@ pub const Function = struct { }; /// One capability header. `offset` is the ABSOLUTE virtual address of the header, so the -/// caller reads its body with `mmio.read(T, cap.offset + n)`. +/// caller reads its body with `mmio.readRegister(T, cap.offset + n)`. pub const Capability = struct { id: u8, offset: usize }; pub const CapabilityIterator = struct { @@ -102,8 +102,8 @@ pub const CapabilityIterator = struct { if (self.cursor == 0 or self.guard >= 48) return null; self.guard += 1; const at = self.config + self.cursor; - const id = mmio.read(u8, at + 0); - self.cursor = mmio.read(u8, at + 1) & pci_class.capability_pointer_mask; + const id = mmio.readRegister(u8, at + 0); + self.cursor = mmio.readRegister(u8, at + 1) & pci_class.capability_pointer_mask; return .{ .id = id, .offset = at }; } }; diff --git a/system/drivers/usb-xhci-bus/usb-xhci-library.zig b/system/drivers/usb-xhci-bus/usb-xhci-library.zig index 656ca98..8306bbd 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-library.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-library.zig @@ -162,7 +162,7 @@ const ProducerRing = struct { // holds it) is written after `parameter`/`status`, with a barrier between. slot.parameter = trb.parameter; slot.status = trb.status; - mmio.wmb(); + mmio.writeMemoryBarrier(); slot.control = control; const physical = self.region.physical + index * @sizeOf(Trb); self.enqueue_index += 1; @@ -628,10 +628,10 @@ pub const Controller = struct { // interrupter is enabled, so a hot-plug port-change event is silently // dropped otherwise. Enabling it is harmless to a polling driver. write32(self.interrupter(interrupter_management), 1 << 1); // IE - mmio.wmb(); + mmio.writeMemoryBarrier(); // Run. - mmio.wmb(); + mmio.writeMemoryBarrier(); write32(self.operational(op_usbcmd), read32(self.operational(op_usbcmd)) | usbcmd_run | usbcmd_interrupter_enable); if (!waitClear(self.operational(op_usbsts), usbsts_halted)) return null; @@ -699,7 +699,7 @@ pub const Controller = struct { /// address of the enqueued TRB (which the Command Completion Event echoes). fn submitCommand(self: *Controller, trb: Trb) u64 { const physical = self.command_ring.push(trb); - mmio.wmb(); + mmio.writeMemoryBarrier(); self.ringDoorbell(0, 0); // doorbell 0, target 0 = command ring return physical; } @@ -711,7 +711,7 @@ pub const Controller = struct { const slot = &ring[self.event_ring.dequeue_index]; const control = slot.control; if ((control & cycle_bit != 0) == self.event_ring.cycle) { - mmio.rmb(); + mmio.readMemoryBarrier(); const event = Trb{ .parameter = slot.parameter, .status = slot.status, .control = control }; self.event_ring.dequeue_index += 1; if (self.event_ring.dequeue_index >= trbs_per_ring) { @@ -1216,7 +1216,7 @@ pub const Controller = struct { .control = trbControl(.status_stage, status_direction | (1 << 5)), // DIR | IOC }); - mmio.wmb(); + mmio.writeMemoryBarrier(); self.ringDoorbell(device.slot_id, 1); // DCI 1 = EP0 const code = self.awaitTransfer(device.slot_id, 1, @intCast(data.len)) orelse return false; if (code != @intFromEnum(CompletionCode.success) and code != @intFromEnum(CompletionCode.short_packet)) return false; @@ -1457,7 +1457,7 @@ pub const Controller = struct { .status = length, .control = trbControl(.normal, (1 << 5)), // IOC }); - mmio.wmb(); + mmio.writeMemoryBarrier(); const number: u8 = endpoint.address & 0x0F; const direction_in = endpoint.address & 0x80 != 0; const dci = doorbellContextIndex(number, direction_in); @@ -1507,7 +1507,7 @@ pub const Controller = struct { .status = subscription.max_length, .control = trbControl(.normal, (1 << 5)), // IOC }); - mmio.wmb(); + mmio.writeMemoryBarrier(); self.ringDoorbell(subscription.slot_id, subscription.dci); } diff --git a/system/drivers/virtio-gpu/virtio-gpu.zig b/system/drivers/virtio-gpu/virtio-gpu.zig index 4d101cf..b485fc3 100644 --- a/system/drivers/virtio-gpu/virtio-gpu.zig +++ b/system/drivers/virtio-gpu/virtio-gpu.zig @@ -102,17 +102,17 @@ var used_shadow: u16 = 0; // --- common-config register access (little-endian MMIO at `common_base`) --------------- fn cfgRead(comptime T: type, comptime field: []const u8) T { - return mmio.read(T, common_base + @offsetOf(vp.CommonCfg, field)); + return mmio.readRegister(T, common_base + @offsetOf(vp.CommonCfg, field)); } fn cfgWrite(comptime T: type, comptime field: []const u8, value: T) void { - mmio.write(T, common_base + @offsetOf(vp.CommonCfg, field), value); + mmio.writeRegister(T, common_base + @offsetOf(vp.CommonCfg, field), value); } /// Write a 64-bit common-config register as two 32-bit halves (low then high) — the widest /// access every virtio-pci host is required to accept for the queue-address registers. fn cfgWrite64(comptime field: []const u8, value: u64) void { const at = common_base + @offsetOf(vp.CommonCfg, field); - mmio.write(u32, at, @truncate(value)); - mmio.write(u32, at + 4, @truncate(value >> 32)); + mmio.writeRegister(u32, at, @truncate(value)); + mmio.writeRegister(u32, at + 4, @truncate(value >> 32)); } fn orStatus(bit: u8) void { cfgWrite(u8, "device_status", cfgRead(u8, "device_status") | bit); @@ -140,12 +140,12 @@ fn submit(request_len: usize, response_len: usize) bool { const avail_ring: [*]u16 = @ptrFromInt(ring.virtual + avail_offset + 4); avail_ring[avail_shadow % queue_size] = 0; // head of the chain is descriptor 0 - mmio.wmb(); + mmio.writeMemoryBarrier(); avail_shadow +%= 1; - mmio.write(u16, ring.virtual + avail_offset + 2, avail_shadow); // avail.idx - mmio.wmb(); + mmio.writeRegister(u16, ring.virtual + avail_offset + 2, avail_shadow); // avail.idx + mmio.writeMemoryBarrier(); - mmio.write(u16, notify_addr, 0); // ring the control queue's doorbell + mmio.writeRegister(u16, notify_addr, 0); // ring the control queue's doorbell return waitUsed(); } @@ -155,8 +155,8 @@ fn submit(request_len: usize, response_len: usize) bool { fn waitUsed() bool { var tries: u32 = 0; while (tries < 2000) : (tries += 1) { - mmio.rmb(); - const idx = mmio.read(u16, ring.virtual + used_offset + 2); // used.idx + mmio.readMemoryBarrier(); + const idx = mmio.readRegister(u16, ring.virtual + used_offset + 2); // used.idx if (idx != used_shadow) { used_shadow = idx; return true; @@ -231,16 +231,16 @@ fn initialise(endpoint: ipc.Handle) bool { var caps = function.capabilities(); while (caps.next()) |cap| { if (cap.id != vp.pci_cap_vendor) continue; - const cfg_type = mmio.read(u8, cap.offset + 3); + const cfg_type = mmio.readRegister(u8, cap.offset + 3); if (cfg_type != vp.cfg_common and cfg_type != vp.cfg_notify) continue; - const bar = mmio.read(u8, cap.offset + 4); - const offset = mmio.read(u32, cap.offset + 8); + const bar = mmio.readRegister(u8, cap.offset + 4); + const offset = mmio.readRegister(u32, cap.offset + 8); if (function.mapBar(bar)) |bar_base| { if (cfg_type == vp.cfg_common) { common_base = bar_base + offset; } else { notify_base = bar_base + offset; - notify_multiplier = mmio.read(u32, cap.offset + 16); // virtio_pci_notify_cap tail + notify_multiplier = mmio.readRegister(u32, cap.offset + 16); // virtio_pci_notify_cap tail } } } @@ -290,7 +290,7 @@ fn initialise(endpoint: ipc.Handle) bool { std.log.info("command-buffer allocation failed", .{}); return false; }; - mmio.write(u16, ring.virtual + avail_offset, 1); // VIRTQ_AVAIL_F_NO_INTERRUPT: we poll + mmio.writeRegister(u16, ring.virtual + avail_offset, 1); // VIRTQ_AVAIL_F_NO_INTERRUPT: we poll cfgWrite(u16, "queue_size", queue_size); cfgWrite64("queue_desc", ring.physical + desc_offset); cfgWrite64("queue_driver", ring.physical + avail_offset); @@ -372,7 +372,7 @@ fn initialise(endpoint: ipc.Handle) bool { } // The scanout surface is CPU-visible RAM: read the pattern back to prove the mapping, // which together with the flush ack above is the automated stand-in for "it's on screen". - mmio.rmb(); + mmio.readMemoryBarrier(); if (pixels[0] != testPixel(0) or pixels[pixel_count / 2] != testPixel(@intCast(pixel_count / 2))) { std.log.info("pixel read-back mismatch", .{}); return false; @@ -435,7 +435,7 @@ fn readEdid() void { /// the panel. Reused by the V3 self-test and by every compositor present over `.scanout`. V4 /// presents the full surface; the damage-rect fast path is a later refinement. fn presentFull() bool { - mmio.wmb(); // the surface writes must be visible before the device transfers them + mmio.writeMemoryBarrier(); // the surface writes must be visible before the device transfers them { // Transfer the current-mode rectangle from the guest backing to the host resource. The // device uses the resource's (max) width as the row stride, so the top-left rect at