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