78 lines
3.8 KiB
Zig
78 lines
3.8 KiB
Zig
//! x86-64 IOMMU backends, behind the architecture boundary: Intel VT-d
|
|
//! (iommu-intel.zig) and AMD-Vi (iommu-amd.zig). The architecture-neutral
|
|
//! core (system/kernel/iommu.zig) owns the domain table and the shared
|
|
//! page-table walker; it hands this file the firmware discovery facts and an
|
|
//! environment (frame allocation + the log sink, injected the same way
|
|
//! enablePaging receives its frame hooks), and gets back a hardware vtable.
|
|
//! A new architecture supplies its own unit (ARM: the SMMU) from its own
|
|
//! directory with no core change.
|
|
|
|
const intel = @import("iommu-intel.zig");
|
|
const amd = @import("iommu-amd.zig");
|
|
|
|
/// What the platform's firmware tables reported: where the unit's registers
|
|
/// live, and which programming model its table implies (an IVRS table
|
|
/// describes AMD-Vi; a DMAR table describes Intel VT-d).
|
|
pub const Discovery = struct {
|
|
register_base: u64,
|
|
amd: bool,
|
|
};
|
|
|
|
/// What the backends need from the generic kernel, injected at detect so this
|
|
/// module never imports kernel internals: physical-frame allocation for the
|
|
/// hardware structures, and the kernel log sink (fault reports, warnings, the
|
|
/// enable banner).
|
|
pub const Environment = struct {
|
|
allocateFrame: *const fn () ?u64,
|
|
allocateContiguous: *const fn (count: usize, max_physical: u64) ?u64,
|
|
write: *const fn (bytes: []const u8) void,
|
|
};
|
|
|
|
/// The bit encodings and hardware operations a backend supplies to the shared
|
|
/// core. Entry helpers build the raw page-table entries for the backend's
|
|
/// format; the core walks the tree with them. The hardware ops act on a whole
|
|
/// domain (identified by its hardware domain id = core index + 1) or device
|
|
/// (by requester id / bdf).
|
|
pub const Backend = struct {
|
|
/// Number of page-table levels (3 or 4) the backend selected from hardware caps.
|
|
levels: u8,
|
|
/// Largest leaf the walker may emit: 4 KiB always, 2 MiB when the backend allows.
|
|
supports_huge_pages: bool,
|
|
|
|
/// Raw entry bits for a leaf mapping `physical` (with the given size), and for a
|
|
/// non-leaf entry pointing at `table_physical` at `level` (level counts down to 1
|
|
/// at the leaf's parent). `isPresent` tests a read-back entry.
|
|
makeLeaf: *const fn (physical: u64, huge: bool) u64,
|
|
makeTable: *const fn (table_physical: u64, level: u8) u64,
|
|
isPresent: *const fn (entry: u64) bool,
|
|
/// Flush a cache line holding IOMMU structures the hardware reads non-coherently
|
|
/// (VT-d with ECAP.C==0). A no-op where the unit snoops caches.
|
|
flushStructure: *const fn (address: usize) void,
|
|
|
|
/// Turn translation on (the core has already seeded any pre-claim domains)
|
|
/// and write the unit's identity lines to the log — the core follows with
|
|
/// the neutral posture lines.
|
|
enable: *const fn () void,
|
|
/// Point `bdf`'s translation structure at `domain` (hardware id) and invalidate the
|
|
/// context/device caches so the change takes effect.
|
|
attach: *const fn (bdf: u16, domain: u16, page_table_root: u64) void,
|
|
/// Return `bdf`'s translation structure to not-present + invalidate — all its DMA
|
|
/// faults afterward.
|
|
detach: *const fn (bdf: u16) void,
|
|
/// Invalidate cached translations for `domain` (after a map or unmap).
|
|
invalidateDomain: *const fn (domain: u16) void,
|
|
/// Pull pending faults out of the hardware, log them (rate-limited), return the
|
|
/// count seen this call.
|
|
faultDrain: *const fn () usize,
|
|
};
|
|
|
|
/// The injected kernel services, stored for the backends at detect time.
|
|
pub var environment: Environment = undefined;
|
|
|
|
/// Probe the discovered unit and return its vtable, or null when it is
|
|
/// unusable (the core stays fail-open and says so).
|
|
pub fn detect(discovery: Discovery, injected: Environment) ?Backend {
|
|
environment = injected;
|
|
return if (discovery.amd) amd.detect(discovery) else intel.detect(discovery);
|
|
}
|