1 // SPDX-License-Identifier: GPL-2.0 2 3 //! DRM GEM shmem helper objects 4 //! 5 //! C header: [`include/linux/drm/drm_gem_shmem_helper.h`](srctree/include/drm/drm_gem_shmem_helper.h) 6 7 // TODO: 8 // - There are a number of spots here that manually acquire/release the DMA reservation lock using 9 // dma_resv_(un)lock(). In the future we should add support for ww mutex, expose a method to 10 // acquire a reference to the WwMutex, and then use that directly instead of the C functions here. 11 12 use crate::{ 13 container_of, 14 device::{ 15 self, 16 Bound, // 17 }, 18 devres::*, 19 drm::{ 20 driver, 21 gem, 22 private::Sealed, 23 Device, // 24 }, 25 error::{ 26 from_err_ptr, 27 to_result, // 28 }, 29 io::{ 30 IoBase, 31 Region, 32 SysMem, 33 SysMemBackend, // 34 }, 35 prelude::*, 36 scatterlist, 37 sync::{ 38 aref::ARef, 39 new_mutex, 40 Mutex, 41 SetOnce, // 42 }, 43 types::{ 44 NotThreadSafe, 45 Opaque, // 46 }, 47 }; 48 use core::{ 49 ffi::c_void, 50 mem::{ 51 ManuallyDrop, 52 MaybeUninit, // 53 }, 54 ops::{ 55 Deref, 56 DerefMut, // 57 }, 58 ptr::{ 59 self, 60 NonNull, // 61 }, 62 }; 63 use gem::{ 64 BaseObject, 65 BaseObjectPrivate, 66 DriverObject, 67 IntoGEMObject, // 68 }; 69 70 /// A struct for controlling the creation of shmem-backed GEM objects. 71 /// 72 /// This is used with [`Object::new()`] to control various properties that can only be set when 73 /// initially creating a shmem-backed GEM object. 74 pub struct ObjectConfig<'a, T: DriverObject> { 75 /// Whether to set the write-combine map flag. 76 pub map_wc: bool, 77 78 /// Reuse the DMA reservation from another GEM object. 79 /// 80 /// The newly created [`Object`] will hold an owned refcount to `parent_resv_obj` if specified. 81 pub parent_resv_obj: Option<&'a Object<T>>, 82 } 83 84 impl<'a, T: DriverObject> Default for ObjectConfig<'a, T> { 85 #[inline(always)] 86 fn default() -> Self { 87 Self { 88 map_wc: false, 89 parent_resv_obj: None, 90 } 91 } 92 } 93 94 /// A shmem-backed GEM object. 95 /// 96 /// # Invariants 97 /// 98 /// - `obj` contains a valid initialized `struct drm_gem_shmem_object` for the lifetime of this 99 /// object. 100 #[repr(C)] 101 #[pin_data] 102 pub struct Object<T: DriverObject> { 103 #[pin] 104 obj: Opaque<bindings::drm_gem_shmem_object>, 105 /// Parent object that owns this object's DMA reservation object. 106 parent_resv_obj: Option<ARef<Object<T>>>, 107 /// Devres object for unmapping any SGTable on driver-unbind. 108 sgt_res: ManuallyDrop<SetOnce<Devres<SGTableMap<T>>>>, 109 #[pin] 110 /// Lock for protecting initialization of `sgt_res`. 111 sgt_lock: Mutex<()>, 112 #[pin] 113 inner: T, 114 } 115 116 super::impl_aref_for_gem_obj! { 117 impl<T> for Object<T> 118 where 119 T: DriverObject 120 } 121 122 // SAFETY: All GEM objects are thread-safe. 123 unsafe impl<T: DriverObject> Send for Object<T> {} 124 125 // SAFETY: All GEM objects are thread-safe. 126 unsafe impl<T: DriverObject> Sync for Object<T> {} 127 128 impl<T: DriverObject> Object<T> { 129 /// `drm_gem_object_funcs` vtable suitable for GEM shmem objects. 130 const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { 131 free: Some(Self::free_callback), 132 open: Some(super::open_callback::<T>), 133 close: Some(super::close_callback::<T>), 134 print_info: Some(bindings::drm_gem_shmem_object_print_info), 135 export: None, 136 pin: Some(bindings::drm_gem_shmem_object_pin), 137 unpin: Some(bindings::drm_gem_shmem_object_unpin), 138 get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table), 139 vmap: Some(bindings::drm_gem_shmem_object_vmap), 140 vunmap: Some(bindings::drm_gem_shmem_object_vunmap), 141 mmap: Some(bindings::drm_gem_shmem_object_mmap), 142 status: None, 143 rss: None, 144 #[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")] 145 // SAFETY: `drm_gem_shmem_vm_ops` is a valid, static const on the C side. 146 vm_ops: unsafe { &raw const bindings::drm_gem_shmem_vm_ops }, 147 evict: None, 148 }; 149 150 /// Return a raw pointer to the embedded drm_gem_shmem_object. 151 fn as_raw_shmem(&self) -> *mut bindings::drm_gem_shmem_object { 152 self.obj.get() 153 } 154 155 /// Returns the `Device` that owns this GEM object. 156 pub fn dev(&self) -> &Device<T::Driver> { 157 // SAFETY: `dev` will have been initialized in `Self::new()` by `drm_gem_shmem_init()`. 158 unsafe { Device::from_raw((*self.as_raw()).dev) } 159 } 160 161 extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { 162 // SAFETY: 163 // - DRM always passes a valid gem object here 164 // - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that 165 // `obj` is contained within a drm_gem_shmem_object 166 let base = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) }; 167 168 // SAFETY: 169 // - We verified above that `obj` is valid, which makes `this` valid 170 // - This function is set in AllocOps, so we know that `this` is contained within an 171 // `Object<T>` 172 let this = unsafe { container_of!(Opaque::cast_from(base), Self, obj) }.cast_mut(); 173 174 // We need to drop `sgt_res` first, since doing so requires that the GEM object is still 175 // alive. 176 // SAFETY: 177 // - We verified above that `this` is valid. 178 // - We are in free_callback, guaranteeing we have exclusive access to `this` and that 179 // `sgt_res` will not be used after dropping it here. 180 unsafe { ManuallyDrop::drop(&mut (*this).sgt_res) }; 181 182 // SAFETY: 183 // - We're in free_callback - so this function is safe to call. 184 // - We won't be using the gem resources on `this` after this call. 185 unsafe { bindings::drm_gem_shmem_release(base) }; 186 187 // SAFETY: We're recovering the Kbox<> we created in gem_create_object() 188 let _ = unsafe { KBox::from_raw(this) }; 189 } 190 191 /// Attempt to create a vmap from the gem object, and confirm the size of said vmap. 192 fn make_vmap<'a, R, const SIZE: usize>(&'a self) -> Result<VMap<T, R, SIZE>> 193 where 194 R: Deref<Target = Self> + From<&'a Self>, 195 { 196 // INVARIANT: We check here that the gem object is at least as large as `SIZE`. 197 if self.size() < SIZE { 198 return Err(ENOSPC); 199 } 200 201 let mut map: MaybeUninit<bindings::iosys_map> = MaybeUninit::uninit(); 202 let guard = DmaResvGuard::new(self); 203 204 // SAFETY: `drm_gem_shmem_vmap()` can be called with the DMA reservation lock held. 205 to_result(unsafe { 206 bindings::drm_gem_shmem_vmap_locked(self.as_raw_shmem(), map.as_mut_ptr()) 207 })?; 208 209 // Drop the guard explicitly here, since we may need to call `raw_vunmap()` (which 210 // re-acquires the lock). 211 drop(guard); 212 213 // SAFETY: The call to `drm_gem_shmem_vmap_locked()` succeeded above, so we are guaranteed 214 // that map is properly initialized. 215 let map = unsafe { map.assume_init() }; 216 217 // XXX: We don't currently support iomem allocations 218 if map.is_iomem { 219 // SAFETY: The vmap operation above succeeded, guaranteeing that `map` points to a valid 220 // memory mapping. 221 unsafe { self.raw_vunmap(map) }; 222 223 Err(ENOTSUPP) 224 } else { 225 Ok(VMap { 226 // INVARIANT: `addr` remains valid for as long as `owner` does, which extends to the 227 // lifetime of `VMap` itself. 228 // SAFETY: We checked that this is not an iomem allocation, making it safe to read 229 // vaddr. 230 addr: unsafe { map.__bindgen_anon_1.vaddr }, 231 owner: self.into(), 232 }) 233 } 234 } 235 236 /// Unmap a vmap from the gem object. 237 /// 238 /// # Safety 239 /// 240 /// - The caller promises that `map` is a valid vmap on this gem object. 241 /// - The caller promises that the memory pointed to by map will no longer be accesed through 242 /// this instance. 243 unsafe fn raw_vunmap(&self, mut map: bindings::iosys_map) { 244 let _guard = DmaResvGuard::new(self); 245 246 // SAFETY: 247 // - This function is safe to call with the DMA reservation lock held. 248 // - The caller promises that `map` is a valid vmap on this gem object. 249 unsafe { bindings::drm_gem_shmem_vunmap_locked(self.as_raw_shmem(), &mut map) }; 250 } 251 252 /// Creates and returns a virtual kernel memory mapping for this object. 253 #[inline] 254 pub fn vmap<const SIZE: usize>(&self) -> Result<VMapRef<'_, T, SIZE>> { 255 self.make_vmap() 256 } 257 258 /// Creates (if necessary) and returns an immutable reference to a scatter-gather table of DMA 259 /// pages for this object. 260 /// 261 /// This will pin the object in memory. It is expected that `dev` should be a pointer to the 262 /// same [`device::Device`] which `self` belongs to, otherwise this function will return 263 /// `Err(EINVAL)`. 264 pub fn sg_table<'a>( 265 &'a self, 266 dev: &'a device::Device<Bound>, 267 ) -> Result<&'a scatterlist::SGTable> { 268 let parent = self.dev().as_ref(); 269 if dev.as_raw() != parent.as_ref().as_raw() { 270 return Err(EINVAL); 271 } 272 273 let sgt_res = 'out: { 274 // Fast path: sgt_res is already initialized 275 if let Some(sgt_res) = self.sgt_res.as_ref() { 276 break 'out sgt_res; 277 } 278 279 // Slow path: Grab the lock and see if we need to initialize sgt_res. 280 let _guard = self.sgt_lock.lock(); 281 282 // If someone initialized it while we were waiting, we can exit early. 283 if let Some(sgt_res) = self.sgt_res.as_ref() { 284 break 'out sgt_res; 285 } 286 287 // If not, finish initializing and return. `populate()` cannot return false, as 288 // `sgt_res` must be unpopulated, and we must hold `sgt_lock` to reach this point. 289 self.sgt_res 290 .populate(Devres::new(dev, SGTableMap::new(self))?); 291 292 // SAFETY: We just populated sgt_res above. 293 unsafe { self.sgt_res.as_ref().unwrap_unchecked() } 294 }; 295 296 Ok(sgt_res.access(dev)?) 297 } 298 299 /// Create a new shmem-backed DRM object of the given size. 300 /// 301 /// Additional config options can be specified using `config`. 302 pub fn new( 303 dev: &Device<T::Driver>, 304 size: usize, 305 config: ObjectConfig<'_, T>, 306 args: T::Args, 307 ) -> Result<ARef<Self>> { 308 let new: Pin<KBox<Self>> = KBox::try_pin_init( 309 try_pin_init!(Self { 310 obj <- Opaque::init_zeroed(), 311 parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), 312 sgt_res: ManuallyDrop::new(SetOnce::new()), 313 sgt_lock <- new_mutex!(()), 314 inner <- T::new(dev, size, args), 315 }), 316 GFP_KERNEL, 317 )?; 318 319 // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. 320 unsafe { (*new.as_raw()).funcs = &Self::VTABLE }; 321 322 // SAFETY: The arguments are all valid via the type invariants. 323 to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.as_raw_shmem(), size) })?; 324 325 // SAFETY: We never move out of `self`. 326 let new = KBox::into_raw(unsafe { Pin::into_inner_unchecked(new) }); 327 328 // SAFETY: We're taking over the owned refcount from `drm_gem_shmem_init`. 329 let obj = unsafe { ARef::from_raw(NonNull::new_unchecked(new)) }; 330 331 // Start filling out values from `config` 332 if let Some(parent_resv) = config.parent_resv_obj { 333 // SAFETY: We have yet to expose the new gem object outside of this function, so it is 334 // safe to modify this field. 335 unsafe { (*obj.obj.get()).base.resv = parent_resv.raw_dma_resv() }; 336 } 337 338 // SAFETY: We have yet to expose this object outside of this function, so we're guaranteed 339 // to have exclusive access - thus making this safe to hold a mutable reference to. 340 let shmem = unsafe { &mut *obj.as_raw_shmem() }; 341 shmem.set_map_wc(config.map_wc); 342 343 Ok(obj) 344 } 345 346 /// Creates and returns an owned reference to a virtual kernel memory mapping for this object. 347 #[inline] 348 pub fn owned_vmap<const SIZE: usize>(&self) -> Result<VMapOwned<T, SIZE>> { 349 self.make_vmap() 350 } 351 } 352 353 impl<T: DriverObject> Deref for Object<T> { 354 type Target = T; 355 356 fn deref(&self) -> &Self::Target { 357 &self.inner 358 } 359 } 360 361 impl<T: DriverObject> DerefMut for Object<T> { 362 fn deref_mut(&mut self) -> &mut Self::Target { 363 &mut self.inner 364 } 365 } 366 367 impl<T: DriverObject> Sealed for Object<T> {} 368 369 impl<T: DriverObject> gem::IntoGEMObject for Object<T> { 370 fn as_raw(&self) -> *mut bindings::drm_gem_object { 371 // SAFETY: 372 // - Our immutable reference is proof that this is safe to dereference. 373 // - `obj` is always a valid drm_gem_shmem_object via our type invariants. 374 unsafe { &raw mut (*self.obj.get()).base } 375 } 376 377 unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Self { 378 // SAFETY: The safety contract of from_gem_obj() guarantees that `obj` is contained within 379 // `Self` 380 unsafe { 381 let obj = Opaque::cast_from(container_of!(obj, bindings::drm_gem_shmem_object, base)); 382 383 &*container_of!(obj, Self, obj) 384 } 385 } 386 } 387 388 impl<T: DriverObject> driver::AllocImpl for Object<T> { 389 type Driver = T::Driver; 390 391 const ALLOC_OPS: driver::AllocOps = driver::AllocOps { 392 gem_create_object: None, 393 prime_handle_to_fd: None, 394 prime_fd_to_handle: None, 395 gem_prime_import: None, 396 gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table), 397 dumb_create: Some(bindings::drm_gem_shmem_dumb_create), 398 dumb_map_offset: None, 399 }; 400 } 401 402 /// Private helper-type for holding the `dma_resv` object for a GEM shmem object. 403 /// 404 /// When this is dropped, the `dma_resv` lock is dropped as well. 405 /// 406 // TODO: This should be replace with a WwMutex equivalent once we have such bindings in the kernel. 407 struct DmaResvGuard<'a, T: DriverObject>(&'a Object<T>, NotThreadSafe); 408 409 impl<'a, T: DriverObject> DmaResvGuard<'a, T> { 410 #[inline] 411 fn new(obj: &'a Object<T>) -> Self { 412 // SAFETY: This lock is initialized throughout the lifetime of `object`. 413 unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut()) }; 414 415 Self(obj, NotThreadSafe) 416 } 417 } 418 419 impl<'a, T: DriverObject> Drop for DmaResvGuard<'a, T> { 420 #[inline] 421 fn drop(&mut self) { 422 // SAFETY: We are releasing the lock grabbed during the creation of this object. 423 unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) }; 424 } 425 } 426 427 /// A reference to a virtual mapping for an shmem-based GEM object in kernel address space. 428 /// 429 /// # Invariants 430 /// 431 /// - The size of `owner` is >= SIZE. 432 /// - The memory pointed to by `addr` remains valid at least until this object is dropped. 433 pub struct VMap<D, R, const SIZE: usize = 0> 434 where 435 D: DriverObject, 436 R: Deref<Target = Object<D>>, 437 { 438 addr: *mut c_void, 439 owner: R, 440 } 441 442 /// An alias type for a reference to a shmem-based GEM object's VMap. 443 pub type VMapRef<'a, D, const SIZE: usize = 0> = VMap<D, &'a Object<D>, SIZE>; 444 445 /// An alias type for an owned reference to a shmem-based GEM object's VMap. 446 pub type VMapOwned<D, const SIZE: usize = 0> = VMap<D, ARef<Object<D>>, SIZE>; 447 448 impl<D, R, const SIZE: usize> VMap<D, R, SIZE> 449 where 450 D: DriverObject, 451 R: Deref<Target = Object<D>>, 452 { 453 /// Borrows a reference to the object that owns this virtual mapping. 454 #[inline] 455 pub fn owner(&self) -> &Object<D> { 456 &self.owner 457 } 458 } 459 460 impl<'a, D, R, const SIZE: usize> IoBase<'a> for &'a VMap<D, R, SIZE> 461 where 462 D: DriverObject, 463 R: Deref<Target = Object<D>>, 464 { 465 type Backend = SysMemBackend; 466 type Target = Region<SIZE>; 467 468 #[inline] 469 fn as_view(self) -> SysMem<'a, Region<SIZE>> { 470 let ptr = Region::ptr_from_raw_parts_mut(self.addr.cast(), self.owner.size()); 471 472 // SAFETY: Per type invariants of `VMap`: 473 // - `addr .. addr + owner.size()` is a valid kernel accessible memory region. 474 // - `addr` is page-aligned, which satisfies `Region`'s 4-byte alignment requirement. 475 // - The memory remains valid until this `VMap` is dropped; since `self` is `&'a VMap`, 476 // the borrow prevents the `VMap` from being dropped for the lifetime `'a`. 477 unsafe { SysMem::new(ptr) } 478 } 479 } 480 481 impl<D, R, const SIZE: usize> Drop for VMap<D, R, SIZE> 482 where 483 D: DriverObject, 484 R: Deref<Target = Object<D>>, 485 { 486 #[inline] 487 fn drop(&mut self) { 488 // SAFETY: 489 // - Our existence is proof that this map was previously created using self.owner. 490 // - Since we are in Drop, we are guaranteed that no one will access the memory 491 // through this mapping after calling this. 492 unsafe { 493 self.owner.raw_vunmap(bindings::iosys_map { 494 is_iomem: false, 495 __bindgen_anon_1: bindings::iosys_map__bindgen_ty_1 { vaddr: self.addr }, 496 }) 497 }; 498 } 499 } 500 501 // SAFETY: `addr` points to a valid memory address for as long as `owner` exists, meaning that so 502 // long as `owner` is `Send` so is `VMap`. 503 unsafe impl<D, R, const SIZE: usize> Send for VMap<D, R, SIZE> 504 where 505 D: DriverObject, 506 R: Deref<Target = Object<D>> + Send, 507 { 508 } 509 510 // SAFETY: `addr` points to a valid memory address for as long as `owner` exists, meaning that so 511 // long as `owner` is `Sync` so is `VMap`. 512 unsafe impl<D, R, const SIZE: usize> Sync for VMap<D, R, SIZE> 513 where 514 D: DriverObject, 515 R: Deref<Target = Object<D>> + Sync, 516 { 517 } 518 519 /// A reference to a GEM object that is known to have a mapped [`SGTable`]. 520 /// 521 /// This is used by the Rust bindings with [`Devres`] in order to ensure that mappings for SGTables 522 /// on GEM shmem objects are revoked on driver-unbind. 523 /// 524 /// # Invariants 525 /// 526 /// - `self.obj` always points to a valid GEM object. 527 /// - This object is proof that `self.obj.owner.sgt_res` has an initialized and valid pointer to an 528 /// [`SGTable`]. 529 /// 530 /// [`SGTable`]: scatterlist::SGTable 531 pub struct SGTableMap<T: DriverObject> { 532 obj: NonNull<Object<T>>, 533 } 534 535 impl<T: DriverObject> Deref for SGTableMap<T> { 536 type Target = scatterlist::SGTable; 537 538 fn deref(&self) -> &Self::Target { 539 // SAFETY: 540 // - The NonNull is guaranteed to be valid via our type invariants. 541 // - The sgt field is guaranteed to be initialized and valid via our type invariants. 542 unsafe { scatterlist::SGTable::from_raw((*self.obj.as_ref().as_raw_shmem()).sgt) } 543 } 544 } 545 546 impl<T: DriverObject> Drop for SGTableMap<T> { 547 fn drop(&mut self) { 548 // SAFETY: `obj` is always valid via our type invariants 549 let obj = unsafe { self.obj.as_ref() }; 550 let _lock = DmaResvGuard::new(obj); 551 552 // SAFETY: We acquired the lock needed for calling this function above 553 unsafe { bindings::__drm_gem_shmem_free_sgt_locked(obj.as_raw_shmem()) }; 554 } 555 } 556 557 impl<T: DriverObject> SGTableMap<T> { 558 fn new(obj: &Object<T>) -> impl Init<Self, Error> { 559 // INVARIANT: 560 // - We call drm_gem_shmem_get_pages_sgt below and check whether or not it succeeds, 561 // fulfilling the invariant of SGTableMap that the object's `sgt` field is initialized. 562 // SAFETY: 563 // - `obj` is fully initialized, making this function safe to call. 564 from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(obj.as_raw_shmem()) })?; 565 566 Ok(Self { obj: obj.into() }) 567 } 568 } 569 570 // SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invariants, and the GEM object 571 // it points to is guaranteed to be thread-safe. 572 unsafe impl<T: DriverObject> Send for SGTableMap<T> {} 573 // SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invariants, and the GEM object 574 // it points to is guaranteed to be thread-safe. 575 unsafe impl<T: DriverObject> Sync for SGTableMap<T> {} 576 577 #[kunit_tests(rust_drm_gem_shmem)] 578 mod tests { 579 use super::*; 580 use crate::{ 581 drm::{ 582 self, 583 UnregisteredDevice, // 584 }, 585 faux, 586 io::Io, 587 page::PAGE_SIZE, // 588 }; 589 590 // The bare minimum needed to create a fake drm driver for kunit 591 592 #[pin_data] 593 struct KunitData {} 594 struct KunitDriver; 595 struct KunitFile; 596 #[pin_data] 597 struct KunitObject {} 598 599 const INFO: drm::DriverInfo = drm::DriverInfo { 600 major: 0, 601 minor: 0, 602 patchlevel: 0, 603 name: c"kunit", 604 desc: c"Kunit", 605 }; 606 607 impl drm::file::DriverFile for KunitFile { 608 type Driver = KunitDriver; 609 610 fn open(_dev: &drm::Device<KunitDriver>) -> Result<Pin<KBox<Self>>> { 611 Ok(KBox::new(Self, GFP_KERNEL)?.into()) 612 } 613 } 614 615 impl gem::DriverObject for KunitObject { 616 type Driver = KunitDriver; 617 type Args = (); 618 619 fn new( 620 _dev: &drm::Device<KunitDriver>, 621 _size: usize, 622 _args: Self::Args, 623 ) -> impl PinInit<Self, Error> { 624 try_pin_init!(KunitObject {}) 625 } 626 } 627 628 #[vtable] 629 impl drm::Driver for KunitDriver { 630 type Data = KunitData; 631 type RegistrationData<'a> = (); 632 type File = KunitFile; 633 type Object = Object<KunitObject>; 634 type ParentDevice<Ctx: device::DeviceContext> = faux::Device<Ctx>; 635 636 const INFO: drm::DriverInfo = INFO; 637 const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor] = &[]; 638 } 639 640 fn create_drm_dev() -> Result<(faux::Registration, UnregisteredDevice<KunitDriver>)> { 641 // Create a faux DRM device so we can test gem object creation. 642 let data = try_pin_init!(KunitData {}); 643 let reg = faux::Registration::new(c"Kunit", None)?; 644 let fdev = reg.as_ref(); 645 let drm = UnregisteredDevice::new(fdev, data)?; 646 647 Ok((reg, drm)) 648 } 649 650 #[test] 651 fn compile_time_vmap_sizes() -> Result { 652 let (_dev, drm) = create_drm_dev()?; 653 654 let obj = Object::<KunitObject>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?; 655 656 // Try creating a normal vmap 657 obj.vmap::<PAGE_SIZE>()?; 658 659 // Try creating a vmap that's smaller then the size we specified 660 let vmap = obj.vmap::<{ PAGE_SIZE - 100 }>()?; 661 662 // Verify the owner matches 663 assert!(ptr::eq(vmap.owner(), obj.deref())); 664 665 // Verify the size matches the actual object size 666 assert_eq!(vmap.size(), PAGE_SIZE); 667 668 // Make sure creating a vmap that's too large fails 669 assert!(obj.vmap::<{ PAGE_SIZE + 200 }>().is_err()); 670 671 Ok(()) 672 } 673 674 #[test] 675 fn vmap_io() -> Result { 676 let (_dev, drm) = create_drm_dev()?; 677 678 let obj = Object::<KunitObject>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?; 679 680 let vmap = obj.vmap::<PAGE_SIZE>()?; 681 682 vmap.write8(0xDE, 0x0); 683 assert_eq!(vmap.read8(0x0), 0xDE); 684 vmap.write32(0xFEDCBA98, 0x20); 685 686 assert_eq!(vmap.read32(0x20), 0xFEDCBA98); 687 688 // Ensure the ordering in memory is correct 689 let expected = 0xFEDCBA98_u32.to_ne_bytes().into_iter(); 690 for (offset, expected) in (0x20..=0x23).zip(expected) { 691 assert_eq!(vmap.try_read8(offset).unwrap(), expected); 692 } 693 694 Ok(()) 695 } 696 697 // TODO: I would love to actually test the success paths of sg_table(), but that would require 698 // also implementing dummy dma_ops so that trying to create a mapping doesn't explode. So, leave 699 // that for someone else. 700 701 // Ensures that passing the wrong device to sg_table() fails as we expect, and also ensure it 702 // skips initializing `sgt_res` since we could otherwise create `sgt_res` with the wrong device 703 // bound to it. 704 #[test] 705 fn fail_sg_table_on_wrong_dev() -> Result { 706 let (_dev, drm) = create_drm_dev()?; 707 let reg = faux::Registration::new(c"EvilKunit", None)?; 708 let wrong_dev = reg.as_ref(); 709 710 let obj = Object::<KunitObject>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?; 711 712 assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL); 713 714 // If sgt_res was not initialized mistakenly with the wrong device, this should still fail. 715 assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL); 716 717 // TODO: Someday, we should test that creating an sg_table here still succeeds. 718 719 Ok(()) 720 } 721 } 722