49 lines
2.0 KiB
Zig
49 lines
2.0 KiB
Zig
//! 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);
|
|
}
|