//! 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); }