xref: /linux/drivers/gpu/drm/tyr/vm.rs (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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, &region)?;
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