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