1*f2dc32d5SBoris Brezillon // SPDX-License-Identifier: GPL-2.0 or MIT 2*f2dc32d5SBoris Brezillon 3*f2dc32d5SBoris Brezillon //! GPU virtual memory management using the DRM GPUVM framework. 4*f2dc32d5SBoris Brezillon //! 5*f2dc32d5SBoris Brezillon //! This module manages GPU virtual address spaces, providing memory isolation and 6*f2dc32d5SBoris Brezillon //! the illusion of owning the entire virtual address (VA) range, similar to CPU virtual memory. 7*f2dc32d5SBoris Brezillon //! Each virtual memory (VM) area is backed by ARM64 LPAE Stage 1 page tables and can be 8*f2dc32d5SBoris Brezillon //! mapped into hardware address space (AS) slots for GPU execution. 9*f2dc32d5SBoris Brezillon 10*f2dc32d5SBoris Brezillon use core::marker::PhantomData; 11*f2dc32d5SBoris Brezillon use core::ops::Range; 12*f2dc32d5SBoris Brezillon 13*f2dc32d5SBoris Brezillon use kernel::{ 14*f2dc32d5SBoris Brezillon device::{ 15*f2dc32d5SBoris Brezillon Bound, 16*f2dc32d5SBoris Brezillon Device, // 17*f2dc32d5SBoris Brezillon }, 18*f2dc32d5SBoris Brezillon drm::{ 19*f2dc32d5SBoris Brezillon gem::BaseObject, 20*f2dc32d5SBoris Brezillon gpuvm::{ 21*f2dc32d5SBoris Brezillon DriverGpuVm, 22*f2dc32d5SBoris Brezillon GpuVaAlloc, 23*f2dc32d5SBoris Brezillon GpuVm, 24*f2dc32d5SBoris Brezillon GpuVmBo, 25*f2dc32d5SBoris Brezillon OpMap, 26*f2dc32d5SBoris Brezillon OpMapRequest, 27*f2dc32d5SBoris Brezillon OpMapped, 28*f2dc32d5SBoris Brezillon OpRemap, 29*f2dc32d5SBoris Brezillon OpRemapped, 30*f2dc32d5SBoris Brezillon OpUnmap, 31*f2dc32d5SBoris Brezillon OpUnmapped, 32*f2dc32d5SBoris Brezillon UniqueRefGpuVm, // 33*f2dc32d5SBoris Brezillon }, // 34*f2dc32d5SBoris Brezillon }, 35*f2dc32d5SBoris Brezillon fmt, 36*f2dc32d5SBoris Brezillon impl_flags, 37*f2dc32d5SBoris Brezillon io::PhysAddr, 38*f2dc32d5SBoris Brezillon iommu::pgtable::{ 39*f2dc32d5SBoris Brezillon prot, 40*f2dc32d5SBoris Brezillon IoPageTable, 41*f2dc32d5SBoris Brezillon ARM64LPAES1, // 42*f2dc32d5SBoris Brezillon }, 43*f2dc32d5SBoris Brezillon new_mutex, 44*f2dc32d5SBoris Brezillon prelude::*, 45*f2dc32d5SBoris Brezillon sizes::{ 46*f2dc32d5SBoris Brezillon SZ_1G, 47*f2dc32d5SBoris Brezillon SZ_2M, 48*f2dc32d5SBoris Brezillon SZ_4K, // 49*f2dc32d5SBoris Brezillon }, 50*f2dc32d5SBoris Brezillon sync::{ 51*f2dc32d5SBoris Brezillon aref::ARef, 52*f2dc32d5SBoris Brezillon Arc, 53*f2dc32d5SBoris Brezillon ArcBorrow, 54*f2dc32d5SBoris Brezillon Mutex, // 55*f2dc32d5SBoris Brezillon }, 56*f2dc32d5SBoris Brezillon uapi, // 57*f2dc32d5SBoris Brezillon }; 58*f2dc32d5SBoris Brezillon 59*f2dc32d5SBoris Brezillon use crate::{ 60*f2dc32d5SBoris Brezillon driver::{ 61*f2dc32d5SBoris Brezillon TyrDrmDevice, 62*f2dc32d5SBoris Brezillon TyrDrmDriver, // 63*f2dc32d5SBoris Brezillon }, 64*f2dc32d5SBoris Brezillon gem, 65*f2dc32d5SBoris Brezillon gem::Bo, 66*f2dc32d5SBoris Brezillon gpu::GpuInfo, 67*f2dc32d5SBoris Brezillon mmu::{ 68*f2dc32d5SBoris Brezillon address_space::VmAsData, 69*f2dc32d5SBoris Brezillon Mmu, // 70*f2dc32d5SBoris Brezillon }, 71*f2dc32d5SBoris Brezillon regs::gpu_control::MMU_FEATURES, 72*f2dc32d5SBoris Brezillon }; 73*f2dc32d5SBoris Brezillon 74*f2dc32d5SBoris Brezillon impl_flags!( 75*f2dc32d5SBoris Brezillon /// Flags controlling virtual memory mapping behavior. 76*f2dc32d5SBoris Brezillon /// 77*f2dc32d5SBoris Brezillon /// These flags control access permissions and caching behavior for GPU virtual 78*f2dc32d5SBoris Brezillon /// memory mappings. 79*f2dc32d5SBoris Brezillon #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] 80*f2dc32d5SBoris Brezillon pub(crate) struct VmMapFlags(u32); 81*f2dc32d5SBoris Brezillon 82*f2dc32d5SBoris Brezillon /// Individual flags that can be combined in [`VmMapFlags`]. 83*f2dc32d5SBoris Brezillon #[derive(Debug, Clone, Copy, PartialEq, Eq)] 84*f2dc32d5SBoris Brezillon pub(crate) enum VmFlag { 85*f2dc32d5SBoris Brezillon /// Map as read-only. 86*f2dc32d5SBoris Brezillon Readonly = uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_OP_MAP_READONLY as u32, 87*f2dc32d5SBoris Brezillon /// Map as non-executable. 88*f2dc32d5SBoris Brezillon Noexec = uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC as u32, 89*f2dc32d5SBoris Brezillon /// Map as uncached. 90*f2dc32d5SBoris Brezillon Uncached = uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED as u32, 91*f2dc32d5SBoris Brezillon } 92*f2dc32d5SBoris Brezillon ); 93*f2dc32d5SBoris Brezillon 94*f2dc32d5SBoris Brezillon impl VmMapFlags { 95*f2dc32d5SBoris Brezillon /// Convert the flags to `pgtable::prot`. 96*f2dc32d5SBoris Brezillon fn to_prot(self) -> u32 { 97*f2dc32d5SBoris Brezillon let mut prot = 0; 98*f2dc32d5SBoris Brezillon 99*f2dc32d5SBoris Brezillon if self.contains(VmFlag::Readonly) { 100*f2dc32d5SBoris Brezillon prot |= prot::READ; 101*f2dc32d5SBoris Brezillon } else { 102*f2dc32d5SBoris Brezillon prot |= prot::READ | prot::WRITE; 103*f2dc32d5SBoris Brezillon } 104*f2dc32d5SBoris Brezillon 105*f2dc32d5SBoris Brezillon if self.contains(VmFlag::Noexec) { 106*f2dc32d5SBoris Brezillon prot |= prot::NOEXEC; 107*f2dc32d5SBoris Brezillon } 108*f2dc32d5SBoris Brezillon 109*f2dc32d5SBoris Brezillon if !self.contains(VmFlag::Uncached) { 110*f2dc32d5SBoris Brezillon prot |= prot::CACHE; 111*f2dc32d5SBoris Brezillon } 112*f2dc32d5SBoris Brezillon 113*f2dc32d5SBoris Brezillon prot 114*f2dc32d5SBoris Brezillon } 115*f2dc32d5SBoris Brezillon } 116*f2dc32d5SBoris Brezillon 117*f2dc32d5SBoris Brezillon impl fmt::Display for VmMapFlags { 118*f2dc32d5SBoris Brezillon fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 119*f2dc32d5SBoris Brezillon let mut first = true; 120*f2dc32d5SBoris Brezillon 121*f2dc32d5SBoris Brezillon if self.contains(VmFlag::Readonly) { 122*f2dc32d5SBoris Brezillon write!(f, "READONLY")?; 123*f2dc32d5SBoris Brezillon first = false; 124*f2dc32d5SBoris Brezillon } 125*f2dc32d5SBoris Brezillon if self.contains(VmFlag::Noexec) { 126*f2dc32d5SBoris Brezillon if !first { 127*f2dc32d5SBoris Brezillon write!(f, " | ")?; 128*f2dc32d5SBoris Brezillon } 129*f2dc32d5SBoris Brezillon write!(f, "NOEXEC")?; 130*f2dc32d5SBoris Brezillon first = false; 131*f2dc32d5SBoris Brezillon } 132*f2dc32d5SBoris Brezillon 133*f2dc32d5SBoris Brezillon if self.contains(VmFlag::Uncached) { 134*f2dc32d5SBoris Brezillon if !first { 135*f2dc32d5SBoris Brezillon write!(f, " | ")?; 136*f2dc32d5SBoris Brezillon } 137*f2dc32d5SBoris Brezillon write!(f, "UNCACHED")?; 138*f2dc32d5SBoris Brezillon } 139*f2dc32d5SBoris Brezillon 140*f2dc32d5SBoris Brezillon Ok(()) 141*f2dc32d5SBoris Brezillon } 142*f2dc32d5SBoris Brezillon } 143*f2dc32d5SBoris Brezillon 144*f2dc32d5SBoris Brezillon impl TryFrom<u32> for VmMapFlags { 145*f2dc32d5SBoris Brezillon type Error = Error; 146*f2dc32d5SBoris Brezillon 147*f2dc32d5SBoris Brezillon fn try_from(value: u32) -> Result<Self, Self::Error> { 148*f2dc32d5SBoris Brezillon let valid = VmFlag::Readonly as u32 | VmFlag::Noexec as u32 | VmFlag::Uncached as u32; 149*f2dc32d5SBoris Brezillon 150*f2dc32d5SBoris Brezillon if value & !valid != 0 { 151*f2dc32d5SBoris Brezillon return Err(EINVAL); 152*f2dc32d5SBoris Brezillon } 153*f2dc32d5SBoris Brezillon Ok(Self(value)) 154*f2dc32d5SBoris Brezillon } 155*f2dc32d5SBoris Brezillon } 156*f2dc32d5SBoris Brezillon 157*f2dc32d5SBoris Brezillon /// Arguments for a virtual memory map operation. 158*f2dc32d5SBoris Brezillon struct VmMapArgs<'drm> { 159*f2dc32d5SBoris Brezillon /// Access permissions and caching behavior for the mapping. 160*f2dc32d5SBoris Brezillon flags: VmMapFlags, 161*f2dc32d5SBoris Brezillon /// GEM buffer object registered with the GPUVM framework. 162*f2dc32d5SBoris Brezillon vm_bo: ARef<GpuVmBo<GpuVmData<'drm>>>, 163*f2dc32d5SBoris Brezillon /// Offset in bytes from the start of the buffer object. 164*f2dc32d5SBoris Brezillon bo_offset: u64, 165*f2dc32d5SBoris Brezillon } 166*f2dc32d5SBoris Brezillon 167*f2dc32d5SBoris Brezillon /// Type of virtual memory operation. 168*f2dc32d5SBoris Brezillon enum VmOpType<'drm> { 169*f2dc32d5SBoris Brezillon /// Map a GEM buffer object into the virtual address space. 170*f2dc32d5SBoris Brezillon Map(VmMapArgs<'drm>), 171*f2dc32d5SBoris Brezillon /// Unmap a region from the virtual address space. 172*f2dc32d5SBoris Brezillon Unmap, 173*f2dc32d5SBoris Brezillon } 174*f2dc32d5SBoris Brezillon 175*f2dc32d5SBoris Brezillon /// Preallocated resources needed to execute a VM operation. 176*f2dc32d5SBoris Brezillon /// 177*f2dc32d5SBoris Brezillon /// VM operations may require allocating new GPUVA objects to track mappings. 178*f2dc32d5SBoris Brezillon /// To avoid allocation failures during the operation, preallocate the 179*f2dc32d5SBoris Brezillon /// maximum number of GPUVAs that might be needed. 180*f2dc32d5SBoris Brezillon struct VmOpResources<'drm> { 181*f2dc32d5SBoris Brezillon /// Preallocated GPUVA objects for remap operations. 182*f2dc32d5SBoris Brezillon /// 183*f2dc32d5SBoris Brezillon /// Partial unmap requests or map requests overlapping existing mappings 184*f2dc32d5SBoris Brezillon /// will trigger a remap call, which needs to register up to three VA 185*f2dc32d5SBoris Brezillon /// objects (one for the new mapping, and two for the previous and next 186*f2dc32d5SBoris Brezillon /// mappings). 187*f2dc32d5SBoris Brezillon preallocated_gpuvas: [Option<GpuVaAlloc<GpuVmData<'drm>>>; 3], 188*f2dc32d5SBoris Brezillon } 189*f2dc32d5SBoris Brezillon 190*f2dc32d5SBoris Brezillon /// Request to execute a virtual memory operation. 191*f2dc32d5SBoris Brezillon struct VmOpRequest<'drm> { 192*f2dc32d5SBoris Brezillon /// Request type. 193*f2dc32d5SBoris Brezillon op_type: VmOpType<'drm>, 194*f2dc32d5SBoris Brezillon 195*f2dc32d5SBoris Brezillon /// Region of the virtual address space covered by this request. 196*f2dc32d5SBoris Brezillon region: Range<u64>, 197*f2dc32d5SBoris Brezillon } 198*f2dc32d5SBoris Brezillon 199*f2dc32d5SBoris Brezillon /// Arguments for a page table map operation. 200*f2dc32d5SBoris Brezillon struct PtMapArgs { 201*f2dc32d5SBoris Brezillon /// Memory protection flags describing allowed accesses for this mapping. 202*f2dc32d5SBoris Brezillon /// 203*f2dc32d5SBoris Brezillon /// This is directly derived from [`VmMapFlags`] via [`VmMapFlags::to_prot`]. 204*f2dc32d5SBoris Brezillon prot: u32, 205*f2dc32d5SBoris Brezillon } 206*f2dc32d5SBoris Brezillon 207*f2dc32d5SBoris Brezillon /// Type of page table operation. 208*f2dc32d5SBoris Brezillon enum PtOpType { 209*f2dc32d5SBoris Brezillon /// Map pages into the page table. 210*f2dc32d5SBoris Brezillon Map(PtMapArgs), 211*f2dc32d5SBoris Brezillon /// Unmap pages from the page table. 212*f2dc32d5SBoris Brezillon Unmap, 213*f2dc32d5SBoris Brezillon } 214*f2dc32d5SBoris Brezillon 215*f2dc32d5SBoris Brezillon /// Context for updating the GPU page table. 216*f2dc32d5SBoris Brezillon /// 217*f2dc32d5SBoris Brezillon /// This context is created when beginning a page table update operation and 218*f2dc32d5SBoris Brezillon /// automatically flushes changes when dropped. It ensures that the 219*f2dc32d5SBoris Brezillon /// Memory Management Unit (MMU) state is properly managed and Translation 220*f2dc32d5SBoris Brezillon /// Lookaside Buffer (TLB) entries are flushed. 221*f2dc32d5SBoris Brezillon pub(crate) struct PtUpdateContext<'ctx, 'drm> { 222*f2dc32d5SBoris Brezillon /// Device used for DMA-mapping GEM shmem SG tables. 223*f2dc32d5SBoris Brezillon dev: &'ctx Device<Bound>, 224*f2dc32d5SBoris Brezillon 225*f2dc32d5SBoris Brezillon /// Page table. 226*f2dc32d5SBoris Brezillon pt: &'ctx IoPageTable<'drm, ARM64LPAES1>, 227*f2dc32d5SBoris Brezillon 228*f2dc32d5SBoris Brezillon /// MMU manager. 229*f2dc32d5SBoris Brezillon mmu: &'ctx Mmu<'drm>, 230*f2dc32d5SBoris Brezillon 231*f2dc32d5SBoris Brezillon /// Reference to the address space data to pass to the MMU functions. 232*f2dc32d5SBoris Brezillon as_data: &'ctx VmAsData<'drm>, 233*f2dc32d5SBoris Brezillon 234*f2dc32d5SBoris Brezillon /// Region of the virtual address space covered by this request. 235*f2dc32d5SBoris Brezillon region: Range<u64>, 236*f2dc32d5SBoris Brezillon 237*f2dc32d5SBoris Brezillon /// Operation type. 238*f2dc32d5SBoris Brezillon op_type: PtOpType, 239*f2dc32d5SBoris Brezillon 240*f2dc32d5SBoris Brezillon /// Preallocated resources that can be used when executing the request. 241*f2dc32d5SBoris Brezillon resources: &'ctx mut VmOpResources<'drm>, 242*f2dc32d5SBoris Brezillon } 243*f2dc32d5SBoris Brezillon 244*f2dc32d5SBoris Brezillon impl<'ctx, 'drm> PtUpdateContext<'ctx, 'drm> { 245*f2dc32d5SBoris Brezillon /// Creates a new page table update context. 246*f2dc32d5SBoris Brezillon /// 247*f2dc32d5SBoris Brezillon /// This prepares the MMU for a page table update. 248*f2dc32d5SBoris Brezillon /// The context will automatically flush the TLB and 249*f2dc32d5SBoris Brezillon /// complete the update when dropped. 250*f2dc32d5SBoris Brezillon fn new( 251*f2dc32d5SBoris Brezillon dev: &'ctx Device<Bound>, 252*f2dc32d5SBoris Brezillon pt: &'ctx IoPageTable<'drm, ARM64LPAES1>, 253*f2dc32d5SBoris Brezillon mmu: &'ctx Mmu<'drm>, 254*f2dc32d5SBoris Brezillon as_data: &'ctx VmAsData<'drm>, 255*f2dc32d5SBoris Brezillon region: Range<u64>, 256*f2dc32d5SBoris Brezillon op_type: PtOpType, 257*f2dc32d5SBoris Brezillon resources: &'ctx mut VmOpResources<'drm>, 258*f2dc32d5SBoris Brezillon ) -> Result<PtUpdateContext<'ctx, 'drm>> { 259*f2dc32d5SBoris Brezillon mmu.start_vm_update(as_data, ®ion)?; 260*f2dc32d5SBoris Brezillon 261*f2dc32d5SBoris Brezillon Ok(Self { 262*f2dc32d5SBoris Brezillon dev, 263*f2dc32d5SBoris Brezillon pt, 264*f2dc32d5SBoris Brezillon mmu, 265*f2dc32d5SBoris Brezillon as_data, 266*f2dc32d5SBoris Brezillon region, 267*f2dc32d5SBoris Brezillon op_type, 268*f2dc32d5SBoris Brezillon resources, 269*f2dc32d5SBoris Brezillon }) 270*f2dc32d5SBoris Brezillon } 271*f2dc32d5SBoris Brezillon 272*f2dc32d5SBoris Brezillon /// Finds one of our pre-allocated VAs. 273*f2dc32d5SBoris Brezillon fn preallocated_gpuva(&mut self) -> Result<GpuVaAlloc<GpuVmData<'drm>>> { 274*f2dc32d5SBoris Brezillon self.resources 275*f2dc32d5SBoris Brezillon .preallocated_gpuvas 276*f2dc32d5SBoris Brezillon .iter_mut() 277*f2dc32d5SBoris Brezillon .find_map(|f| f.take()) 278*f2dc32d5SBoris Brezillon .ok_or(EINVAL) 279*f2dc32d5SBoris Brezillon } 280*f2dc32d5SBoris Brezillon 281*f2dc32d5SBoris Brezillon /// Returns an unused GPUVA object to the preallocated pool. 282*f2dc32d5SBoris Brezillon /// If the pool is already full, the unused allocation is simply dropped. 283*f2dc32d5SBoris Brezillon fn return_preallocated_gpuva(&mut self, gpuva: GpuVaAlloc<GpuVmData<'drm>>) { 284*f2dc32d5SBoris Brezillon if let Some(slot) = self 285*f2dc32d5SBoris Brezillon .resources 286*f2dc32d5SBoris Brezillon .preallocated_gpuvas 287*f2dc32d5SBoris Brezillon .iter_mut() 288*f2dc32d5SBoris Brezillon .find(|slot| slot.is_none()) 289*f2dc32d5SBoris Brezillon { 290*f2dc32d5SBoris Brezillon *slot = Some(gpuva); 291*f2dc32d5SBoris Brezillon } 292*f2dc32d5SBoris Brezillon } 293*f2dc32d5SBoris Brezillon } 294*f2dc32d5SBoris Brezillon 295*f2dc32d5SBoris Brezillon impl Drop for PtUpdateContext<'_, '_> { 296*f2dc32d5SBoris Brezillon fn drop(&mut self) { 297*f2dc32d5SBoris Brezillon if let Err(e) = self.mmu.end_vm_update(self.as_data) { 298*f2dc32d5SBoris Brezillon dev_err!(self.dev, "Failed to end VM update {:?}", e); 299*f2dc32d5SBoris Brezillon } 300*f2dc32d5SBoris Brezillon 301*f2dc32d5SBoris Brezillon if let Err(e) = self.mmu.flush_vm(self.as_data) { 302*f2dc32d5SBoris Brezillon dev_err!(self.dev, "Failed to flush VM {:?}", e); 303*f2dc32d5SBoris Brezillon } 304*f2dc32d5SBoris Brezillon } 305*f2dc32d5SBoris Brezillon } 306*f2dc32d5SBoris Brezillon 307*f2dc32d5SBoris Brezillon /// Driver implementation for the GPUVM framework. 308*f2dc32d5SBoris Brezillon /// 309*f2dc32d5SBoris Brezillon /// Implements [`DriverGpuVm`] to provide VM operation callbacks (map, unmap, remap) 310*f2dc32d5SBoris Brezillon /// and associated types for buffer objects, virtual addresses, and contexts. 311*f2dc32d5SBoris Brezillon pub(crate) struct GpuVmData<'drm> { 312*f2dc32d5SBoris Brezillon _phantom: PhantomData<&'drm ()>, 313*f2dc32d5SBoris Brezillon } 314*f2dc32d5SBoris Brezillon 315*f2dc32d5SBoris Brezillon /// GPU virtual address space. 316*f2dc32d5SBoris Brezillon /// 317*f2dc32d5SBoris Brezillon /// Each VM can be mapped into a hardware address space slot. 318*f2dc32d5SBoris Brezillon #[pin_data] 319*f2dc32d5SBoris Brezillon pub(crate) struct Vm<'drm> { 320*f2dc32d5SBoris Brezillon /// Data referenced by an AS when the VM is active 321*f2dc32d5SBoris Brezillon as_data: Arc<VmAsData<'drm>>, 322*f2dc32d5SBoris Brezillon /// MMU manager. 323*f2dc32d5SBoris Brezillon mmu: Arc<Mmu<'drm>>, 324*f2dc32d5SBoris Brezillon /// Parent device used for DMA mapping and page-table operations. 325*f2dc32d5SBoris Brezillon dev: &'drm Device<Bound>, 326*f2dc32d5SBoris Brezillon /// DRM GPUVM core for managing virtual address space. 327*f2dc32d5SBoris Brezillon #[pin] 328*f2dc32d5SBoris Brezillon gpuvm_unique: Mutex<UniqueRefGpuVm<GpuVmData<'drm>>>, 329*f2dc32d5SBoris Brezillon /// Non-core part of the GPUVM. Can be used for stuff that doesn't modify the 330*f2dc32d5SBoris Brezillon /// internal mapping tree, like GpuVm::obtain() 331*f2dc32d5SBoris Brezillon gpuvm: ARef<GpuVm<GpuVmData<'drm>>>, 332*f2dc32d5SBoris Brezillon /// VA range for this VM. 333*f2dc32d5SBoris Brezillon va_range: Range<u64>, 334*f2dc32d5SBoris Brezillon } 335*f2dc32d5SBoris Brezillon 336*f2dc32d5SBoris Brezillon impl<'drm> Vm<'drm> { 337*f2dc32d5SBoris Brezillon /// Creates a new GPU virtual address space. 338*f2dc32d5SBoris Brezillon /// 339*f2dc32d5SBoris Brezillon /// The VM is initialized with a page table configured according to the GPU's 340*f2dc32d5SBoris Brezillon /// address translation capabilities and registered with the GPUVM framework. 341*f2dc32d5SBoris Brezillon pub(crate) fn new( 342*f2dc32d5SBoris Brezillon dev: &'drm Device<Bound>, 343*f2dc32d5SBoris Brezillon ddev: &TyrDrmDevice, 344*f2dc32d5SBoris Brezillon mmu: ArcBorrow<'_, Mmu<'drm>>, 345*f2dc32d5SBoris Brezillon gpu_info: &GpuInfo, 346*f2dc32d5SBoris Brezillon ) -> Result<Arc<Vm<'drm>>> { 347*f2dc32d5SBoris Brezillon let mmu_features = MMU_FEATURES::from_raw(gpu_info.mmu_features); 348*f2dc32d5SBoris Brezillon let va_bits = mmu_features.va_bits().get(); 349*f2dc32d5SBoris Brezillon let pa_bits = mmu_features.pa_bits().get(); 350*f2dc32d5SBoris Brezillon 351*f2dc32d5SBoris Brezillon let range = 0..(1u64 << va_bits); 352*f2dc32d5SBoris Brezillon let reserve_range = 0..0u64; 353*f2dc32d5SBoris Brezillon 354*f2dc32d5SBoris Brezillon // dummy_obj is used to initialize the GPUVM tree. 355*f2dc32d5SBoris Brezillon let dummy_obj = gem::new_dummy_object(ddev).inspect_err(|e| { 356*f2dc32d5SBoris Brezillon dev_err!(dev, "Failed to create dummy GEM object: {:?}", e); 357*f2dc32d5SBoris Brezillon })?; 358*f2dc32d5SBoris Brezillon 359*f2dc32d5SBoris Brezillon let gpuvm_unique = GpuVm::new::<Error, _>( 360*f2dc32d5SBoris Brezillon c"Tyr::GpuVm", 361*f2dc32d5SBoris Brezillon ddev, 362*f2dc32d5SBoris Brezillon &*dummy_obj, 363*f2dc32d5SBoris Brezillon range.clone(), 364*f2dc32d5SBoris Brezillon reserve_range, 365*f2dc32d5SBoris Brezillon GpuVmData::<'drm> { 366*f2dc32d5SBoris Brezillon _phantom: PhantomData::<&()>, 367*f2dc32d5SBoris Brezillon }, 368*f2dc32d5SBoris Brezillon ) 369*f2dc32d5SBoris Brezillon .inspect_err(|e| { 370*f2dc32d5SBoris Brezillon dev_err!(dev, "Failed to create GpuVm: {:?}", e); 371*f2dc32d5SBoris Brezillon })?; 372*f2dc32d5SBoris Brezillon let gpuvm = ARef::from(&*gpuvm_unique); 373*f2dc32d5SBoris Brezillon 374*f2dc32d5SBoris Brezillon let as_data = Arc::pin_init(VmAsData::new(&mmu, dev, va_bits, pa_bits), GFP_KERNEL)?; 375*f2dc32d5SBoris Brezillon 376*f2dc32d5SBoris Brezillon let vm = Arc::pin_init( 377*f2dc32d5SBoris Brezillon pin_init!(Self{ 378*f2dc32d5SBoris Brezillon as_data, 379*f2dc32d5SBoris Brezillon dev, 380*f2dc32d5SBoris Brezillon mmu: mmu.into(), 381*f2dc32d5SBoris Brezillon gpuvm, 382*f2dc32d5SBoris Brezillon gpuvm_unique <- new_mutex!(gpuvm_unique), 383*f2dc32d5SBoris Brezillon va_range: range, 384*f2dc32d5SBoris Brezillon }), 385*f2dc32d5SBoris Brezillon GFP_KERNEL, 386*f2dc32d5SBoris Brezillon )?; 387*f2dc32d5SBoris Brezillon 388*f2dc32d5SBoris Brezillon Ok(vm) 389*f2dc32d5SBoris Brezillon } 390*f2dc32d5SBoris Brezillon 391*f2dc32d5SBoris Brezillon /// Returns the parent device used by this VM for DMA mapping and page-table operations. 392*f2dc32d5SBoris Brezillon pub(crate) fn dev(&self) -> &'drm Device<Bound> { 393*f2dc32d5SBoris Brezillon self.dev 394*f2dc32d5SBoris Brezillon } 395*f2dc32d5SBoris Brezillon 396*f2dc32d5SBoris Brezillon /// Activate the VM in a hardware address space slot. 397*f2dc32d5SBoris Brezillon pub(crate) fn activate(&self) -> Result { 398*f2dc32d5SBoris Brezillon self.mmu 399*f2dc32d5SBoris Brezillon .activate_vm(self.as_data.as_arc_borrow()) 400*f2dc32d5SBoris Brezillon .inspect_err(|e| { 401*f2dc32d5SBoris Brezillon dev_err!(self.dev, "Failed to activate VM: {:?}", e); 402*f2dc32d5SBoris Brezillon }) 403*f2dc32d5SBoris Brezillon } 404*f2dc32d5SBoris Brezillon 405*f2dc32d5SBoris Brezillon /// Deactivate the VM by evicting it from its address space slot. 406*f2dc32d5SBoris Brezillon fn deactivate(&self) -> Result { 407*f2dc32d5SBoris Brezillon self.mmu.deactivate_vm(&self.as_data).inspect_err(|e| { 408*f2dc32d5SBoris Brezillon dev_err!(self.dev, "Failed to deactivate VM: {:?}", e); 409*f2dc32d5SBoris Brezillon }) 410*f2dc32d5SBoris Brezillon } 411*f2dc32d5SBoris Brezillon 412*f2dc32d5SBoris Brezillon /// Kills the VM by deactivating it and unmapping all regions. 413*f2dc32d5SBoris Brezillon pub(crate) fn kill(&self) { 414*f2dc32d5SBoris Brezillon // TODO: Turn the VM into a state where it can't be used. 415*f2dc32d5SBoris Brezillon let _ = self.deactivate(); 416*f2dc32d5SBoris Brezillon let _ = self 417*f2dc32d5SBoris Brezillon .unmap_range(self.va_range.start, self.va_range.end - self.va_range.start) 418*f2dc32d5SBoris Brezillon .inspect_err(|e| { 419*f2dc32d5SBoris Brezillon dev_err!(self.dev, "Failed to unmap range during deactivate: {:?}", e); 420*f2dc32d5SBoris Brezillon }); 421*f2dc32d5SBoris Brezillon } 422*f2dc32d5SBoris Brezillon 423*f2dc32d5SBoris Brezillon /// Executes a virtual memory operation. 424*f2dc32d5SBoris Brezillon /// 425*f2dc32d5SBoris Brezillon /// This handles both map and unmap operations by coordinating between the 426*f2dc32d5SBoris Brezillon /// GPUVM framework and the hardware page table. 427*f2dc32d5SBoris Brezillon fn exec_op<'a>( 428*f2dc32d5SBoris Brezillon &self, 429*f2dc32d5SBoris Brezillon gpuvm_unique: &mut UniqueRefGpuVm<GpuVmData<'drm>>, 430*f2dc32d5SBoris Brezillon req: VmOpRequest<'drm>, 431*f2dc32d5SBoris Brezillon resources: &'a mut VmOpResources<'drm>, 432*f2dc32d5SBoris Brezillon ) -> Result { 433*f2dc32d5SBoris Brezillon let pt = &self.as_data.page_table; 434*f2dc32d5SBoris Brezillon 435*f2dc32d5SBoris Brezillon match req.op_type { 436*f2dc32d5SBoris Brezillon VmOpType::Map(args) => { 437*f2dc32d5SBoris Brezillon let mut pt_upd = PtUpdateContext::new( 438*f2dc32d5SBoris Brezillon self.dev, 439*f2dc32d5SBoris Brezillon pt, 440*f2dc32d5SBoris Brezillon &self.mmu, 441*f2dc32d5SBoris Brezillon &self.as_data, 442*f2dc32d5SBoris Brezillon req.region, 443*f2dc32d5SBoris Brezillon PtOpType::Map(PtMapArgs { 444*f2dc32d5SBoris Brezillon prot: args.flags.to_prot(), 445*f2dc32d5SBoris Brezillon }), 446*f2dc32d5SBoris Brezillon resources, 447*f2dc32d5SBoris Brezillon )?; 448*f2dc32d5SBoris Brezillon 449*f2dc32d5SBoris Brezillon gpuvm_unique.sm_map(OpMapRequest { 450*f2dc32d5SBoris Brezillon addr: pt_upd.region.start, 451*f2dc32d5SBoris Brezillon range: pt_upd.region.end - pt_upd.region.start, 452*f2dc32d5SBoris Brezillon gem_offset: args.bo_offset, 453*f2dc32d5SBoris Brezillon vm_bo: &args.vm_bo, 454*f2dc32d5SBoris Brezillon context: &mut pt_upd, 455*f2dc32d5SBoris Brezillon }) 456*f2dc32d5SBoris Brezillon //PtUpdateContext drops here flushing the page table 457*f2dc32d5SBoris Brezillon } 458*f2dc32d5SBoris Brezillon VmOpType::Unmap => { 459*f2dc32d5SBoris Brezillon let mut pt_upd = PtUpdateContext::new( 460*f2dc32d5SBoris Brezillon self.dev, 461*f2dc32d5SBoris Brezillon pt, 462*f2dc32d5SBoris Brezillon &self.mmu, 463*f2dc32d5SBoris Brezillon &self.as_data, 464*f2dc32d5SBoris Brezillon req.region, 465*f2dc32d5SBoris Brezillon PtOpType::Unmap, 466*f2dc32d5SBoris Brezillon resources, 467*f2dc32d5SBoris Brezillon )?; 468*f2dc32d5SBoris Brezillon 469*f2dc32d5SBoris Brezillon gpuvm_unique.sm_unmap( 470*f2dc32d5SBoris Brezillon pt_upd.region.start, 471*f2dc32d5SBoris Brezillon pt_upd.region.end - pt_upd.region.start, 472*f2dc32d5SBoris Brezillon &mut pt_upd, 473*f2dc32d5SBoris Brezillon ) 474*f2dc32d5SBoris Brezillon //PtUpdateContext drops here flushing the page table 475*f2dc32d5SBoris Brezillon } 476*f2dc32d5SBoris Brezillon } 477*f2dc32d5SBoris Brezillon } 478*f2dc32d5SBoris Brezillon 479*f2dc32d5SBoris Brezillon /// Maps a GEM buffer object range into the VM at the specified virtual address. 480*f2dc32d5SBoris Brezillon /// 481*f2dc32d5SBoris Brezillon /// This creates a mapping from GPU virtual address `va` to the physical pages 482*f2dc32d5SBoris Brezillon /// backing the GEM object, starting at `bo_offset` bytes into the object and 483*f2dc32d5SBoris Brezillon /// spanning `map_size` bytes. The mapping respects the access permissions and 484*f2dc32d5SBoris Brezillon /// caching behavior specified in `flags`. 485*f2dc32d5SBoris Brezillon pub(crate) fn map_bo_range( 486*f2dc32d5SBoris Brezillon &self, 487*f2dc32d5SBoris Brezillon bo: &Bo, 488*f2dc32d5SBoris Brezillon bo_offset: u64, 489*f2dc32d5SBoris Brezillon map_size: u64, 490*f2dc32d5SBoris Brezillon va: u64, 491*f2dc32d5SBoris Brezillon flags: VmMapFlags, 492*f2dc32d5SBoris Brezillon ) -> Result { 493*f2dc32d5SBoris Brezillon if map_size == 0 494*f2dc32d5SBoris Brezillon || va % SZ_4K as u64 != 0 495*f2dc32d5SBoris Brezillon || bo_offset % SZ_4K as u64 != 0 496*f2dc32d5SBoris Brezillon || map_size % SZ_4K as u64 != 0 497*f2dc32d5SBoris Brezillon { 498*f2dc32d5SBoris Brezillon return Err(EINVAL); 499*f2dc32d5SBoris Brezillon } 500*f2dc32d5SBoris Brezillon 501*f2dc32d5SBoris Brezillon let bo_size = u64::try_from(bo.size()).map_err(|_| EOVERFLOW)?; 502*f2dc32d5SBoris Brezillon let bo_end = bo_offset.checked_add(map_size).ok_or(EINVAL)?; 503*f2dc32d5SBoris Brezillon 504*f2dc32d5SBoris Brezillon if bo_end > bo_size { 505*f2dc32d5SBoris Brezillon dev_err!( 506*f2dc32d5SBoris Brezillon self.dev, 507*f2dc32d5SBoris Brezillon "BO mapping range {:#x}..{:#x} exceeds BO size {:#x}", 508*f2dc32d5SBoris Brezillon bo_offset, 509*f2dc32d5SBoris Brezillon bo_end, 510*f2dc32d5SBoris Brezillon bo_size 511*f2dc32d5SBoris Brezillon ); 512*f2dc32d5SBoris Brezillon return Err(EINVAL); 513*f2dc32d5SBoris Brezillon } 514*f2dc32d5SBoris Brezillon 515*f2dc32d5SBoris Brezillon let va_end: u64 = va.checked_add(map_size).ok_or(EINVAL)?; 516*f2dc32d5SBoris Brezillon 517*f2dc32d5SBoris Brezillon let req = VmOpRequest { 518*f2dc32d5SBoris Brezillon op_type: VmOpType::Map(VmMapArgs { 519*f2dc32d5SBoris Brezillon vm_bo: self.gpuvm.obtain(bo, ())?, 520*f2dc32d5SBoris Brezillon flags, 521*f2dc32d5SBoris Brezillon bo_offset, 522*f2dc32d5SBoris Brezillon }), 523*f2dc32d5SBoris Brezillon region: va..va_end, 524*f2dc32d5SBoris Brezillon }; 525*f2dc32d5SBoris Brezillon let mut resources = VmOpResources { 526*f2dc32d5SBoris Brezillon preallocated_gpuvas: [ 527*f2dc32d5SBoris Brezillon Some(GpuVaAlloc::<GpuVmData<'drm>>::new(GFP_KERNEL)?), 528*f2dc32d5SBoris Brezillon Some(GpuVaAlloc::<GpuVmData<'drm>>::new(GFP_KERNEL)?), 529*f2dc32d5SBoris Brezillon Some(GpuVaAlloc::<GpuVmData<'drm>>::new(GFP_KERNEL)?), 530*f2dc32d5SBoris Brezillon ], 531*f2dc32d5SBoris Brezillon }; 532*f2dc32d5SBoris Brezillon let result = { 533*f2dc32d5SBoris Brezillon let mut gpuvm_unique = self.gpuvm_unique.lock(); 534*f2dc32d5SBoris Brezillon self.exec_op(gpuvm_unique.as_mut().get_mut(), req, &mut resources) 535*f2dc32d5SBoris Brezillon }; 536*f2dc32d5SBoris Brezillon // We flush the defer cleanup list now. Things will be different in 537*f2dc32d5SBoris Brezillon // the asynchronous VM_BIND path, where we want the cleanup to 538*f2dc32d5SBoris Brezillon // happen outside the DMA signalling path. 539*f2dc32d5SBoris Brezillon self.gpuvm.deferred_cleanup(); 540*f2dc32d5SBoris Brezillon result 541*f2dc32d5SBoris Brezillon } 542*f2dc32d5SBoris Brezillon 543*f2dc32d5SBoris Brezillon /// Unmaps a virtual address range from the VM. 544*f2dc32d5SBoris Brezillon /// 545*f2dc32d5SBoris Brezillon /// This removes any existing mappings in the specified range, freeing the 546*f2dc32d5SBoris Brezillon /// virtual address space for reuse. 547*f2dc32d5SBoris Brezillon pub(crate) fn unmap_range(&self, va: u64, size: u64) -> Result { 548*f2dc32d5SBoris Brezillon if size == 0 || va % SZ_4K as u64 != 0 || size % SZ_4K as u64 != 0 { 549*f2dc32d5SBoris Brezillon return Err(EINVAL); 550*f2dc32d5SBoris Brezillon } 551*f2dc32d5SBoris Brezillon 552*f2dc32d5SBoris Brezillon let end = va.checked_add(size).ok_or(EINVAL)?; 553*f2dc32d5SBoris Brezillon 554*f2dc32d5SBoris Brezillon if va < self.va_range.start || end > self.va_range.end { 555*f2dc32d5SBoris Brezillon dev_err!( 556*f2dc32d5SBoris Brezillon self.dev, 557*f2dc32d5SBoris Brezillon "Unmap range {:#x}..{:#x} exceeds VM range {:#x}..{:#x}", 558*f2dc32d5SBoris Brezillon va, 559*f2dc32d5SBoris Brezillon end, 560*f2dc32d5SBoris Brezillon self.va_range.start, 561*f2dc32d5SBoris Brezillon self.va_range.end 562*f2dc32d5SBoris Brezillon ); 563*f2dc32d5SBoris Brezillon return Err(EINVAL); 564*f2dc32d5SBoris Brezillon } 565*f2dc32d5SBoris Brezillon 566*f2dc32d5SBoris Brezillon let req = VmOpRequest { 567*f2dc32d5SBoris Brezillon op_type: VmOpType::Unmap, 568*f2dc32d5SBoris Brezillon region: va..end, 569*f2dc32d5SBoris Brezillon }; 570*f2dc32d5SBoris Brezillon 571*f2dc32d5SBoris Brezillon let full_vm = va == self.va_range.start && end == self.va_range.end; 572*f2dc32d5SBoris Brezillon 573*f2dc32d5SBoris Brezillon let mut resources = VmOpResources { 574*f2dc32d5SBoris Brezillon preallocated_gpuvas: if full_vm { 575*f2dc32d5SBoris Brezillon // Unmapping the entire VM cannot split an existing mapping, 576*f2dc32d5SBoris Brezillon // so no GPUVA objects are needed for remap operations. 577*f2dc32d5SBoris Brezillon [None, None, None] 578*f2dc32d5SBoris Brezillon } else { 579*f2dc32d5SBoris Brezillon [ 580*f2dc32d5SBoris Brezillon Some(GpuVaAlloc::<GpuVmData<'drm>>::new(GFP_KERNEL)?), 581*f2dc32d5SBoris Brezillon Some(GpuVaAlloc::<GpuVmData<'drm>>::new(GFP_KERNEL)?), 582*f2dc32d5SBoris Brezillon Some(GpuVaAlloc::<GpuVmData<'drm>>::new(GFP_KERNEL)?), 583*f2dc32d5SBoris Brezillon ] 584*f2dc32d5SBoris Brezillon }, 585*f2dc32d5SBoris Brezillon }; 586*f2dc32d5SBoris Brezillon let result = { 587*f2dc32d5SBoris Brezillon let mut gpuvm_unique = self.gpuvm_unique.lock(); 588*f2dc32d5SBoris Brezillon self.exec_op(gpuvm_unique.as_mut().get_mut(), req, &mut resources) 589*f2dc32d5SBoris Brezillon }; 590*f2dc32d5SBoris Brezillon // We flush the defer cleanup list now. Things will be different in 591*f2dc32d5SBoris Brezillon // the asynchronous VM_BIND path, where we want the cleanup to 592*f2dc32d5SBoris Brezillon // happen outside the DMA signalling path. 593*f2dc32d5SBoris Brezillon self.gpuvm.deferred_cleanup(); 594*f2dc32d5SBoris Brezillon result 595*f2dc32d5SBoris Brezillon } 596*f2dc32d5SBoris Brezillon } 597*f2dc32d5SBoris Brezillon 598*f2dc32d5SBoris Brezillon impl<'drm> DriverGpuVm for GpuVmData<'drm> { 599*f2dc32d5SBoris Brezillon type Driver = TyrDrmDriver; 600*f2dc32d5SBoris Brezillon type Object = Bo; 601*f2dc32d5SBoris Brezillon type VmBoData = (); 602*f2dc32d5SBoris Brezillon type VaData = (); 603*f2dc32d5SBoris Brezillon type SmContext<'ctx> 604*f2dc32d5SBoris Brezillon = PtUpdateContext<'ctx, 'drm> 605*f2dc32d5SBoris Brezillon where 606*f2dc32d5SBoris Brezillon Self: 'ctx; 607*f2dc32d5SBoris Brezillon 608*f2dc32d5SBoris Brezillon /// Create a new mapping. 609*f2dc32d5SBoris Brezillon fn sm_step_map<'op>( 610*f2dc32d5SBoris Brezillon &mut self, 611*f2dc32d5SBoris Brezillon op: OpMap<'op, Self>, 612*f2dc32d5SBoris Brezillon context: &mut Self::SmContext<'_>, 613*f2dc32d5SBoris Brezillon ) -> Result<OpMapped<'op, Self>, Error> { 614*f2dc32d5SBoris Brezillon let start_iova = op.addr(); 615*f2dc32d5SBoris Brezillon let mut iova = start_iova; 616*f2dc32d5SBoris Brezillon let mut bytes_left_to_map = op.length(); 617*f2dc32d5SBoris Brezillon let mut gem_offset = op.gem_offset(); 618*f2dc32d5SBoris Brezillon 619*f2dc32d5SBoris Brezillon // Make sure that the end of the requested GEM range doesn't run past the 620*f2dc32d5SBoris Brezillon // end of the GEM buffer itself. 621*f2dc32d5SBoris Brezillon let gem_range_end = op.gem_offset().checked_add(op.length()).ok_or(EINVAL)?; 622*f2dc32d5SBoris Brezillon 623*f2dc32d5SBoris Brezillon if gem_range_end > op.obj().size() as u64 { 624*f2dc32d5SBoris Brezillon dev_err!( 625*f2dc32d5SBoris Brezillon context.dev, 626*f2dc32d5SBoris Brezillon "Requested GEM range ends at {} which is beyond the GEM buffer size {}", 627*f2dc32d5SBoris Brezillon gem_range_end, 628*f2dc32d5SBoris Brezillon op.obj().size() 629*f2dc32d5SBoris Brezillon ); 630*f2dc32d5SBoris Brezillon return Err(EINVAL); 631*f2dc32d5SBoris Brezillon } 632*f2dc32d5SBoris Brezillon 633*f2dc32d5SBoris Brezillon let sgt = op.obj().sg_table(context.dev).inspect_err(|e| { 634*f2dc32d5SBoris Brezillon dev_err!(context.dev, "Failed to get sg_table: {:?}", e); 635*f2dc32d5SBoris Brezillon })?; 636*f2dc32d5SBoris Brezillon let prot = match &context.op_type { 637*f2dc32d5SBoris Brezillon PtOpType::Map(args) => args.prot, 638*f2dc32d5SBoris Brezillon _ => { 639*f2dc32d5SBoris Brezillon return Err(EINVAL); 640*f2dc32d5SBoris Brezillon } 641*f2dc32d5SBoris Brezillon }; 642*f2dc32d5SBoris Brezillon 643*f2dc32d5SBoris Brezillon for sgt_entry in sgt.iter() { 644*f2dc32d5SBoris Brezillon // Expressly convert to u64 to work with arm 32-bit builds. 645*f2dc32d5SBoris Brezillon #[allow(clippy::useless_conversion)] 646*f2dc32d5SBoris Brezillon let mut paddr = u64::from(sgt_entry.dma_address()); 647*f2dc32d5SBoris Brezillon #[allow(clippy::useless_conversion)] 648*f2dc32d5SBoris Brezillon let mut sgt_entry_length = u64::from(sgt_entry.dma_len()); 649*f2dc32d5SBoris Brezillon 650*f2dc32d5SBoris Brezillon if bytes_left_to_map == 0 { 651*f2dc32d5SBoris Brezillon break; 652*f2dc32d5SBoris Brezillon } 653*f2dc32d5SBoris Brezillon 654*f2dc32d5SBoris Brezillon if gem_offset > 0 { 655*f2dc32d5SBoris Brezillon // Skip the entire SGT entry if the gem_offset exceeds its length. 656*f2dc32d5SBoris Brezillon let skip = u64::min(sgt_entry_length, gem_offset); 657*f2dc32d5SBoris Brezillon paddr += skip; 658*f2dc32d5SBoris Brezillon sgt_entry_length -= skip; 659*f2dc32d5SBoris Brezillon gem_offset -= skip; 660*f2dc32d5SBoris Brezillon } 661*f2dc32d5SBoris Brezillon 662*f2dc32d5SBoris Brezillon if sgt_entry_length == 0 { 663*f2dc32d5SBoris Brezillon continue; 664*f2dc32d5SBoris Brezillon } 665*f2dc32d5SBoris Brezillon 666*f2dc32d5SBoris Brezillon let len = u64::min(sgt_entry_length, bytes_left_to_map); 667*f2dc32d5SBoris Brezillon 668*f2dc32d5SBoris Brezillon let segment_mapped = match pt_map(context.dev, context.pt, iova, paddr, len, prot) { 669*f2dc32d5SBoris Brezillon Ok(segment_mapped) => segment_mapped, 670*f2dc32d5SBoris Brezillon Err(e) => { 671*f2dc32d5SBoris Brezillon // clean up any successful mappings from previous SGT entries. 672*f2dc32d5SBoris Brezillon let total_mapped = iova - start_iova; 673*f2dc32d5SBoris Brezillon if total_mapped > 0 { 674*f2dc32d5SBoris Brezillon let _ = pt_unmap( 675*f2dc32d5SBoris Brezillon context.dev, 676*f2dc32d5SBoris Brezillon context.pt, 677*f2dc32d5SBoris Brezillon start_iova..(start_iova + total_mapped), 678*f2dc32d5SBoris Brezillon ); 679*f2dc32d5SBoris Brezillon } 680*f2dc32d5SBoris Brezillon return Err(e); 681*f2dc32d5SBoris Brezillon } 682*f2dc32d5SBoris Brezillon }; 683*f2dc32d5SBoris Brezillon 684*f2dc32d5SBoris Brezillon bytes_left_to_map -= segment_mapped; 685*f2dc32d5SBoris Brezillon iova += segment_mapped; 686*f2dc32d5SBoris Brezillon } 687*f2dc32d5SBoris Brezillon 688*f2dc32d5SBoris Brezillon if bytes_left_to_map != 0 { 689*f2dc32d5SBoris Brezillon let total_mapped = iova - start_iova; 690*f2dc32d5SBoris Brezillon 691*f2dc32d5SBoris Brezillon if total_mapped > 0 { 692*f2dc32d5SBoris Brezillon let _ = pt_unmap(context.dev, context.pt, start_iova..iova); 693*f2dc32d5SBoris Brezillon } 694*f2dc32d5SBoris Brezillon 695*f2dc32d5SBoris Brezillon dev_err!( 696*f2dc32d5SBoris Brezillon context.dev, 697*f2dc32d5SBoris Brezillon "SG table is too small for requested mapping: {} bytes remain", 698*f2dc32d5SBoris Brezillon bytes_left_to_map 699*f2dc32d5SBoris Brezillon ); 700*f2dc32d5SBoris Brezillon 701*f2dc32d5SBoris Brezillon return Err(EINVAL); 702*f2dc32d5SBoris Brezillon } 703*f2dc32d5SBoris Brezillon 704*f2dc32d5SBoris Brezillon let gpuva = context.preallocated_gpuva()?; 705*f2dc32d5SBoris Brezillon let op = op.insert(gpuva, pin_init::init_zeroed()); 706*f2dc32d5SBoris Brezillon 707*f2dc32d5SBoris Brezillon Ok(op) 708*f2dc32d5SBoris Brezillon } 709*f2dc32d5SBoris Brezillon 710*f2dc32d5SBoris Brezillon /// Indicates that an existing mapping should be removed. 711*f2dc32d5SBoris Brezillon fn sm_step_unmap<'op>( 712*f2dc32d5SBoris Brezillon &mut self, 713*f2dc32d5SBoris Brezillon op: OpUnmap<'op, Self>, 714*f2dc32d5SBoris Brezillon context: &mut Self::SmContext<'_>, 715*f2dc32d5SBoris Brezillon ) -> Result<OpUnmapped<'op, Self>, Error> { 716*f2dc32d5SBoris Brezillon let start_iova = op.va().addr(); 717*f2dc32d5SBoris Brezillon let length = op.va().length(); 718*f2dc32d5SBoris Brezillon 719*f2dc32d5SBoris Brezillon let region = start_iova..(start_iova + length); 720*f2dc32d5SBoris Brezillon pt_unmap(context.dev, context.pt, region.clone()).inspect_err(|e| { 721*f2dc32d5SBoris Brezillon dev_err!( 722*f2dc32d5SBoris Brezillon context.dev, 723*f2dc32d5SBoris Brezillon "Failed to unmap region {:#x}..{:#x}: {:?}", 724*f2dc32d5SBoris Brezillon region.start, 725*f2dc32d5SBoris Brezillon region.end, 726*f2dc32d5SBoris Brezillon e 727*f2dc32d5SBoris Brezillon ); 728*f2dc32d5SBoris Brezillon })?; 729*f2dc32d5SBoris Brezillon 730*f2dc32d5SBoris Brezillon let (op_unmapped, _va_removed) = op.remove(); 731*f2dc32d5SBoris Brezillon 732*f2dc32d5SBoris Brezillon Ok(op_unmapped) 733*f2dc32d5SBoris Brezillon } 734*f2dc32d5SBoris Brezillon 735*f2dc32d5SBoris Brezillon /// Split up an existing mapping. 736*f2dc32d5SBoris Brezillon fn sm_step_remap<'op>( 737*f2dc32d5SBoris Brezillon &mut self, 738*f2dc32d5SBoris Brezillon op: OpRemap<'op, Self>, 739*f2dc32d5SBoris Brezillon context: &mut Self::SmContext<'_>, 740*f2dc32d5SBoris Brezillon ) -> Result<OpRemapped<'op, Self>, Error> { 741*f2dc32d5SBoris Brezillon let unmap_start = if let Some(prev) = op.prev() { 742*f2dc32d5SBoris Brezillon prev.addr() + prev.length() 743*f2dc32d5SBoris Brezillon } else { 744*f2dc32d5SBoris Brezillon op.va_to_unmap().addr() 745*f2dc32d5SBoris Brezillon }; 746*f2dc32d5SBoris Brezillon 747*f2dc32d5SBoris Brezillon let unmap_end = if let Some(next) = op.next() { 748*f2dc32d5SBoris Brezillon next.addr() 749*f2dc32d5SBoris Brezillon } else { 750*f2dc32d5SBoris Brezillon op.va_to_unmap().addr() + op.va_to_unmap().length() 751*f2dc32d5SBoris Brezillon }; 752*f2dc32d5SBoris Brezillon 753*f2dc32d5SBoris Brezillon let unmap_length = unmap_end - unmap_start; 754*f2dc32d5SBoris Brezillon 755*f2dc32d5SBoris Brezillon if unmap_length > 0 { 756*f2dc32d5SBoris Brezillon let region = unmap_start..(unmap_start + unmap_length); 757*f2dc32d5SBoris Brezillon pt_unmap(context.dev, context.pt, region.clone()).inspect_err(|e| { 758*f2dc32d5SBoris Brezillon dev_err!( 759*f2dc32d5SBoris Brezillon context.dev, 760*f2dc32d5SBoris Brezillon "Failed to unmap remap region {:#x}..{:#x}: {:?}", 761*f2dc32d5SBoris Brezillon region.start, 762*f2dc32d5SBoris Brezillon region.end, 763*f2dc32d5SBoris Brezillon e 764*f2dc32d5SBoris Brezillon ); 765*f2dc32d5SBoris Brezillon })?; 766*f2dc32d5SBoris Brezillon } 767*f2dc32d5SBoris Brezillon 768*f2dc32d5SBoris Brezillon let prev_va = context.preallocated_gpuva()?; 769*f2dc32d5SBoris Brezillon let next_va = context.preallocated_gpuva()?; 770*f2dc32d5SBoris Brezillon 771*f2dc32d5SBoris Brezillon let (op_remapped, remap_ret) = op.remap( 772*f2dc32d5SBoris Brezillon [prev_va, next_va], 773*f2dc32d5SBoris Brezillon pin_init::init_zeroed(), 774*f2dc32d5SBoris Brezillon pin_init::init_zeroed(), 775*f2dc32d5SBoris Brezillon ); 776*f2dc32d5SBoris Brezillon 777*f2dc32d5SBoris Brezillon if let Some(unused_va) = remap_ret.unused_va { 778*f2dc32d5SBoris Brezillon context.return_preallocated_gpuva(unused_va); 779*f2dc32d5SBoris Brezillon } 780*f2dc32d5SBoris Brezillon 781*f2dc32d5SBoris Brezillon Ok(op_remapped) 782*f2dc32d5SBoris Brezillon } 783*f2dc32d5SBoris Brezillon } 784*f2dc32d5SBoris Brezillon 785*f2dc32d5SBoris Brezillon /// This function selects the largest supported block size (currently 4KB or 2MB) 786*f2dc32d5SBoris Brezillon /// that can be used for a mapping at the given address and size, respecting alignment constraints. 787*f2dc32d5SBoris Brezillon /// 788*f2dc32d5SBoris Brezillon /// We can map multiple pages at once but we can't exceed the size of the 789*f2dc32d5SBoris Brezillon /// table entry itself. So, if mapping 4KB pages, figure out how many pages 790*f2dc32d5SBoris Brezillon /// can be mapped before we hit the 2MB boundary. Or, if mapping 2MB pages, 791*f2dc32d5SBoris Brezillon /// figure out how many pages can be mapped before hitting the 1GB boundary 792*f2dc32d5SBoris Brezillon /// Returns the page size (4KB or 2MB) and the number of pages that can be mapped at that size. 793*f2dc32d5SBoris Brezillon fn get_pgsize(addr: u64, size: u64) -> (u64, u64) { 794*f2dc32d5SBoris Brezillon // Get the distance to the next boundary of 2MB block 795*f2dc32d5SBoris Brezillon let blk_offset_2m = addr.wrapping_neg() % (SZ_2M as u64); 796*f2dc32d5SBoris Brezillon 797*f2dc32d5SBoris Brezillon // Use 4K blocks if the address is not 2MB aligned, or we have less than 2MB to map 798*f2dc32d5SBoris Brezillon if blk_offset_2m != 0 || size < SZ_2M as u64 { 799*f2dc32d5SBoris Brezillon let pgcount = if blk_offset_2m == 0 { 800*f2dc32d5SBoris Brezillon size / SZ_4K as u64 801*f2dc32d5SBoris Brezillon } else { 802*f2dc32d5SBoris Brezillon u64::min(blk_offset_2m, size) / SZ_4K as u64 803*f2dc32d5SBoris Brezillon }; 804*f2dc32d5SBoris Brezillon return (SZ_4K as u64, pgcount); 805*f2dc32d5SBoris Brezillon } 806*f2dc32d5SBoris Brezillon 807*f2dc32d5SBoris Brezillon let blk_offset_1g = addr.wrapping_neg() % (SZ_1G as u64); 808*f2dc32d5SBoris Brezillon let blk_offset = if blk_offset_1g == 0 { 809*f2dc32d5SBoris Brezillon SZ_1G as u64 810*f2dc32d5SBoris Brezillon } else { 811*f2dc32d5SBoris Brezillon blk_offset_1g 812*f2dc32d5SBoris Brezillon }; 813*f2dc32d5SBoris Brezillon let pgcount = u64::min(blk_offset, size) / SZ_2M as u64; 814*f2dc32d5SBoris Brezillon 815*f2dc32d5SBoris Brezillon (SZ_2M as u64, pgcount) 816*f2dc32d5SBoris Brezillon } 817*f2dc32d5SBoris Brezillon 818*f2dc32d5SBoris Brezillon /// Maps a physical address range into the page table at the specified virtual address. 819*f2dc32d5SBoris Brezillon /// 820*f2dc32d5SBoris Brezillon /// This function maps `len` bytes of physical memory starting at `paddr` to the 821*f2dc32d5SBoris Brezillon /// virtual address `iova`, using the protection flags specified in `prot`. It 822*f2dc32d5SBoris Brezillon /// automatically selects optimal page sizes to minimize page table overhead. 823*f2dc32d5SBoris Brezillon /// 824*f2dc32d5SBoris Brezillon /// If the mapping fails partway through, all successfully mapped pages are 825*f2dc32d5SBoris Brezillon /// unmapped before returning an error. 826*f2dc32d5SBoris Brezillon /// 827*f2dc32d5SBoris Brezillon /// Returns the number of bytes successfully mapped. 828*f2dc32d5SBoris Brezillon fn pt_map( 829*f2dc32d5SBoris Brezillon dev: &Device, 830*f2dc32d5SBoris Brezillon pt: &IoPageTable<'_, ARM64LPAES1>, 831*f2dc32d5SBoris Brezillon iova: u64, 832*f2dc32d5SBoris Brezillon paddr: u64, 833*f2dc32d5SBoris Brezillon len: u64, 834*f2dc32d5SBoris Brezillon prot: u32, 835*f2dc32d5SBoris Brezillon ) -> Result<u64> { 836*f2dc32d5SBoris Brezillon let mut segment_mapped = 0u64; 837*f2dc32d5SBoris Brezillon while segment_mapped < len { 838*f2dc32d5SBoris Brezillon let remaining = len - segment_mapped; 839*f2dc32d5SBoris Brezillon let curr_iova = iova + segment_mapped; 840*f2dc32d5SBoris Brezillon let curr_paddr = paddr + segment_mapped; 841*f2dc32d5SBoris Brezillon 842*f2dc32d5SBoris Brezillon let (pgsize, pgcount) = get_pgsize(curr_iova | curr_paddr, remaining); 843*f2dc32d5SBoris Brezillon 844*f2dc32d5SBoris Brezillon // On 32-bit systems, usize is only 32 bits, so check that 845*f2dc32d5SBoris Brezillon // the iova can be converted without truncation. 846*f2dc32d5SBoris Brezillon let curr_iova = match usize::try_from(curr_iova) { 847*f2dc32d5SBoris Brezillon Ok(curr_iova) => curr_iova, 848*f2dc32d5SBoris Brezillon Err(_) => { 849*f2dc32d5SBoris Brezillon dev_err!( 850*f2dc32d5SBoris Brezillon dev, 851*f2dc32d5SBoris Brezillon "curr_iova {:#x} cannot be represented as usize (max {:#x})", 852*f2dc32d5SBoris Brezillon curr_iova, 853*f2dc32d5SBoris Brezillon usize::MAX 854*f2dc32d5SBoris Brezillon ); 855*f2dc32d5SBoris Brezillon 856*f2dc32d5SBoris Brezillon if segment_mapped > 0 { 857*f2dc32d5SBoris Brezillon let _ = pt_unmap(dev, pt, iova..(iova + segment_mapped)); 858*f2dc32d5SBoris Brezillon } 859*f2dc32d5SBoris Brezillon 860*f2dc32d5SBoris Brezillon return Err(EOVERFLOW); 861*f2dc32d5SBoris Brezillon } 862*f2dc32d5SBoris Brezillon }; 863*f2dc32d5SBoris Brezillon 864*f2dc32d5SBoris Brezillon // SAFETY: 865*f2dc32d5SBoris Brezillon // No other io-pgtable operation can currently access this range because Tyr holds 866*f2dc32d5SBoris Brezillon // the gpuvm_unique mutex for the entire sm_map() operation. 867*f2dc32d5SBoris Brezillon // The addresses being mapped won't overlap any existing mappings in this 868*f2dc32d5SBoris Brezillon // page table because drm_gpuvm_sm_map() checks each requested mapping and either unmaps 869*f2dc32d5SBoris Brezillon // or remaps any overlap before creating the new mapping. 870*f2dc32d5SBoris Brezillon let (mapped, result) = unsafe { 871*f2dc32d5SBoris Brezillon pt.map_pages( 872*f2dc32d5SBoris Brezillon curr_iova, 873*f2dc32d5SBoris Brezillon curr_paddr as PhysAddr, 874*f2dc32d5SBoris Brezillon pgsize as usize, 875*f2dc32d5SBoris Brezillon pgcount as usize, 876*f2dc32d5SBoris Brezillon prot, 877*f2dc32d5SBoris Brezillon GFP_KERNEL, 878*f2dc32d5SBoris Brezillon ) 879*f2dc32d5SBoris Brezillon }; 880*f2dc32d5SBoris Brezillon 881*f2dc32d5SBoris Brezillon if let Err(e) = result { 882*f2dc32d5SBoris Brezillon // If map_pages fails, mapped will be zero because the ARM LPAE backend 883*f2dc32d5SBoris Brezillon // only updates the mapped value after the entire request succeeds. 884*f2dc32d5SBoris Brezillon dev_err!(dev, "pt.map_pages failed at iova {:#x}: {:?}", curr_iova, e); 885*f2dc32d5SBoris Brezillon if segment_mapped > 0 { 886*f2dc32d5SBoris Brezillon let _ = pt_unmap(dev, pt, iova..(iova + segment_mapped)); 887*f2dc32d5SBoris Brezillon } 888*f2dc32d5SBoris Brezillon return Err(e); 889*f2dc32d5SBoris Brezillon } 890*f2dc32d5SBoris Brezillon 891*f2dc32d5SBoris Brezillon if mapped == 0 { 892*f2dc32d5SBoris Brezillon dev_err!(dev, "Failed to map any pages at iova {:#x}", curr_iova); 893*f2dc32d5SBoris Brezillon if segment_mapped > 0 { 894*f2dc32d5SBoris Brezillon let _ = pt_unmap(dev, pt, iova..(iova + segment_mapped)); 895*f2dc32d5SBoris Brezillon } 896*f2dc32d5SBoris Brezillon return Err(ENOMEM); 897*f2dc32d5SBoris Brezillon } 898*f2dc32d5SBoris Brezillon 899*f2dc32d5SBoris Brezillon segment_mapped += mapped as u64; 900*f2dc32d5SBoris Brezillon } 901*f2dc32d5SBoris Brezillon 902*f2dc32d5SBoris Brezillon Ok(segment_mapped) 903*f2dc32d5SBoris Brezillon } 904*f2dc32d5SBoris Brezillon 905*f2dc32d5SBoris Brezillon /// Unmaps a virtual address range from the page table. 906*f2dc32d5SBoris Brezillon /// 907*f2dc32d5SBoris Brezillon /// This function removes all page table entries in the specified range, 908*f2dc32d5SBoris Brezillon /// automatically handling different page sizes that may be present. 909*f2dc32d5SBoris Brezillon fn pt_unmap(dev: &Device, pt: &IoPageTable<'_, ARM64LPAES1>, range: Range<u64>) -> Result { 910*f2dc32d5SBoris Brezillon let mut iova = range.start; 911*f2dc32d5SBoris Brezillon let mut bytes_left_to_unmap = range.end - range.start; 912*f2dc32d5SBoris Brezillon 913*f2dc32d5SBoris Brezillon while bytes_left_to_unmap > 0 { 914*f2dc32d5SBoris Brezillon // It is fine to use just the iova to determine the page size 915*f2dc32d5SBoris Brezillon // because if the actual mapping was represented with smaller page sizes, 916*f2dc32d5SBoris Brezillon // (e.g. because the physical address was not 2MiB aligned) 917*f2dc32d5SBoris Brezillon // the ARM LPAE backend will notice and handle the lower-level table correctly. 918*f2dc32d5SBoris Brezillon let (pgsize, pgcount) = get_pgsize(iova, bytes_left_to_unmap); 919*f2dc32d5SBoris Brezillon 920*f2dc32d5SBoris Brezillon // On 32-bit systems, usize is only 32 bits, so check that 921*f2dc32d5SBoris Brezillon // the iova can be converted without truncation. 922*f2dc32d5SBoris Brezillon let iova_usize = usize::try_from(iova).map_err(|_| { 923*f2dc32d5SBoris Brezillon dev_err!( 924*f2dc32d5SBoris Brezillon dev, 925*f2dc32d5SBoris Brezillon "IOVA {:#x} cannot be represented as usize (max {:#x})", 926*f2dc32d5SBoris Brezillon iova, 927*f2dc32d5SBoris Brezillon usize::MAX 928*f2dc32d5SBoris Brezillon ); 929*f2dc32d5SBoris Brezillon EOVERFLOW 930*f2dc32d5SBoris Brezillon })?; 931*f2dc32d5SBoris Brezillon 932*f2dc32d5SBoris Brezillon // SAFETY: 933*f2dc32d5SBoris Brezillon // No other io-pgtable operation can currently access this range because Tyr holds 934*f2dc32d5SBoris Brezillon // the gpuvm_unique mutex for the entire sm_unmap() operation. 935*f2dc32d5SBoris Brezillon // We know that this page table has one or more consecutive mappings 936*f2dc32d5SBoris Brezillon // starting at `iova` with the total size of `pgcount * pgsize` because 937*f2dc32d5SBoris Brezillon // gpuvm callbacks provide exactly the range that was previously mapped. 938*f2dc32d5SBoris Brezillon let unmapped = unsafe { pt.unmap_pages(iova_usize, pgsize as usize, pgcount as usize) }; 939*f2dc32d5SBoris Brezillon 940*f2dc32d5SBoris Brezillon if unmapped == 0 { 941*f2dc32d5SBoris Brezillon dev_err!(dev, "Failed to unmap any bytes at iova {:#x}", iova_usize); 942*f2dc32d5SBoris Brezillon return Err(EINVAL); 943*f2dc32d5SBoris Brezillon } 944*f2dc32d5SBoris Brezillon 945*f2dc32d5SBoris Brezillon bytes_left_to_unmap -= unmapped as u64; 946*f2dc32d5SBoris Brezillon iova += unmapped as u64; 947*f2dc32d5SBoris Brezillon } 948*f2dc32d5SBoris Brezillon 949*f2dc32d5SBoris Brezillon Ok(()) 950*f2dc32d5SBoris Brezillon } 951