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 drm::{ 15 driver, 16 gem, 17 private::Sealed, 18 Device, 19 DeviceContext, 20 Registered, // 21 }, 22 error::to_result, 23 prelude::*, 24 sync::aref::ARef, 25 types::Opaque, // 26 }; 27 use core::{ 28 marker::PhantomData, 29 ops::{ 30 Deref, 31 DerefMut, // 32 }, 33 ptr::NonNull, // 34 }; 35 use gem::{ 36 BaseObjectPrivate, 37 DriverObject, 38 IntoGEMObject, // 39 }; 40 41 /// A struct for controlling the creation of shmem-backed GEM objects. 42 /// 43 /// This is used with [`Object::new()`] to control various properties that can only be set when 44 /// initially creating a shmem-backed GEM object. 45 #[derive(Default)] 46 pub struct ObjectConfig<'a, T: DriverObject, C: DeviceContext = Registered> { 47 /// Whether to set the write-combine map flag. 48 pub map_wc: bool, 49 50 /// Reuse the DMA reservation from another GEM object. 51 /// 52 /// The newly created [`Object`] will hold an owned refcount to `parent_resv_obj` if specified. 53 pub parent_resv_obj: Option<&'a Object<T, C>>, 54 } 55 56 /// A shmem-backed GEM object. 57 /// 58 /// # Invariants 59 /// 60 /// - `obj` contains a valid initialized `struct drm_gem_shmem_object` for the lifetime of this 61 /// object. 62 /// - Any type invariants of `C` apply to the parent DRM device for this GEM object. 63 #[repr(C)] 64 #[pin_data] 65 pub struct Object<T: DriverObject, C: DeviceContext = Registered> { 66 #[pin] 67 obj: Opaque<bindings::drm_gem_shmem_object>, 68 /// Parent object that owns this object's DMA reservation object. 69 parent_resv_obj: Option<ARef<Object<T, C>>>, 70 #[pin] 71 inner: T, 72 _ctx: PhantomData<C>, 73 } 74 75 super::impl_aref_for_gem_obj! { 76 impl<T, C> for Object<T, C> 77 where 78 T: DriverObject, 79 C: DeviceContext 80 } 81 82 // SAFETY: All GEM objects are thread-safe. 83 unsafe impl<T: DriverObject, C: DeviceContext> Send for Object<T, C> {} 84 85 // SAFETY: All GEM objects are thread-safe. 86 unsafe impl<T: DriverObject, C: DeviceContext> Sync for Object<T, C> {} 87 88 impl<T: DriverObject, C: DeviceContext> Object<T, C> { 89 /// `drm_gem_object_funcs` vtable suitable for GEM shmem objects. 90 const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { 91 free: Some(Self::free_callback), 92 open: Some(super::open_callback::<T>), 93 close: Some(super::close_callback::<T>), 94 print_info: Some(bindings::drm_gem_shmem_object_print_info), 95 export: None, 96 pin: Some(bindings::drm_gem_shmem_object_pin), 97 unpin: Some(bindings::drm_gem_shmem_object_unpin), 98 get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table), 99 vmap: Some(bindings::drm_gem_shmem_object_vmap), 100 vunmap: Some(bindings::drm_gem_shmem_object_vunmap), 101 mmap: Some(bindings::drm_gem_shmem_object_mmap), 102 status: None, 103 rss: None, 104 #[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")] 105 // SAFETY: `drm_gem_shmem_vm_ops` is a valid, static const on the C side. 106 vm_ops: unsafe { &raw const bindings::drm_gem_shmem_vm_ops }, 107 evict: None, 108 }; 109 110 /// Return a raw pointer to the embedded drm_gem_shmem_object. 111 fn as_raw_shmem(&self) -> *mut bindings::drm_gem_shmem_object { 112 self.obj.get() 113 } 114 115 /// Create a new shmem-backed DRM object of the given size. 116 /// 117 /// Additional config options can be specified using `config`. 118 pub fn new( 119 dev: &Device<T::Driver, C>, 120 size: usize, 121 config: ObjectConfig<'_, T, C>, 122 args: T::Args, 123 ) -> Result<ARef<Self>> { 124 let new: Pin<KBox<Self>> = KBox::try_pin_init( 125 try_pin_init!(Self { 126 obj <- Opaque::init_zeroed(), 127 parent_resv_obj: config.parent_resv_obj.map(|p| p.into()), 128 inner <- T::new(dev, size, args), 129 _ctx: PhantomData::<C>, 130 }), 131 GFP_KERNEL, 132 )?; 133 134 // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. 135 unsafe { (*new.as_raw()).funcs = &Self::VTABLE }; 136 137 // SAFETY: The arguments are all valid via the type invariants. 138 to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.as_raw_shmem(), size) })?; 139 140 // SAFETY: We never move out of `self`. 141 let new = KBox::into_raw(unsafe { Pin::into_inner_unchecked(new) }); 142 143 // SAFETY: We're taking over the owned refcount from `drm_gem_shmem_init`. 144 let obj = unsafe { ARef::from_raw(NonNull::new_unchecked(new)) }; 145 146 // Start filling out values from `config` 147 if let Some(parent_resv) = config.parent_resv_obj { 148 // SAFETY: We have yet to expose the new gem object outside of this function, so it is 149 // safe to modify this field. 150 unsafe { (*obj.obj.get()).base.resv = parent_resv.raw_dma_resv() }; 151 } 152 153 // SAFETY: We have yet to expose this object outside of this function, so we're guaranteed 154 // to have exclusive access - thus making this safe to hold a mutable reference to. 155 let shmem = unsafe { &mut *obj.as_raw_shmem() }; 156 shmem.set_map_wc(config.map_wc); 157 158 Ok(obj) 159 } 160 161 /// Returns the `Device` that owns this GEM object. 162 pub fn dev(&self) -> &Device<T::Driver, C> { 163 // SAFETY: `dev` will have been initialized in `Self::new()` by `drm_gem_shmem_init()`. 164 unsafe { Device::from_raw((*self.as_raw()).dev) } 165 } 166 167 extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { 168 // SAFETY: 169 // - DRM always passes a valid gem object here 170 // - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that 171 // `obj` is contained within a drm_gem_shmem_object 172 let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) }; 173 174 // SAFETY: 175 // - We're in free_callback - so this function is safe to call. 176 // - We won't be using the gem resources on `this` after this call. 177 unsafe { bindings::drm_gem_shmem_release(this) }; 178 179 // SAFETY: 180 // - We verified above that `obj` is valid, which makes `this` valid 181 // - This function is set in AllocOps, so we know that `this` is contained within a 182 // `Object<T, C>` 183 let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut(); 184 185 // SAFETY: We're recovering the Kbox<> we created in gem_create_object() 186 let _ = unsafe { KBox::from_raw(this) }; 187 } 188 } 189 190 impl<T: DriverObject, C: DeviceContext> Deref for Object<T, C> { 191 type Target = T; 192 193 fn deref(&self) -> &Self::Target { 194 &self.inner 195 } 196 } 197 198 impl<T: DriverObject, C: DeviceContext> DerefMut for Object<T, C> { 199 fn deref_mut(&mut self) -> &mut Self::Target { 200 &mut self.inner 201 } 202 } 203 204 impl<T: DriverObject, C: DeviceContext> Sealed for Object<T, C> {} 205 206 impl<T: DriverObject, C: DeviceContext> gem::IntoGEMObject for Object<T, C> { 207 fn as_raw(&self) -> *mut bindings::drm_gem_object { 208 // SAFETY: 209 // - Our immutable reference is proof that this is safe to dereference. 210 // - `obj` is always a valid drm_gem_shmem_object via our type invariants. 211 unsafe { &raw mut (*self.obj.get()).base } 212 } 213 214 unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Self { 215 // SAFETY: The safety contract of from_gem_obj() guarantees that `obj` is contained within 216 // `Self` 217 unsafe { 218 let obj = Opaque::cast_from(container_of!(obj, bindings::drm_gem_shmem_object, base)); 219 220 &*container_of!(obj, Self, obj) 221 } 222 } 223 } 224 225 impl<T: DriverObject, C: DeviceContext> driver::AllocImpl for Object<T, C> { 226 type Driver = T::Driver; 227 228 const ALLOC_OPS: driver::AllocOps = driver::AllocOps { 229 gem_create_object: None, 230 prime_handle_to_fd: None, 231 prime_fd_to_handle: None, 232 gem_prime_import: None, 233 gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table), 234 dumb_create: Some(bindings::drm_gem_shmem_dumb_create), 235 dumb_map_offset: None, 236 }; 237 } 238