// SPDX-License-Identifier: GPL-2.0 OR MIT //! DRM GEM API //! //! C header: [`include/linux/drm/drm_gem.h`](srctree/include/linux/drm/drm_gem.h) use crate::{ alloc::flags::*, bindings, drm::{device, drv, file}, error::{to_result, Result}, prelude::*, }; use core::{marker::PhantomPinned, mem, ops::Deref, ops::DerefMut}; /// GEM object functions, which must be implemented by drivers. pub trait BaseDriverObject: Sync + Send + Sized { /// Create a new driver data object for a GEM object of a given size. fn new(dev: &device::Device, size: usize) -> impl PinInit; /// Open a new handle to an existing object, associated with a File. fn open( _obj: &<::Driver as drv::Driver>::Object, _file: &file::File<<::Driver as drv::Driver>::File>, ) -> Result { Ok(()) } /// Close a handle to an existing object, associated with a File. fn close( _obj: &<::Driver as drv::Driver>::Object, _file: &file::File<<::Driver as drv::Driver>::File>, ) { } } /// Trait that represents a GEM object subtype pub trait IntoGEMObject: Sized + crate::private::Sealed { /// Owning driver for this type type Driver: drv::Driver; /// Returns a reference to the raw `drm_gem_object` structure, which must be valid as long as /// this owning object is valid. fn gem_obj(&self) -> &bindings::drm_gem_object; /// Converts a pointer to a `drm_gem_object` into a pointer to this type. fn from_gem_obj(obj: *mut bindings::drm_gem_object) -> *mut Self; } /// Trait which must be implemented by drivers using base GEM objects. pub trait DriverObject: BaseDriverObject> { /// Parent `Driver` for this object. type Driver: drv::Driver; } #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { // SAFETY: All of our objects are Object. let this = unsafe { crate::container_of!(obj, Object, obj) } as *mut Object; // SAFETY: The pointer we got has to be valid unsafe { bindings::drm_gem_object_release(obj) }; // SAFETY: All of our objects are allocated via KBox<>, and we're in the // free callback which guarantees this object has zero remaining references, // so we can drop it unsafe { let _ = KBox::from_raw(this); }; } #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn open_callback, U: BaseObject>( raw_obj: *mut bindings::drm_gem_object, raw_file: *mut bindings::drm_file, ) -> core::ffi::c_int { // SAFETY: The pointer we got has to be valid. let file = unsafe { file::File::<<::Driver as drv::Driver>::File>::from_raw(raw_file) }; let obj = <<::Driver as drv::Driver>::Object as IntoGEMObject>::from_gem_obj( raw_obj, ); // SAFETY: from_gem_obj() returns a valid pointer as long as the type is // correct and the raw_obj we got is valid. match T::open(unsafe { &*obj }, &file) { Err(e) => e.to_errno(), Ok(()) => 0, } } #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn close_callback, U: BaseObject>( raw_obj: *mut bindings::drm_gem_object, raw_file: *mut bindings::drm_file, ) { // SAFETY: The pointer we got has to be valid. let file = unsafe { file::File::<<::Driver as drv::Driver>::File>::from_raw(raw_file) }; let obj = <<::Driver as drv::Driver>::Object as IntoGEMObject>::from_gem_obj( raw_obj, ); // SAFETY: from_gem_obj() returns a valid pointer as long as the type is // correct and the raw_obj we got is valid. T::close(unsafe { &*obj }, &file); } impl IntoGEMObject for Object { type Driver = T::Driver; fn gem_obj(&self) -> &bindings::drm_gem_object { &self.obj } fn from_gem_obj(obj: *mut bindings::drm_gem_object) -> *mut Object { // SAFETY: All of our objects are Object. unsafe { crate::container_of!(obj, Object, obj) as *mut Object } } } /// Base operations shared by all GEM object classes pub trait BaseObject: IntoGEMObject { /// Returns the size of the object in bytes. fn size(&self) -> usize { self.gem_obj().size } /// Creates a new reference to the object. fn reference(&self) -> ObjectRef { // SAFETY: Having a reference to an Object implies holding a GEM reference unsafe { bindings::drm_gem_object_get(self.gem_obj() as *const _ as *mut _); } ObjectRef { ptr: self as *const _, } } /// Creates a new handle for the object associated with a given `File` /// (or returns an existing one). fn create_handle( &self, file: &file::File<<::Driver as drv::Driver>::File>, ) -> Result { let mut handle: u32 = 0; // SAFETY: The arguments are all valid per the type invariants. to_result(unsafe { bindings::drm_gem_handle_create( file.raw() as *mut _, self.gem_obj() as *const _ as *mut _, &mut handle, ) })?; Ok(handle) } /// Looks up an object by its handle for a given `File`. fn lookup_handle( file: &file::File<<::Driver as drv::Driver>::File>, handle: u32, ) -> Result> { // SAFETY: The arguments are all valid per the type invariants. let ptr = unsafe { bindings::drm_gem_object_lookup(file.raw() as *mut _, handle) }; if ptr.is_null() { Err(ENOENT) } else { Ok(ObjectRef { ptr: ptr as *const _, }) } } /// Creates an mmap offset to map the object from userspace. fn create_mmap_offset(&self) -> Result { // SAFETY: The arguments are valid per the type invariant. to_result(unsafe { bindings::drm_gem_create_mmap_offset(self.gem_obj() as *const _ as *mut _) })?; // SAFETY: The arguments are valid per the type invariant. Ok(unsafe { bindings::drm_vma_node_offset_addr(&self.gem_obj().vma_node as *const _ as *mut _) }) } } impl BaseObject for T {} /// A base GEM object. #[repr(C)] #[pin_data] pub struct Object { obj: bindings::drm_gem_object, // The DRM core ensures the Device exists as long as its objects exist, so we don't need to // manage the reference count here. dev: *const bindings::drm_device, #[pin] inner: T, #[pin] _p: PhantomPinned, } // SAFETY: This struct is safe to zero-initialize unsafe impl init::Zeroable for bindings::drm_gem_object {} impl Object { /// The size of this object's structure. pub const SIZE: usize = mem::size_of::(); const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { free: Some(free_callback::), open: Some(open_callback::>), close: Some(close_callback::>), print_info: None, export: None, pin: None, unpin: None, get_sg_table: None, vmap: None, vunmap: None, mmap: None, status: None, vm_ops: core::ptr::null_mut(), evict: None, rss: None, }; /// Create a new GEM object. pub fn new(dev: &device::Device, size: usize) -> Result>> { let obj: Pin> = KBox::pin_init( try_pin_init!(Self { // SAFETY: This struct is expected to be zero-initialized obj: bindings::drm_gem_object { funcs: &Self::OBJECT_FUNCS, ..Default::default() }, inner <- T::new(dev, size), // SAFETY: The drm subsystem guarantees that the drm_device will live as long as // the GEM object lives, so we can conjure a reference out of thin air. dev: dev.as_raw(), _p: PhantomPinned }), GFP_KERNEL, )?; // SAFETY: The arguments are all valid per the type invariants. to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), &obj.obj as *const _ as *mut _, size) })?; // SAFETY: We never move out of self let obj_ref = unsafe { Pin::new_unchecked(UniqueObjectRef { // SAFETY: We never move out of the Box ptr: KBox::leak(Pin::into_inner_unchecked(obj)), _p: PhantomPinned, }) }; Ok(obj_ref) } /// Returns the `Device` that owns this GEM object. pub fn dev(&self) -> &device::Device { // SAFETY: The drm subsystem guarantees that the drm_device will live as long as // the GEM object lives, so we can just borrow from the raw pointer. unsafe { device::Device::borrow(self.dev) } } } impl crate::private::Sealed for Object {} impl Deref for Object { type Target = T; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for Object { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl drv::AllocImpl for Object { const ALLOC_OPS: drv::AllocOps = drv::AllocOps { gem_create_object: None, prime_handle_to_fd: None, prime_fd_to_handle: None, gem_prime_import: None, gem_prime_import_sg_table: None, dumb_create: None, dumb_map_offset: None, }; } /// A reference-counted shared reference to a base GEM object. pub struct ObjectRef { // Invariant: the pointer is valid and initialized, and this ObjectRef owns a reference to it. ptr: *const T, } impl ObjectRef { /// Downgrade this reference to a shared reference. pub fn from_pinned_unique(pin: Pin>) -> Self { // SAFETY: A (shared) `ObjectRef` doesn't need to be pinned, since it doesn't allow us to // optain a mutable reference. let uq = unsafe { Pin::into_inner_unchecked(pin) }; uq.into_ref() } } /// SAFETY: GEM object references are safe to send between threads. unsafe impl Send for ObjectRef {} /// SAFETY: GEM object references are safe to share between threads. unsafe impl Sync for ObjectRef {} impl Clone for ObjectRef { fn clone(&self) -> Self { self.reference() } } impl Drop for ObjectRef { fn drop(&mut self) { // SAFETY: Having an ObjectRef implies holding a GEM reference. // The free callback will take care of deallocation. unsafe { bindings::drm_gem_object_put((*self.ptr).gem_obj() as *const _ as *mut _); } } } impl Deref for ObjectRef { type Target = T; fn deref(&self) -> &Self::Target { // SAFETY: The pointer is valid per the invariant unsafe { &*self.ptr } } } /// A unique reference to a base GEM object. pub struct UniqueObjectRef { // Invariant: the pointer is valid and initialized, and this ObjectRef owns the only reference // to it. ptr: *mut T, _p: PhantomPinned, } impl UniqueObjectRef { /// Downgrade this reference to a shared reference. pub fn into_ref(self) -> ObjectRef { let ptr = self.ptr as *const _; core::mem::forget(self); ObjectRef { ptr } } } impl Drop for UniqueObjectRef { fn drop(&mut self) { // SAFETY: Having a UniqueObjectRef implies holding a GEM // reference. The free callback will take care of deallocation. unsafe { bindings::drm_gem_object_put((*self.ptr).gem_obj() as *const _ as *mut _); } } } impl Deref for UniqueObjectRef { type Target = T; fn deref(&self) -> &Self::Target { // SAFETY: The pointer is valid per the invariant unsafe { &*self.ptr } } } impl DerefMut for UniqueObjectRef { fn deref_mut(&mut self) -> &mut Self::Target { // SAFETY: The pointer is valid per the invariant unsafe { &mut *self.ptr } } } pub(super) const fn create_fops() -> bindings::file_operations { // SAFETY: As by the type invariant, it is safe to initialize `bindings::file_operations` // zeroed. let mut fops: bindings::file_operations = unsafe { core::mem::zeroed() }; fops.owner = core::ptr::null_mut(); fops.open = Some(bindings::drm_open); fops.release = Some(bindings::drm_release); fops.unlocked_ioctl = Some(bindings::drm_ioctl); #[cfg(CONFIG_COMPAT)] { fops.compat_ioctl = Some(bindings::drm_compat_ioctl); } fops.poll = Some(bindings::drm_poll); fops.read = Some(bindings::drm_read); fops.llseek = Some(bindings::noop_llseek); fops.mmap = Some(bindings::drm_gem_mmap); fops.fop_flags = bindings::FOP_UNSIGNED_OFFSET; fops }