diff options
Diffstat (limited to 'rust/kernel/drm/gem')
| -rw-r--r-- | rust/kernel/drm/gem/mod.rs | 320 | 
1 files changed, 320 insertions, 0 deletions
| diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs new file mode 100644 index 000000000000..0cafa4a42420 --- /dev/null +++ b/rust/kernel/drm/gem/mod.rs @@ -0,0 +1,320 @@ +// 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, +    drm::driver::{AllocImpl, AllocOps}, +    error::{to_result, Result}, +    prelude::*, +    types::{ARef, Opaque}, +}; +use core::{mem, ops::Deref, ptr, ptr::NonNull}; + +/// GEM object functions, which must be implemented by drivers. +pub trait BaseDriverObject<T: BaseObject>: Sync + Send + Sized { +    /// Create a new driver data object for a GEM object of a given size. +    fn new(dev: &drm::Device<T::Driver>, size: usize) -> impl PinInit<Self, Error>; + +    /// Open a new handle to an existing object, associated with a File. +    fn open( +        _obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object, +        _file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>, +    ) -> Result { +        Ok(()) +    } + +    /// Close a handle to an existing object, associated with a File. +    fn close( +        _obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object, +        _file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>, +    ) { +    } +} + +/// Trait that represents a GEM object subtype +pub trait IntoGEMObject: Sized + super::private::Sealed { +    /// Owning driver for this type +    type Driver: drm::Driver; + +    /// Returns a reference to the raw `drm_gem_object` structure, which must be valid as long as +    /// this owning object is valid. +    #[allow(clippy::wrong_self_convention)] +    fn into_gem_obj(&self) -> &Opaque<bindings::drm_gem_object>; + +    /// Converts a pointer to a `struct drm_gem_object` into a pointer to `Self`. +    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<Object<Self>> { +    /// Parent `Driver` for this object. +    type Driver: drm::Driver; +} + +extern "C" fn open_callback<T: BaseDriverObject<U>, U: BaseObject>( +    raw_obj: *mut bindings::drm_gem_object, +    raw_file: *mut bindings::drm_file, +) -> core::ffi::c_int { +    // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`. +    let file = unsafe { +        drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file) +    }; +    let obj = +        <<<U as IntoGEMObject>::Driver as drm::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, +    } +} + +extern "C" fn close_callback<T: BaseDriverObject<U>, U: BaseObject>( +    raw_obj: *mut bindings::drm_gem_object, +    raw_file: *mut bindings::drm_file, +) { +    // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`. +    let file = unsafe { +        drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file) +    }; +    let obj = +        <<<U as IntoGEMObject>::Driver as drm::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<T: DriverObject> IntoGEMObject for Object<T> { +    type Driver = T::Driver; + +    fn into_gem_obj(&self) -> &Opaque<bindings::drm_gem_object> { +        &self.obj +    } + +    fn from_gem_obj(obj: *mut bindings::drm_gem_object) -> *mut Self { +        // SAFETY: All of our objects are Object<T>. +        unsafe { crate::container_of!(obj, Object<T>, obj).cast_mut() } +    } +} + +/// Base operations shared by all GEM object classes +pub trait BaseObject +where +    Self: crate::types::AlwaysRefCounted + IntoGEMObject, +{ +    /// Returns the size of the object in bytes. +    fn size(&self) -> usize { +        // SAFETY: `self.into_gem_obj()` is guaranteed to be a pointer to a valid `struct +        // drm_gem_object`. +        unsafe { (*self.into_gem_obj().get()).size } +    } + +    /// Creates a new handle for the object associated with a given `File` +    /// (or returns an existing one). +    fn create_handle( +        &self, +        file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>, +    ) -> Result<u32> { +        let mut handle: u32 = 0; +        // SAFETY: The arguments are all valid per the type invariants. +        to_result(unsafe { +            bindings::drm_gem_handle_create( +                file.as_raw().cast(), +                self.into_gem_obj().get(), +                &mut handle, +            ) +        })?; +        Ok(handle) +    } + +    /// Looks up an object by its handle for a given `File`. +    fn lookup_handle( +        file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>, +        handle: u32, +    ) -> Result<ARef<Self>> { +        // SAFETY: The arguments are all valid per the type invariants. +        let ptr = unsafe { bindings::drm_gem_object_lookup(file.as_raw().cast(), handle) }; +        let ptr = <Self as IntoGEMObject>::from_gem_obj(ptr); +        let ptr = NonNull::new(ptr).ok_or(ENOENT)?; + +        // SAFETY: We take ownership of the reference of `drm_gem_object_lookup()`. +        Ok(unsafe { ARef::from_raw(ptr) }) +    } + +    /// Creates an mmap offset to map the object from userspace. +    fn create_mmap_offset(&self) -> Result<u64> { +        // SAFETY: The arguments are valid per the type invariant. +        to_result(unsafe { bindings::drm_gem_create_mmap_offset(self.into_gem_obj().get()) })?; + +        // SAFETY: The arguments are valid per the type invariant. +        Ok(unsafe { +            bindings::drm_vma_node_offset_addr(ptr::addr_of_mut!( +                (*self.into_gem_obj().get()).vma_node +            )) +        }) +    } +} + +impl<T> BaseObject for T where Self: crate::types::AlwaysRefCounted + IntoGEMObject {} + +/// A base GEM object. +/// +/// Invariants +/// +/// - `self.obj` is a valid instance of a `struct drm_gem_object`. +/// - `self.dev` is always a valid pointer to a `struct drm_device`. +#[repr(C)] +#[pin_data] +pub struct Object<T: DriverObject + Send + Sync> { +    obj: Opaque<bindings::drm_gem_object>, +    dev: *const drm::Device<T::Driver>, +    #[pin] +    data: T, +} + +impl<T: DriverObject> Object<T> { +    /// The size of this object's structure. +    pub const SIZE: usize = mem::size_of::<Self>(); + +    const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { +        free: Some(Self::free_callback), +        open: Some(open_callback::<T, Object<T>>), +        close: Some(close_callback::<T, Object<T>>), +        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: &drm::Device<T::Driver>, size: usize) -> Result<ARef<Self>> { +        let obj: Pin<KBox<Self>> = KBox::pin_init( +            try_pin_init!(Self { +                obj: Opaque::new(bindings::drm_gem_object::default()), +                data <- T::new(dev, size), +                // INVARIANT: The drm subsystem guarantees that the `struct drm_device` will live +                // as long as the GEM object lives. +                dev, +            }), +            GFP_KERNEL, +        )?; + +        // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. +        unsafe { (*obj.as_raw()).funcs = &Self::OBJECT_FUNCS }; + +        // SAFETY: The arguments are all valid per the type invariants. +        to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) })?; + +        // SAFETY: We never move out of `Self`. +        let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(obj) }); + +        // SAFETY: `ptr` comes from `KBox::into_raw` and hence can't be NULL. +        let ptr = unsafe { NonNull::new_unchecked(ptr) }; + +        // SAFETY: We take over the initial reference count from `drm_gem_object_init()`. +        Ok(unsafe { ARef::from_raw(ptr) }) +    } + +    /// Returns the `Device` that owns this GEM object. +    pub fn dev(&self) -> &drm::Device<T::Driver> { +        // SAFETY: The DRM subsystem guarantees that the `struct drm_device` will live as long as +        // the GEM object lives, hence the pointer must be valid. +        unsafe { &*self.dev } +    } + +    fn as_raw(&self) -> *mut bindings::drm_gem_object { +        self.obj.get() +    } + +    extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) { +        // SAFETY: All of our objects are of type `Object<T>`. +        let this = unsafe { crate::container_of!(obj, Self, obj) }.cast_mut(); + +        // SAFETY: The C code only ever calls this callback with a valid pointer to a `struct +        // drm_gem_object`. +        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. +        let _ = unsafe { KBox::from_raw(this) }; +    } +} + +// SAFETY: Instances of `Object<T>` are always reference-counted. +unsafe impl<T: DriverObject> crate::types::AlwaysRefCounted for Object<T> { +    fn inc_ref(&self) { +        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. +        unsafe { bindings::drm_gem_object_get(self.as_raw()) }; +    } + +    unsafe fn dec_ref(obj: NonNull<Self>) { +        // SAFETY: `obj` is a valid pointer to an `Object<T>`. +        let obj = unsafe { obj.as_ref() }; + +        // SAFETY: The safety requirements guarantee that the refcount is non-zero. +        unsafe { bindings::drm_gem_object_put(obj.as_raw()) } +    } +} + +impl<T: DriverObject> super::private::Sealed for Object<T> {} + +impl<T: DriverObject> Deref for Object<T> { +    type Target = T; + +    fn deref(&self) -> &Self::Target { +        &self.data +    } +} + +impl<T: DriverObject> AllocImpl for Object<T> { +    const ALLOC_OPS: AllocOps = 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, +    }; +} + +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 +} | 
