//! Platform-agnostic page map interface. //! //! This file defines the `PMap` wrapper and the two built-in mapping sources //! (`ContiguousSource`, `AllocatingSource`) used to drive `PMap.map_from`. //! //! `PMap` itself is a thin wrapper over `mi.impl.PMap`, which is expected to //! be a `RadixPmap` instantiation (or any type that satisfies the same //! interface). To add a new platform, provide a concrete `impl.PMap` that //! exposes: //! //! - `map_from(va: VAddr, size: usize, source: anytype) void` //! - `activate() void` //! //! Mapping sources are ordinary structs with a single method: //! //! - `next(self: *const Source, va: VAddr) MapItem` //! //! `next` is called once per mapping step and returns the physical address, //! length, and flags for that step. The two sources here cover the common //! cases (contiguous PA range and on-demand allocation); custom sources can //! be passed directly to `map_from` for other needs such as file-backed or //! scatter-gather mappings. const r = @import("root"); const mm = r.mm; const mi = mm.private; /// Describes a contiguous region of physical memory to be mapped at a given virtual address. pub const MapItem = struct { pa: r.PAddr, len: usize, flags: mm.MapFlags, }; /// A mapping source that maps a single contiguous virtual-to-physical region. /// All calls to `next` return the same physical base address and size, so this /// is intended to be consumed exactly once by `map_from`. const ContiguousSource = struct { flags: mm.MapFlags, size: usize, base_va: usize, base_pa: usize, pub fn next(self: *const ContiguousSource, _: r.VAddr) MapItem { return .{ .pa = self.base_pa, .len = self.size, .flags = self.flags, }; } }; /// A mapping source that allocates physical pages on demand. Each call to `next` /// returns a single page, so this is intended to be consumed by `map_from` in a loop until the entire range is mapped. const AllocatingSource = struct { flags: mm.MapFlags, pub fn next(self: *const AllocatingSource, _: r.VAddr) MapItem { const pa = mi.phys.alloc(); return .{ .pa = pa, .len = mm.page_size, .flags = self.flags, }; } }; /// Generic wrapper over the platform-specific pmap implementation. /// Provides some convenient methods for common mapping patterns. pub const PMap = struct { const Self = @This(); impl: mi.impl.PMap, /// Map a virtual address range to physical addresses provided by the source. pub fn map_from(self: *Self, va_start: r.VAddr, size: usize, source: anytype) void { const Source = @TypeOf(source); comptime { if (!@hasDecl(Source, "next")) @compileError("source must provide next(self: *Source, va: b.VAddr) MapItem"); } self.impl.map_from(va_start, size, source); } /// Map a contiguous virtual address range to a contiguous physical address range. pub fn map_contiguous_range(self: *Self, va: r.VAddr, pa: r.PAddr, size: usize, flags: mm.MapFlags) void { const src = ContiguousSource{ .flags = flags, .size = size, .base_va = va, .base_pa = pa, }; self.map_from(va, size, src); } /// Map a virtual address range to physical addresses allocated on demand. pub fn map_range_allocating(self: *Self, va: r.VAddr, size: usize, flags: mm.MapFlags) void { const src = AllocatingSource{ .flags = flags, }; self.map_from(va, size, src); } /// Unmap a contiguous range of virtual pages. /// Returns a list threaded through PFNs of the unmapped physical pages if any. pub fn unmap(self: *Self, va: r.VAddr, size: usize) ?mm.Pfn { return self.impl.unmap(va, size); } /// Flush the TLB entry of a single page. pub fn flush(self: *Self, va: r.VAddr) void { self.impl.flush(va); } /// Map a single page. Convenience wrapper around map_contiguous_range. pub fn map_page(self: *Self, va: r.VAddr, pa: r.PAddr, flags: mm.MapFlags) void { self.map_contiguous_range(va, pa, mm.page_size, flags); } /// Activate this pagemap. pub fn activate(self: *Self) void { self.impl.activate(); } /// Given a virtual page, return its physical page, if present. pub fn query(self: *Self, va: r.VAddr) ?r.PAddr { return self.impl.query(va); } };