//! User-space DMA memory: `dma_alloc` / `dma_free`. A driver that programs a //! bus-mastering engine needs a descriptor ring the device can read — memory that is //! physically contiguous, at a physical address the driver knows, uncacheable, and //! pinned. `mmap` gives none of those; this does. Pair it with the barriers in //! `/lib/mmio` (fill the ring, `wmb()`, ring the doorbell). See docs/driver-model.md. const abi = @import("abi"); const sc = @import("system-call.zig"); /// Allocation flags. `coherent` (uncacheable) is the portable default; the rest are /// opt-in for specific hardware — see `abi`. pub const coherent: usize = abi.dma_coherent; pub const write_combining: usize = abi.dma_write_combining; pub const below_4g: usize = abi.dma_below_4g; /// A DMA allocation: the `virtual` address the CPU touches, and the `physical` address /// to program into the device's descriptor-ring / base registers. pub const Region = struct { virtual: usize, physical: usize, }; inline fn failed(r: usize) bool { return r > ~@as(usize, 0) - 4095; } /// Allocate `len` bytes of DMA-capable memory with `flags` (e.g. `coherent`, or /// `coherent | below_4g`). Returns the virtual/physical pair, or null on failure. Two /// return values — the virtual address in rax, the physical address in rdx — so it /// needs a hand-written stub. pub fn alloc(len: usize, flags: usize) ?Region { var rax: usize = undefined; var rdx: usize = undefined; // out: physical address asm volatile ("syscall" : [rax] "={rax}" (rax), [rdx] "={rdx}" (rdx), : [n] "{rax}" (@intFromEnum(abi.SystemCall.dma_alloc)), [a0] "{rdi}" (len), [a1] "{rsi}" (flags), : .{ .rcx = true, .r11 = true, .memory = true }); if (failed(rax)) return null; return .{ .virtual = rax, .physical = rdx }; } /// Release a region from a prior `alloc` (`virtual` and the same `len`). pub fn free(virtual: usize, len: usize) void { _ = sc.systemCall2(.dma_free, virtual, len); }