// SPDX-License-Identifier: GPL-2.012//! IOMMU page table management.3//!4//! C header: [`include/linux/io-pgtable.h`](srctree/include/linux/io-pgtable.h)56use core::{7marker::PhantomData,8ptr::NonNull, //9};1011use crate::{12alloc,13bindings,14device::{15Bound,16Device, //17},18devres::Devres,19error::to_result,20io::PhysAddr,21prelude::*, //22};2324use bindings::io_pgtable_fmt;2526/// Protection flags used with IOMMU mappings.27pub mod prot {28/// Read access.29pub const READ: u32 = bindings::IOMMU_READ;30/// Write access.31pub const WRITE: u32 = bindings::IOMMU_WRITE;32/// Request cache coherency.33pub const CACHE: u32 = bindings::IOMMU_CACHE;34/// Request no-execute permission.35pub const NOEXEC: u32 = bindings::IOMMU_NOEXEC;36/// MMIO peripheral mapping.37pub const MMIO: u32 = bindings::IOMMU_MMIO;38/// Privileged mapping.39pub const PRIVILEGED: u32 = bindings::IOMMU_PRIV;40}4142/// Represents a requested `io_pgtable` configuration.43pub struct Config {44/// Quirk bitmask (type-specific).45pub quirks: usize,46/// Valid page sizes, as a bitmask of powers of two.47pub pgsize_bitmap: usize,48/// Input address space size in bits.49pub ias: u32,50/// Output address space size in bits.51pub oas: u32,52/// IOMMU uses coherent accesses for page table walks.53pub coherent_walk: bool,54}5556/// An io page table using a specific format.57///58/// # Invariants59///60/// The pointer references a valid io page table.61pub struct IoPageTable<F: IoPageTableFmt> {62ptr: NonNull<bindings::io_pgtable_ops>,63_marker: PhantomData<F>,64}6566// SAFETY: `struct io_pgtable_ops` is not restricted to a single thread.67unsafe impl<F: IoPageTableFmt> Send for IoPageTable<F> {}68// SAFETY: `struct io_pgtable_ops` may be accessed concurrently.69unsafe impl<F: IoPageTableFmt> Sync for IoPageTable<F> {}7071/// The format used by this page table.72pub trait IoPageTableFmt: 'static {73/// The value representing this format.74const FORMAT: io_pgtable_fmt;75}7677impl<F: IoPageTableFmt> IoPageTable<F> {78/// Create a new `IoPageTable` as a device resource.79#[inline]80pub fn new(81dev: &Device<Bound>,82config: Config,83) -> impl PinInit<Devres<IoPageTable<F>>, Error> + '_ {84// SAFETY: Devres ensures that the value is dropped during device unbind.85Devres::new(dev, unsafe { Self::new_raw(dev, config) })86}8788/// Create a new `IoPageTable`.89///90/// # Safety91///92/// If successful, then the returned `IoPageTable` must be dropped before the device is93/// unbound.94#[inline]95pub unsafe fn new_raw(dev: &Device<Bound>, config: Config) -> Result<IoPageTable<F>> {96let mut raw_cfg = bindings::io_pgtable_cfg {97quirks: config.quirks,98pgsize_bitmap: config.pgsize_bitmap,99ias: config.ias,100oas: config.oas,101coherent_walk: config.coherent_walk,102tlb: &raw const NOOP_FLUSH_OPS,103iommu_dev: dev.as_raw(),104// SAFETY: All zeroes is a valid value for `struct io_pgtable_cfg`.105..unsafe { core::mem::zeroed() }106};107108// SAFETY:109// * The raw_cfg pointer is valid for the duration of this call.110// * The provided `FLUSH_OPS` contains valid function pointers that accept a null pointer111// as cookie.112// * The caller ensures that the io pgtable does not outlive the device.113let ops = unsafe {114bindings::alloc_io_pgtable_ops(F::FORMAT, &mut raw_cfg, core::ptr::null_mut())115};116117// INVARIANT: We successfully created a valid page table.118Ok(IoPageTable {119ptr: NonNull::new(ops).ok_or(ENOMEM)?,120_marker: PhantomData,121})122}123124/// Obtain a raw pointer to the underlying `struct io_pgtable_ops`.125#[inline]126pub fn raw_ops(&self) -> *mut bindings::io_pgtable_ops {127self.ptr.as_ptr()128}129130/// Obtain a raw pointer to the underlying `struct io_pgtable`.131#[inline]132pub fn raw_pgtable(&self) -> *mut bindings::io_pgtable {133// SAFETY: The io_pgtable_ops of an io-pgtable is always the ops field of a io_pgtable.134unsafe { kernel::container_of!(self.raw_ops(), bindings::io_pgtable, ops) }135}136137/// Obtain a raw pointer to the underlying `struct io_pgtable_cfg`.138#[inline]139pub fn raw_cfg(&self) -> *mut bindings::io_pgtable_cfg {140// SAFETY: The `raw_pgtable()` method returns a valid pointer.141unsafe { &raw mut (*self.raw_pgtable()).cfg }142}143144/// Map a physically contiguous range of pages of the same size.145///146/// Even if successful, this operation may not map the entire range. In that case, only a147/// prefix of the range is mapped, and the returned integer indicates its length in bytes. In148/// this case, the caller will usually call `map_pages` again for the remaining range.149///150/// The returned [`Result`] indicates whether an error was encountered while mapping pages.151/// Note that this may return a non-zero length even if an error was encountered. The caller152/// will usually [unmap the relevant pages](Self::unmap_pages) on error.153///154/// The caller must flush the TLB before using the pgtable to access the newly created mapping.155///156/// # Safety157///158/// * No other io-pgtable operation may access the range `iova .. iova+pgsize*pgcount` while159/// this `map_pages` operation executes.160/// * This page table must not contain any mapping that overlaps with the mapping created by161/// this call.162/// * If this page table is live, then the caller must ensure that it's okay to access the163/// physical address being mapped for the duration in which it is mapped.164#[inline]165pub unsafe fn map_pages(166&self,167iova: usize,168paddr: PhysAddr,169pgsize: usize,170pgcount: usize,171prot: u32,172flags: alloc::Flags,173) -> (usize, Result) {174let mut mapped: usize = 0;175176// SAFETY: The `map_pages` function in `io_pgtable_ops` is never null.177let map_pages = unsafe { (*self.raw_ops()).map_pages.unwrap_unchecked() };178179// SAFETY: The safety requirements of this method are sufficient to call `map_pages`.180let ret = to_result(unsafe {181(map_pages)(182self.raw_ops(),183iova,184paddr,185pgsize,186pgcount,187prot as i32,188flags.as_raw(),189&mut mapped,190)191});192193(mapped, ret)194}195196/// Unmap a range of virtually contiguous pages of the same size.197///198/// This may not unmap the entire range, and returns the length of the unmapped prefix in199/// bytes.200///201/// # Safety202///203/// * No other io-pgtable operation may access the range `iova .. iova+pgsize*pgcount` while204/// this `unmap_pages` operation executes.205/// * This page table must contain one or more consecutive mappings starting at `iova` whose206/// total size is `pgcount * pgsize`.207#[inline]208#[must_use]209pub unsafe fn unmap_pages(&self, iova: usize, pgsize: usize, pgcount: usize) -> usize {210// SAFETY: The `unmap_pages` function in `io_pgtable_ops` is never null.211let unmap_pages = unsafe { (*self.raw_ops()).unmap_pages.unwrap_unchecked() };212213// SAFETY: The safety requirements of this method are sufficient to call `unmap_pages`.214unsafe { (unmap_pages)(self.raw_ops(), iova, pgsize, pgcount, core::ptr::null_mut()) }215}216}217218// For the initial users of these rust bindings, the GPU FW is managing the IOTLB and performs all219// required invalidations using a range. There is no need for it get ARM style invalidation220// instructions from the page table code.221//222// Support for flushing the TLB with ARM style invalidation instructions may be added in the223// future.224static NOOP_FLUSH_OPS: bindings::iommu_flush_ops = bindings::iommu_flush_ops {225tlb_flush_all: Some(rust_tlb_flush_all_noop),226tlb_flush_walk: Some(rust_tlb_flush_walk_noop),227tlb_add_page: None,228};229230#[no_mangle]231extern "C" fn rust_tlb_flush_all_noop(_cookie: *mut core::ffi::c_void) {}232233#[no_mangle]234extern "C" fn rust_tlb_flush_walk_noop(235_iova: usize,236_size: usize,237_granule: usize,238_cookie: *mut core::ffi::c_void,239) {240}241242impl<F: IoPageTableFmt> Drop for IoPageTable<F> {243fn drop(&mut self) {244// SAFETY: The caller of `Self::ttbr()` promised that the page table is not live when this245// destructor runs.246unsafe { bindings::free_io_pgtable_ops(self.raw_ops()) };247}248}249250/// The `ARM_64_LPAE_S1` page table format.251pub enum ARM64LPAES1 {}252253impl IoPageTableFmt for ARM64LPAES1 {254const FORMAT: io_pgtable_fmt = bindings::io_pgtable_fmt_ARM_64_LPAE_S1 as io_pgtable_fmt;255}256257impl IoPageTable<ARM64LPAES1> {258/// Access the `ttbr` field of the configuration.259///260/// This is the physical address of the page table, which may be passed to the device that261/// needs to use it.262///263/// # Safety264///265/// The caller must ensure that the device stops using the page table before dropping it.266#[inline]267pub unsafe fn ttbr(&self) -> u64 {268// SAFETY: `arm_lpae_s1_cfg` is the right cfg type for `ARM64LPAES1`.269unsafe { (*self.raw_cfg()).__bindgen_anon_1.arm_lpae_s1_cfg.ttbr }270}271272/// Access the `mair` field of the configuration.273#[inline]274pub fn mair(&self) -> u64 {275// SAFETY: `arm_lpae_s1_cfg` is the right cfg type for `ARM64LPAES1`.276unsafe { (*self.raw_cfg()).__bindgen_anon_1.arm_lpae_s1_cfg.mair }277}278}279280281