Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+454
View File
@@ -0,0 +1,454 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
//! DRM device.
//!
//! C header: [`include/drm/drm_device.h`](srctree/include/drm/drm_device.h)
use crate::{
alloc::allocator::Kmalloc,
bindings,
device,
drm::{
self,
driver::AllocImpl,
private::Sealed, //
},
error::from_err_ptr,
prelude::*,
sync::aref::{
ARef,
AlwaysRefCounted, //
},
types::{
NotThreadSafe,
Opaque, //
},
workqueue::{
HasDelayedWork,
HasWork,
Work,
WorkItem, //
}, //
};
use core::{
alloc::Layout,
marker::PhantomData,
mem,
ops::Deref,
ptr::{
self,
NonNull, //
},
};
#[cfg(CONFIG_DRM_LEGACY)]
macro_rules! drm_legacy_fields {
( $($field:ident: $val:expr),* $(,)? ) => {
bindings::drm_driver {
$( $field: $val ),*,
firstopen: None,
preclose: None,
dma_ioctl: None,
dma_quiescent: None,
context_dtor: None,
irq_handler: None,
irq_preinstall: None,
irq_postinstall: None,
irq_uninstall: None,
get_vblank_counter: None,
enable_vblank: None,
disable_vblank: None,
dev_priv_size: 0,
}
}
}
#[cfg(not(CONFIG_DRM_LEGACY))]
macro_rules! drm_legacy_fields {
( $($field:ident: $val:expr),* $(,)? ) => {
bindings::drm_driver {
$( $field: $val ),*
}
}
}
/// A trait implemented by all possible contexts a [`Device`] can be used in.
///
/// Setting up a new [`Device`] is a multi-stage process. Each step of the process that a user
/// interacts with in Rust has a respective [`DeviceContext`] typestate. For example,
/// `Device<T, Registered>` would be a [`Device`] that reached the [`Registered`] [`DeviceContext`].
///
/// Each stage of this process is described below:
///
/// ```text
/// 1 2 3
/// +--------------+ +------------------+ +-----------------------+
/// |Device created| → |Device initialized| → |Registered w/ userspace|
/// +--------------+ +------------------+ +-----------------------+
/// (Uninit) (Registered)
/// ```
///
/// 1. The [`Device`] is in the [`Uninit`] context and is not guaranteed to be initialized or
/// registered with userspace. Only a limited subset of DRM core functionality is available.
/// 2. The [`Device`] is guaranteed to be fully initialized, but is not guaranteed to be registered
/// with userspace. All DRM core functionality which doesn't interact with userspace is
/// available. We currently don't have a context for representing this.
/// 3. The [`Device`] is guaranteed to be fully initialized, and is guaranteed to have been
/// registered with userspace at some point - thus putting it in the [`Registered`] context.
///
/// An important caveat of [`DeviceContext`] which must be kept in mind: when used as a typestate
/// for a reference type, it can only guarantee that a [`Device`] reached a particular stage in the
/// initialization process _at the time the reference was taken_. No guarantee is made in regards to
/// what stage of the process the [`Device`] is currently in. This means for instance that a
/// `&Device<T, Uninit>` may actually be registered with userspace, it just wasn't known to be
/// registered at the time the reference was taken.
pub trait DeviceContext: Sealed + Send + Sync {}
/// The [`DeviceContext`] of a [`Device`] that was registered with userspace at some point.
///
/// This represents a [`Device`] which is guaranteed to have been registered with userspace at
/// some point in time. Such a DRM device is guaranteed to have been fully-initialized.
///
/// Note: A device in this context is not guaranteed to remain registered with userspace for its
/// entire lifetime, as this is impossible to guarantee at compile-time.
///
/// # Invariants
///
/// A [`Device`] in this [`DeviceContext`] is guaranteed to have been registered with userspace
/// at some point in time.
pub struct Registered;
impl Sealed for Registered {}
impl DeviceContext for Registered {}
/// The [`DeviceContext`] of a [`Device`] that may be unregistered and partly uninitialized.
///
/// A [`Device`] in this context is only guaranteed to be partly initialized, and may or may not
/// be registered with userspace. Thus operations which depend on the [`Device`] being fully
/// initialized, or which depend on the [`Device`] being registered with userspace are not
/// available through this [`DeviceContext`].
///
/// A [`Device`] in this context can be used to create a
/// [`Registration`](drm::driver::Registration).
pub struct Uninit;
impl Sealed for Uninit {}
impl DeviceContext for Uninit {}
/// A [`Device`] which is known at compile-time to be unregistered with userspace.
///
/// This type allows performing operations which are only safe to do before userspace registration,
/// and can be used to create a [`Registration`](drm::driver::Registration) once the driver is ready
/// to register the device with userspace.
///
/// Since DRM device initialization must be single-threaded, this object is not thread-safe.
///
/// # Invariants
///
/// The device in `self.0` is guaranteed to be a newly created [`Device`] that has not yet been
/// registered with userspace until this type is dropped.
pub struct UnregisteredDevice<T: drm::Driver>(ARef<Device<T, Uninit>>, NotThreadSafe);
impl<T: drm::Driver> Deref for UnregisteredDevice<T> {
type Target = Device<T, Uninit>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: drm::Driver> UnregisteredDevice<T> {
const fn compute_features() -> u32 {
let mut features = drm::driver::FEAT_GEM;
if T::FEAT_RENDER {
features |= drm::driver::FEAT_RENDER;
}
features
}
const VTABLE: bindings::drm_driver = drm_legacy_fields! {
load: None,
open: Some(drm::File::<T::File>::open_callback),
postclose: Some(drm::File::<T::File>::postclose_callback),
unload: None,
release: Some(Device::<T>::release),
master_set: None,
master_drop: None,
debugfs_init: None,
// Ignore the Uninit DeviceContext below. It is only provided because it is required by the
// compiler, and it is not actually used by these functions.
gem_create_object: T::Object::<Uninit>::ALLOC_OPS.gem_create_object,
prime_handle_to_fd: T::Object::<Uninit>::ALLOC_OPS.prime_handle_to_fd,
prime_fd_to_handle: T::Object::<Uninit>::ALLOC_OPS.prime_fd_to_handle,
gem_prime_import: T::Object::<Uninit>::ALLOC_OPS.gem_prime_import,
gem_prime_import_sg_table: T::Object::<Uninit>::ALLOC_OPS.gem_prime_import_sg_table,
dumb_create: T::Object::<Uninit>::ALLOC_OPS.dumb_create,
dumb_map_offset: T::Object::<Uninit>::ALLOC_OPS.dumb_map_offset,
show_fdinfo: None,
fbdev_probe: None,
major: T::INFO.major,
minor: T::INFO.minor,
patchlevel: T::INFO.patchlevel,
name: crate::str::as_char_ptr_in_const_context(T::INFO.name).cast_mut(),
desc: crate::str::as_char_ptr_in_const_context(T::INFO.desc).cast_mut(),
driver_features: Self::compute_features(),
ioctls: T::IOCTLS.as_ptr(),
num_ioctls: T::IOCTLS.len() as i32,
fops: &Self::GEM_FOPS,
};
const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
/// Create a new `UnregisteredDevice` for a `drm::Driver`.
///
/// This can be used to create a [`Registration`](kernel::drm::Registration).
pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<Self> {
// `__drm_dev_alloc` uses `kmalloc()` to allocate memory, hence ensure a `kmalloc()`
// compatible `Layout`.
let layout = Kmalloc::aligned_layout(Layout::new::<Device<T, Uninit>>());
// Use a temporary vtable without a `release` callback until `data` is initialized, so
// init failure can release the DRM device without dropping uninitialized fields.
let alloc_vtable = bindings::drm_driver {
release: None,
..Self::VTABLE
};
// SAFETY:
// - `alloc_vtable` reference remains valid until no longer used,
// - `dev` is valid by its type invarants,
let raw_drm: *mut Device<T, Uninit> = unsafe {
bindings::__drm_dev_alloc(
dev.as_raw(),
&alloc_vtable,
layout.size(),
mem::offset_of!(Device<T, Uninit>, dev),
)
}
.cast();
let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?;
// SAFETY: `raw_drm` is a valid pointer to `Self`, given that `__drm_dev_alloc` was
// successful.
let drm_dev = unsafe { Device::into_drm_device(raw_drm) };
// SAFETY: `raw_drm` is a valid pointer to `Self`.
let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) };
// SAFETY:
// - `raw_data` is a valid pointer to uninitialized memory.
// - `raw_data` will not move until it is dropped.
unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| {
// SAFETY: `__drm_dev_alloc()` was successful, hence `drm_dev` must be valid and the
// refcount must be non-zero.
unsafe { bindings::drm_dev_put(drm_dev) };
})?;
// SAFETY: `drm_dev` is still private to this function.
unsafe { (*drm_dev).driver = const { &Self::VTABLE } };
// SAFETY: The reference count is one, and now we take ownership of that reference as a
// `drm::Device`.
// INVARIANT: We just created the device above, but have yet to call `drm_dev_register`.
// `Self` cannot be copied or sent to another thread - ensuring that `drm_dev_register`
// won't be called during its lifetime and that the device is unregistered.
Ok(Self(unsafe { ARef::from_raw(raw_drm) }, NotThreadSafe))
}
}
/// A typed DRM device with a specific [`drm::Driver`] implementation and [`DeviceContext`].
///
/// Since DRM devices can be used before being fully initialized and registered with userspace, `C`
/// represents the furthest [`DeviceContext`] we can guarantee that this [`Device`] has reached.
///
/// Keep in mind: this means that an unregistered device can still have the registration state
/// [`Registered`] as long as it was registered with userspace once in the past, and that the
/// behavior of such a device is still well-defined. Additionally, a device with the registration
/// state [`Uninit`] simply does not have a guaranteed registration state at compile time, and could
/// be either registered or unregistered. Since there is no way to guarantee a long-lived reference
/// to an unregistered device would remain unregistered, we do not provide a [`DeviceContext`] for
/// this.
///
/// # Invariants
///
/// * `self.dev` is a valid instance of a `struct device`.
/// * The data layout of `Self` remains the same across all implementations of `C`.
/// * Any invariants for `C` also apply.
#[repr(C)]
pub struct Device<T: drm::Driver, C: DeviceContext = Registered> {
dev: Opaque<bindings::drm_device>,
data: T::Data,
_ctx: PhantomData<C>,
}
impl<T: drm::Driver, C: DeviceContext> Device<T, C> {
pub(crate) fn as_raw(&self) -> *mut bindings::drm_device {
self.dev.get()
}
/// # Safety
///
/// `ptr` must be a valid pointer to a `struct device` embedded in `Self`.
unsafe fn from_drm_device(ptr: *const bindings::drm_device) -> *mut Self {
// SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a
// `struct drm_device` embedded in `Self`.
unsafe { crate::container_of!(Opaque::cast_from(ptr), Self, dev) }.cast_mut()
}
/// # Safety
///
/// `ptr` must be a valid pointer to `Self`.
unsafe fn into_drm_device(ptr: NonNull<Self>) -> *mut bindings::drm_device {
// SAFETY: By the safety requirements of this function, `ptr` is a valid pointer to `Self`.
unsafe { &raw mut (*ptr.as_ptr()).dev }.cast()
}
/// Not intended to be called externally, except via declare_drm_ioctls!()
///
/// # Safety
///
/// * Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
/// i.e. it must be ensured that the reference count of the C `struct drm_device` `ptr` points
/// to can't drop to zero, for the duration of this function call and the entire duration when
/// the returned reference exists.
/// * Additionally, callers must ensure that the `struct device`, `ptr` is pointing to, is
/// embedded in `Self`.
/// * Callers promise that any type invariants of `C` will be upheld.
#[doc(hidden)]
pub unsafe fn from_raw<'a>(ptr: *const bindings::drm_device) -> &'a Self {
// SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a
// `struct drm_device` embedded in `Self`.
let ptr = unsafe { Self::from_drm_device(ptr) };
// SAFETY: `ptr` is valid by the safety requirements of this function.
unsafe { &*ptr.cast() }
}
extern "C" fn release(ptr: *mut bindings::drm_device) {
// SAFETY: `ptr` is a valid pointer to a `struct drm_device` and embedded in `Self`.
let this = unsafe { Self::from_drm_device(ptr) };
// SAFETY:
// - When `release` runs it is guaranteed that there is no further access to `this`.
// - `this` is valid for dropping.
unsafe { core::ptr::drop_in_place(this) };
}
/// Change the [`DeviceContext`] for a [`Device`].
///
/// # Safety
///
/// The caller promises that `self` fulfills all of the guarantees provided by the given
/// [`DeviceContext`].
pub(crate) unsafe fn assume_ctx<NewCtx: DeviceContext>(&self) -> &Device<T, NewCtx> {
// SAFETY: The data layout is identical via our type invariants.
unsafe { mem::transmute(self) }
}
}
impl<T: drm::Driver, C: DeviceContext> Deref for Device<T, C> {
type Target = T::Data;
fn deref(&self) -> &Self::Target {
&self.data
}
}
// SAFETY: DRM device objects are always reference counted and the get/put functions
// satisfy the requirements.
unsafe impl<T: drm::Driver, C: DeviceContext> AlwaysRefCounted for Device<T, C> {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
unsafe { bindings::drm_dev_get(self.as_raw()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
// SAFETY: `obj` is a valid pointer to `Self`.
let drm_dev = unsafe { Self::into_drm_device(obj) };
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
unsafe { bindings::drm_dev_put(drm_dev) };
}
}
impl<T: drm::Driver, C: DeviceContext> AsRef<device::Device> for Device<T, C> {
fn as_ref(&self) -> &device::Device {
// SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid,
// which is guaranteed by the type invariant.
unsafe { device::Device::from_raw((*self.as_raw()).dev) }
}
}
// SAFETY: A `drm::Device` can be released from any thread.
unsafe impl<T: drm::Driver, C: DeviceContext> Send for Device<T, C> {}
// SAFETY: A `drm::Device` can be shared among threads because all immutable methods are protected
// by the synchronization in `struct drm_device`.
unsafe impl<T: drm::Driver, C: DeviceContext> Sync for Device<T, C> {}
impl<T, C, const ID: u64> WorkItem<ID> for Device<T, C>
where
T: drm::Driver,
T::Data: WorkItem<ID, Pointer = ARef<Self>>,
T::Data: HasWork<Self, ID>,
C: DeviceContext,
{
type Pointer = ARef<Self>;
fn run(ptr: ARef<Self>) {
T::Data::run(ptr);
}
}
// SAFETY:
//
// - `raw_get_work` and `work_container_of` return valid pointers by relying on
// `T::Data::raw_get_work` and `container_of`. In particular, `T::Data` is
// stored inline in `drm::Device`, so the `container_of` call is valid.
//
// - The two methods are true inverses of each other: given `ptr: *mut
// Device<T, C>`, `raw_get_work` will return a `*mut Work<Device<T, C>, ID>` through
// `T::Data::raw_get_work` and given a `ptr: *mut Work<Device<T, C>, ID>`,
// `work_container_of` will return a `*mut Device<T, C>` through `container_of`.
unsafe impl<T, C, const ID: u64> HasWork<Self, ID> for Device<T, C>
where
T: drm::Driver,
T::Data: HasWork<Self, ID>,
C: DeviceContext,
{
unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<Self, ID> {
// SAFETY: The caller promises that `ptr` points to a valid `Device<T, C>`.
let data_ptr = unsafe { &raw mut (*ptr).data };
// SAFETY: `data_ptr` is a valid pointer to `T::Data`.
unsafe { T::Data::raw_get_work(data_ptr) }
}
unsafe fn work_container_of(ptr: *mut Work<Self, ID>) -> *mut Self {
// SAFETY: The caller promises that `ptr` points at a `Work` field in
// `T::Data`.
let data_ptr = unsafe { T::Data::work_container_of(ptr) };
// SAFETY: `T::Data` is stored as the `data` field in `Device<T, C>`.
unsafe { crate::container_of!(data_ptr, Self, data) }
}
}
// SAFETY: Our `HasWork<T, ID>` implementation returns a `work_struct` that is
// stored in the `work` field of a `delayed_work` with the same access rules as
// the `work_struct` owing to the bound on `T::Data: HasDelayedWork<Device<T, C>,
// ID>`, which requires that `T::Data::raw_get_work` return a `work_struct` that
// is inside a `delayed_work`.
unsafe impl<T, C, const ID: u64> HasDelayedWork<Self, ID> for Device<T, C>
where
T: drm::Driver,
T::Data: HasDelayedWork<Self, ID>,
C: DeviceContext,
{
}
+203
View File
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
//! DRM driver core.
//!
//! C header: [`include/drm/drm_drv.h`](srctree/include/drm/drm_drv.h)
use crate::{
bindings,
device,
devres,
drm,
error::to_result,
prelude::*,
sync::aref::ARef, //
};
use core::{
mem,
ptr::NonNull, //
};
/// Driver use the GEM memory manager. This should be set for all modern drivers.
pub(crate) const FEAT_GEM: u32 = bindings::drm_driver_feature_DRIVER_GEM;
/// Driver supports render nodes, i.e.: /dev/dri/renderDXX devices.
pub(crate) const FEAT_RENDER: u32 = bindings::drm_driver_feature_DRIVER_RENDER;
/// Information data for a DRM Driver.
pub struct DriverInfo {
/// Driver major version.
pub major: i32,
/// Driver minor version.
pub minor: i32,
/// Driver patchlevel version.
pub patchlevel: i32,
/// Driver name.
pub name: &'static CStr,
/// Driver description.
pub desc: &'static CStr,
}
/// Internal memory management operation set, normally created by memory managers (e.g. GEM).
pub struct AllocOps {
pub(crate) gem_create_object: Option<
unsafe extern "C" fn(
dev: *mut bindings::drm_device,
size: usize,
) -> *mut bindings::drm_gem_object,
>,
pub(crate) prime_handle_to_fd: Option<
unsafe extern "C" fn(
dev: *mut bindings::drm_device,
file_priv: *mut bindings::drm_file,
handle: u32,
flags: u32,
prime_fd: *mut core::ffi::c_int,
) -> core::ffi::c_int,
>,
pub(crate) prime_fd_to_handle: Option<
unsafe extern "C" fn(
dev: *mut bindings::drm_device,
file_priv: *mut bindings::drm_file,
prime_fd: core::ffi::c_int,
handle: *mut u32,
) -> core::ffi::c_int,
>,
pub(crate) gem_prime_import: Option<
unsafe extern "C" fn(
dev: *mut bindings::drm_device,
dma_buf: *mut bindings::dma_buf,
) -> *mut bindings::drm_gem_object,
>,
pub(crate) gem_prime_import_sg_table: Option<
unsafe extern "C" fn(
dev: *mut bindings::drm_device,
attach: *mut bindings::dma_buf_attachment,
sgt: *mut bindings::sg_table,
) -> *mut bindings::drm_gem_object,
>,
pub(crate) dumb_create: Option<
unsafe extern "C" fn(
file_priv: *mut bindings::drm_file,
dev: *mut bindings::drm_device,
args: *mut bindings::drm_mode_create_dumb,
) -> core::ffi::c_int,
>,
pub(crate) dumb_map_offset: Option<
unsafe extern "C" fn(
file_priv: *mut bindings::drm_file,
dev: *mut bindings::drm_device,
handle: u32,
offset: *mut u64,
) -> core::ffi::c_int,
>,
}
/// Trait for memory manager implementations. Implemented internally.
pub trait AllocImpl: super::private::Sealed + drm::gem::IntoGEMObject {
/// The [`Driver`] implementation for this [`AllocImpl`].
type Driver: drm::Driver;
/// The C callback operations for this memory manager.
const ALLOC_OPS: AllocOps;
}
/// The DRM `Driver` trait.
///
/// This trait must be implemented by drivers in order to create a `struct drm_device` and `struct
/// drm_driver` to be registered in the DRM subsystem.
#[vtable]
pub trait Driver {
/// Context data associated with the DRM driver
type Data: Sync + Send;
/// The type used to manage memory for this driver.
type Object<Ctx: drm::DeviceContext>: AllocImpl;
/// The type used to represent a DRM File (client)
type File: drm::file::DriverFile;
/// Driver metadata
const INFO: DriverInfo;
/// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`.
const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor];
/// Sets the `DRIVER_RENDER` feature for this driver.
///
/// When enabled, the driver exposes `/dev/dri/renderDXX` render nodes to
/// userspace. The render node is an alternate low-priviledge way to access
/// the driver, which is enforced on a per-ioctl level. Userspace processes
/// that open the render node can only invoke ioctls explicitly listed as
/// usable from the render node (i.e. marked DRM_RENDER_ALLOW), whereas
/// userspace processes using the master node can invoke any ioctl.
const FEAT_RENDER: bool = false;
}
/// The registration type of a `drm::Device`.
///
/// Once the `Registration` structure is dropped, the device is unregistered.
pub struct Registration<T: Driver>(ARef<drm::Device<T>>);
impl<T: Driver> Registration<T> {
fn new(drm: drm::UnregisteredDevice<T>, flags: usize) -> Result<Self> {
// SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Device`.
to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?;
// SAFETY: We just called `drm_dev_register` above
let new = NonNull::from(unsafe { drm.assume_ctx() });
// Leak the ARef from UnregisteredDevice in preparation for transferring its ownership.
mem::forget(drm);
// SAFETY: `drm`'s `Drop` constructor was never called, ensuring that there remains at least
// one reference to the device - which we take ownership over here.
let new = unsafe { ARef::from_raw(new) };
Ok(Self(new))
}
/// Registers a new [`UnregisteredDevice`](drm::UnregisteredDevice) with userspace.
///
/// Ownership of the [`Registration`] object is passed to [`devres::register`].
pub fn new_foreign_owned<'a>(
drm: drm::UnregisteredDevice<T>,
dev: &'a device::Device<device::Bound>,
flags: usize,
) -> Result<&'a drm::Device<T>>
where
T: 'static,
{
if drm.as_ref().as_raw() != dev.as_raw() {
return Err(EINVAL);
}
let reg = Registration::<T>::new(drm, flags)?;
let drm = NonNull::from(reg.device());
devres::register(dev, reg, GFP_KERNEL)?;
// SAFETY: Since `reg` was passed to devres::register(), the device now owns the lifetime
// of the DRM registration - ensuring that this references lives for at least as long as 'a.
Ok(unsafe { drm.as_ref() })
}
/// Returns a reference to the `Device` instance for this registration.
pub fn device(&self) -> &drm::Device<T> {
&self.0
}
}
// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between
// threads, hence it's safe to share it.
unsafe impl<T: Driver> Sync for Registration<T> {}
// SAFETY: Registration with and unregistration from the DRM subsystem can happen from any thread.
unsafe impl<T: Driver> Send for Registration<T> {}
impl<T: Driver> Drop for Registration<T> {
fn drop(&mut self) {
// SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
// `Registration` also guarantees the this `drm::Device` is actually registered.
unsafe { bindings::drm_dev_unregister(self.0.as_raw()) };
}
}
+103
View File
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
//! DRM File objects.
//!
//! C header: [`include/drm/drm_file.h`](srctree/include/drm/drm_file.h)
use crate::{
bindings,
drm,
prelude::*,
types::Opaque, //
};
use core::marker::PhantomData;
/// Trait that must be implemented by DRM drivers to represent a DRM File (a client instance).
pub trait DriverFile {
/// The parent `Driver` implementation for this `DriverFile`.
type Driver: drm::Driver;
/// Open a new file (called when a client opens the DRM device).
fn open(device: &drm::Device<Self::Driver>) -> Result<Pin<KBox<Self>>>;
}
/// An open DRM File.
///
/// # Invariants
///
/// `self.0` is a valid instance of a `struct drm_file`.
#[repr(transparent)]
pub struct File<T: DriverFile>(Opaque<bindings::drm_file>, PhantomData<T>);
impl<T: DriverFile> File<T> {
#[doc(hidden)]
/// Not intended to be called externally, except via declare_drm_ioctls!()
///
/// # Safety
///
/// `raw_file` must be a valid pointer to an open `struct drm_file`, opened through `T::open`.
pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_file) -> &'a File<T> {
// SAFETY: `raw_file` is valid by the safety requirements of this function.
unsafe { &*ptr.cast() }
}
pub(super) fn as_raw(&self) -> *mut bindings::drm_file {
self.0.get()
}
fn driver_priv(&self) -> *mut T {
// SAFETY: By the type invariants of `Self`, `self.as_raw()` is always valid.
unsafe { (*self.as_raw()).driver_priv }.cast()
}
/// Return a pinned reference to the driver file structure.
pub fn inner(&self) -> Pin<&T> {
// SAFETY: By the type invariant the pointer `self.as_raw()` points to a valid and opened
// `struct drm_file`, hence `driver_priv` has been properly initialized by `open_callback`.
unsafe { Pin::new_unchecked(&*(self.driver_priv())) }
}
/// The open callback of a `struct drm_file`.
pub(crate) extern "C" fn open_callback(
raw_dev: *mut bindings::drm_device,
raw_file: *mut bindings::drm_file,
) -> core::ffi::c_int {
// SAFETY: A callback from `struct drm_driver::open` guarantees that
// - `raw_dev` is valid pointer to a `struct drm_device`,
// - the corresponding `struct drm_device` has been registered.
let drm = unsafe { drm::Device::from_raw(raw_dev) };
// SAFETY: `raw_file` is a valid pointer to a `struct drm_file`.
let file = unsafe { File::<T>::from_raw(raw_file) };
let inner = match T::open(drm) {
Err(e) => {
return e.to_errno();
}
Ok(i) => i,
};
// SAFETY: This pointer is treated as pinned, and the Drop guarantee is upheld in
// `postclose_callback()`.
let driver_priv = KBox::into_raw(unsafe { Pin::into_inner_unchecked(inner) });
// SAFETY: By the type invariants of `Self`, `self.as_raw()` is always valid.
unsafe { (*file.as_raw()).driver_priv = driver_priv.cast() };
0
}
/// The postclose callback of a `struct drm_file`.
pub(crate) extern "C" fn postclose_callback(
_raw_dev: *mut bindings::drm_device,
raw_file: *mut bindings::drm_file,
) {
// SAFETY: This reference won't escape this function
let file = unsafe { File::<T>::from_raw(raw_file) };
// SAFETY: `file.driver_priv` has been created in `open_callback` through `KBox::into_raw`.
let _ = unsafe { KBox::from_raw(file.driver_priv()) };
}
}
impl<T: DriverFile> super::private::Sealed for File<T> {}
+408
View File
@@ -0,0 +1,408 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
//! DRM GEM API
//!
//! C header: [`include/drm/drm_gem.h`](srctree/include/drm/drm_gem.h)
use crate::{
bindings,
drm::{
self,
device::{
DeviceContext,
Registered, //
},
driver::{
AllocImpl,
AllocOps, //
},
},
error::to_result,
prelude::*,
sync::aref::{
ARef,
AlwaysRefCounted, //
},
types::Opaque,
};
use core::{
marker::PhantomData,
ops::Deref,
ptr::NonNull, //
};
#[cfg(CONFIG_RUST_DRM_GEM_SHMEM_HELPER)]
pub mod shmem;
/// A macro for implementing [`AlwaysRefCounted`] for any GEM object type.
///
/// Since all GEM objects use the same refcounting scheme.
#[macro_export]
macro_rules! impl_aref_for_gem_obj {
(
impl $( <$( $tparam_id:ident ),+> )? for $type:ty
$(
where
$( $bind_param:path : $bind_trait:path ),+
)?
) => {
// SAFETY: All GEM objects are refcounted.
unsafe impl $( <$( $tparam_id ),+> )? $crate::sync::aref::AlwaysRefCounted for $type
where
Self: IntoGEMObject,
$( $( $bind_param : $bind_trait ),+ )?
{
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: core::ptr::NonNull<Self>) {
// SAFETY: `obj` is a valid pointer to an `Object<T>`.
let obj = unsafe { obj.as_ref() }.as_raw();
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
unsafe { bindings::drm_gem_object_put(obj) };
}
}
};
}
#[cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), allow(unused))]
pub(crate) use impl_aref_for_gem_obj;
/// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementation from its
/// [`DriverObject`] implementation.
///
/// [`Driver`]: drm::Driver
/// [`DriverFile`]: drm::file::DriverFile
pub type DriverFile<T> = drm::File<<<T as DriverObject>::Driver as drm::Driver>::File>;
/// A type alias for retrieving the current [`AllocImpl`] for a given [`DriverObject`].
///
/// [`Driver`]: drm::Driver
pub type DriverAllocImpl<T, Ctx = Registered> =
<<T as DriverObject>::Driver as drm::Driver>::Object<Ctx>;
/// GEM object functions, which must be implemented by drivers.
pub trait DriverObject: Sync + Send + Sized {
/// Parent `Driver` for this object.
type Driver: drm::Driver;
/// The data type to use for passing arguments to [`DriverObject::new`].
type Args;
/// Create a new driver data object for a GEM object of a given size.
fn new<Ctx: DeviceContext>(
dev: &drm::Device<Self::Driver, Ctx>,
size: usize,
args: Self::Args,
) -> impl PinInit<Self, Error>;
/// Open a new handle to an existing object, associated with a File.
fn open(_obj: &DriverAllocImpl<Self>, _file: &DriverFile<Self>) -> Result {
Ok(())
}
/// Close a handle to an existing object, associated with a File.
fn close(_obj: &DriverAllocImpl<Self>, _file: &DriverFile<Self>) {}
}
/// Trait that represents a GEM object subtype
pub trait IntoGEMObject: Sized + super::private::Sealed + AlwaysRefCounted {
/// Returns a reference to the raw `drm_gem_object` structure, which must be valid as long as
/// this owning object is valid.
fn as_raw(&self) -> *mut bindings::drm_gem_object;
/// Converts a pointer to a `struct drm_gem_object` into a reference to `Self`.
///
/// # Safety
///
/// - `self_ptr` must be a valid pointer to `Self`.
/// - The caller promises that holding the immutable reference returned by this function does
/// not violate rust's data aliasing rules and remains valid throughout the lifetime of `'a`.
unsafe fn from_raw<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self;
}
extern "C" fn open_callback<T: DriverObject>(
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 { DriverFile::<T>::from_raw(raw_file) };
// SAFETY:
// * `open_callback` is specified in the AllocOps structure for `DriverObject`, ensuring that
// `raw_obj` is contained within a `DriverAllocImpl<T>`
// * It is only possible for `open_callback` to be called after device registration, ensuring
// that the object's device is in the `Registered` state.
let obj: &DriverAllocImpl<T> = unsafe { IntoGEMObject::from_raw(raw_obj) };
match T::open(obj, file) {
Err(e) => e.to_errno(),
Ok(()) => 0,
}
}
extern "C" fn close_callback<T: DriverObject>(
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 { DriverFile::<T>::from_raw(raw_file) };
// SAFETY: `close_callback` is specified in the AllocOps structure for `Object<T>`, ensuring
// that `raw_obj` is indeed contained within a `Object<T>`.
let obj: &DriverAllocImpl<T> = unsafe { IntoGEMObject::from_raw(raw_obj) };
T::close(obj, file);
}
impl<T: DriverObject, Ctx: DeviceContext> IntoGEMObject for Object<T, Ctx> {
fn as_raw(&self) -> *mut bindings::drm_gem_object {
self.obj.get()
}
unsafe fn from_raw<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self {
// SAFETY: `obj` is guaranteed to be in an `Object<T>` via the safety contract of this
// function
unsafe { &*crate::container_of!(Opaque::cast_from(self_ptr), Object<T, Ctx>, obj) }
}
}
/// Base operations shared by all GEM object classes
pub trait BaseObject: IntoGEMObject {
/// Returns the size of the object in bytes.
fn size(&self) -> usize {
// SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `struct drm_gem_object`.
unsafe { (*self.as_raw()).size }
}
/// Creates a new handle for the object associated with a given `File`
/// (or returns an existing one).
fn create_handle<D, F>(&self, file: &drm::File<F>) -> Result<u32>
where
Self: AllocImpl<Driver = D>,
D: drm::Driver<Object<Registered> = Self, File = F>,
F: drm::file::DriverFile<Driver = D>,
{
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.as_raw(), &mut handle)
})?;
Ok(handle)
}
/// Looks up an object by its handle for a given `File`.
fn lookup_handle<D, F>(file: &drm::File<F>, handle: u32) -> Result<ARef<Self>>
where
Self: AllocImpl<Driver = D>,
D: drm::Driver<Object<Registered> = Self, File = F>,
F: drm::file::DriverFile<Driver = D>,
{
// SAFETY: The arguments are all valid per the type invariants.
let ptr = unsafe { bindings::drm_gem_object_lookup(file.as_raw().cast(), handle) };
if ptr.is_null() {
return Err(ENOENT);
}
// SAFETY:
// - A `drm::Driver` can only have a single `File` implementation.
// - `file` uses the same `drm::Driver` as `Self`.
// - Therefore, we're guaranteed that `ptr` must be a gem object embedded within `Self`.
// - And we check if the pointer is null befoe calling from_raw(), ensuring that `ptr` is a
// valid pointer to an initialized `Self`.
let obj = unsafe { Self::from_raw(ptr) };
// SAFETY:
// - We take ownership of the reference of `drm_gem_object_lookup()`.
// - Our `NonNull` comes from an immutable reference, thus ensuring it is a valid pointer to
// `Self`.
Ok(unsafe { ARef::from_raw(obj.into()) })
}
/// 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.as_raw()) })?;
// SAFETY: The arguments are valid per the type invariant.
Ok(unsafe { bindings::drm_vma_node_offset_addr(&raw mut (*self.as_raw()).vma_node) })
}
}
impl<T: IntoGEMObject> BaseObject for T {}
/// Crate-private base operations shared by all GEM object classes.
#[cfg_attr(not(CONFIG_RUST_DRM_GEM_SHMEM_HELPER), expect(unused))]
pub(crate) trait BaseObjectPrivate: IntoGEMObject {
/// Return a pointer to this object's dma_resv.
fn raw_dma_resv(&self) -> *mut bindings::dma_resv {
// SAFETY: `self.as_raw()` always returns a valid pointer to the base DRM GEM object.
unsafe { (*self.as_raw()).resv }
}
}
impl<T: IntoGEMObject> BaseObjectPrivate for T {}
/// A base GEM object.
///
/// # Invariants
///
/// * `self.obj` is a valid instance of a `struct drm_gem_object`.
/// * Any type invariants of `Ctx` apply to the parent DRM device for this GEM object.
#[repr(C)]
#[pin_data]
pub struct Object<T: DriverObject + Send + Sync, Ctx: DeviceContext = Registered> {
obj: Opaque<bindings::drm_gem_object>,
#[pin]
data: T,
_ctx: PhantomData<Ctx>,
}
impl<T: DriverObject, Ctx: DeviceContext> Object<T, Ctx> {
const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs {
free: Some(Self::free_callback),
open: Some(open_callback::<T>),
close: Some(close_callback::<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, Ctx>,
size: usize,
args: T::Args,
) -> 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, args),
_ctx: PhantomData,
}),
GFP_KERNEL,
)?;
// SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above.
unsafe { (*obj.as_raw()).funcs = &Self::OBJECT_FUNCS };
// INVARIANT: `dev` and the GEM object are in the same state at the moment, and upgrading
// the typestate in `dev` will not carry over to the GEM object.
if let Err(err) =
// 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: `drm_gem_object_init()` initializes the private GEM object state before
// failing, so `drm_gem_private_object_fini()` is the matching cleanup.
unsafe { bindings::drm_gem_private_object_fini(obj.obj.get()) };
return Err(err);
}
// SAFETY: We will never move out of `Self` as `ARef<Self>` is always treated as pinned.
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, Ctx> {
// SAFETY:
// - `struct drm_gem_object.dev` is initialized and valid for as long as the GEM
// object lives.
// - The device we used for creating the gem object is passed as &drm::Device<T::Driver> to
// Object::<T>::new(), so we know that `T::Driver` is the right generic parameter to use
// here.
// - Any type invariants of `Ctx` are upheld by using the same `Ctx` for the `Device` we
// return.
unsafe { drm::Device::from_raw((*self.as_raw()).dev) }
}
fn as_raw(&self) -> *mut bindings::drm_gem_object {
self.obj.get()
}
extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
let ptr: *mut Opaque<bindings::drm_gem_object> = obj.cast();
// SAFETY: All of our objects are of type `Object<T>`.
let this = unsafe { crate::container_of!(ptr, Self, obj) };
// 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) };
}
}
impl_aref_for_gem_obj! {
impl<T, C> for Object<T, C>
where
T: DriverObject,
C: DeviceContext
}
impl<T: DriverObject, Ctx: DeviceContext> super::private::Sealed for Object<T, Ctx> {}
impl<T: DriverObject, Ctx: DeviceContext> Deref for Object<T, Ctx> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: DriverObject, Ctx: DeviceContext> AllocImpl for Object<T, Ctx> {
type Driver = T::Driver;
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 {
let mut fops: bindings::file_operations = pin_init::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
}
+237
View File
@@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0
//! DRM GEM shmem helper objects
//!
//! C header: [`include/linux/drm/drm_gem_shmem_helper.h`](srctree/include/drm/drm_gem_shmem_helper.h)
// TODO:
// - There are a number of spots here that manually acquire/release the DMA reservation lock using
// dma_resv_(un)lock(). In the future we should add support for ww mutex, expose a method to
// acquire a reference to the WwMutex, and then use that directly instead of the C functions here.
use crate::{
container_of,
drm::{
driver,
gem,
private::Sealed,
Device,
DeviceContext,
Registered, //
},
error::to_result,
prelude::*,
sync::aref::ARef,
types::Opaque, //
};
use core::{
marker::PhantomData,
ops::{
Deref,
DerefMut, //
},
ptr::NonNull, //
};
use gem::{
BaseObjectPrivate,
DriverObject,
IntoGEMObject, //
};
/// A struct for controlling the creation of shmem-backed GEM objects.
///
/// This is used with [`Object::new()`] to control various properties that can only be set when
/// initially creating a shmem-backed GEM object.
#[derive(Default)]
pub struct ObjectConfig<'a, T: DriverObject, C: DeviceContext = Registered> {
/// Whether to set the write-combine map flag.
pub map_wc: bool,
/// Reuse the DMA reservation from another GEM object.
///
/// The newly created [`Object`] will hold an owned refcount to `parent_resv_obj` if specified.
pub parent_resv_obj: Option<&'a Object<T, C>>,
}
/// A shmem-backed GEM object.
///
/// # Invariants
///
/// - `obj` contains a valid initialized `struct drm_gem_shmem_object` for the lifetime of this
/// object.
/// - Any type invariants of `C` apply to the parent DRM device for this GEM object.
#[repr(C)]
#[pin_data]
pub struct Object<T: DriverObject, C: DeviceContext = Registered> {
#[pin]
obj: Opaque<bindings::drm_gem_shmem_object>,
/// Parent object that owns this object's DMA reservation object.
parent_resv_obj: Option<ARef<Object<T, C>>>,
#[pin]
inner: T,
_ctx: PhantomData<C>,
}
super::impl_aref_for_gem_obj! {
impl<T, C> for Object<T, C>
where
T: DriverObject,
C: DeviceContext
}
// SAFETY: All GEM objects are thread-safe.
unsafe impl<T: DriverObject, C: DeviceContext> Send for Object<T, C> {}
// SAFETY: All GEM objects are thread-safe.
unsafe impl<T: DriverObject, C: DeviceContext> Sync for Object<T, C> {}
impl<T: DriverObject, C: DeviceContext> Object<T, C> {
/// `drm_gem_object_funcs` vtable suitable for GEM shmem objects.
const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs {
free: Some(Self::free_callback),
open: Some(super::open_callback::<T>),
close: Some(super::close_callback::<T>),
print_info: Some(bindings::drm_gem_shmem_object_print_info),
export: None,
pin: Some(bindings::drm_gem_shmem_object_pin),
unpin: Some(bindings::drm_gem_shmem_object_unpin),
get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table),
vmap: Some(bindings::drm_gem_shmem_object_vmap),
vunmap: Some(bindings::drm_gem_shmem_object_vunmap),
mmap: Some(bindings::drm_gem_shmem_object_mmap),
status: None,
rss: None,
#[allow(unused_unsafe, reason = "Safe since Rust 1.82.0")]
// SAFETY: `drm_gem_shmem_vm_ops` is a valid, static const on the C side.
vm_ops: unsafe { &raw const bindings::drm_gem_shmem_vm_ops },
evict: None,
};
/// Return a raw pointer to the embedded drm_gem_shmem_object.
fn as_raw_shmem(&self) -> *mut bindings::drm_gem_shmem_object {
self.obj.get()
}
/// Create a new shmem-backed DRM object of the given size.
///
/// Additional config options can be specified using `config`.
pub fn new(
dev: &Device<T::Driver, C>,
size: usize,
config: ObjectConfig<'_, T, C>,
args: T::Args,
) -> Result<ARef<Self>> {
let new: Pin<KBox<Self>> = KBox::try_pin_init(
try_pin_init!(Self {
obj <- Opaque::init_zeroed(),
parent_resv_obj: config.parent_resv_obj.map(|p| p.into()),
inner <- T::new(dev, size, args),
_ctx: PhantomData::<C>,
}),
GFP_KERNEL,
)?;
// SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above.
unsafe { (*new.as_raw()).funcs = &Self::VTABLE };
// SAFETY: The arguments are all valid via the type invariants.
to_result(unsafe { bindings::drm_gem_shmem_init(dev.as_raw(), new.as_raw_shmem(), size) })?;
// SAFETY: We never move out of `self`.
let new = KBox::into_raw(unsafe { Pin::into_inner_unchecked(new) });
// SAFETY: We're taking over the owned refcount from `drm_gem_shmem_init`.
let obj = unsafe { ARef::from_raw(NonNull::new_unchecked(new)) };
// Start filling out values from `config`
if let Some(parent_resv) = config.parent_resv_obj {
// SAFETY: We have yet to expose the new gem object outside of this function, so it is
// safe to modify this field.
unsafe { (*obj.obj.get()).base.resv = parent_resv.raw_dma_resv() };
}
// SAFETY: We have yet to expose this object outside of this function, so we're guaranteed
// to have exclusive access - thus making this safe to hold a mutable reference to.
let shmem = unsafe { &mut *obj.as_raw_shmem() };
shmem.set_map_wc(config.map_wc);
Ok(obj)
}
/// Returns the `Device` that owns this GEM object.
pub fn dev(&self) -> &Device<T::Driver, C> {
// SAFETY: `dev` will have been initialized in `Self::new()` by `drm_gem_shmem_init()`.
unsafe { Device::from_raw((*self.as_raw()).dev) }
}
extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
// SAFETY:
// - DRM always passes a valid gem object here
// - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that
// `obj` is contained within a drm_gem_shmem_object
let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) };
// SAFETY:
// - We're in free_callback - so this function is safe to call.
// - We won't be using the gem resources on `this` after this call.
unsafe { bindings::drm_gem_shmem_release(this) };
// SAFETY:
// - We verified above that `obj` is valid, which makes `this` valid
// - This function is set in AllocOps, so we know that `this` is contained within a
// `Object<T, C>`
let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut();
// SAFETY: We're recovering the Kbox<> we created in gem_create_object()
let _ = unsafe { KBox::from_raw(this) };
}
}
impl<T: DriverObject, C: DeviceContext> Deref for Object<T, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: DriverObject, C: DeviceContext> DerefMut for Object<T, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: DriverObject, C: DeviceContext> Sealed for Object<T, C> {}
impl<T: DriverObject, C: DeviceContext> gem::IntoGEMObject for Object<T, C> {
fn as_raw(&self) -> *mut bindings::drm_gem_object {
// SAFETY:
// - Our immutable reference is proof that this is safe to dereference.
// - `obj` is always a valid drm_gem_shmem_object via our type invariants.
unsafe { &raw mut (*self.obj.get()).base }
}
unsafe fn from_raw<'a>(obj: *mut bindings::drm_gem_object) -> &'a Self {
// SAFETY: The safety contract of from_gem_obj() guarantees that `obj` is contained within
// `Self`
unsafe {
let obj = Opaque::cast_from(container_of!(obj, bindings::drm_gem_shmem_object, base));
&*container_of!(obj, Self, obj)
}
}
}
impl<T: DriverObject, C: DeviceContext> driver::AllocImpl for Object<T, C> {
type Driver = T::Driver;
const ALLOC_OPS: driver::AllocOps = driver::AllocOps {
gem_create_object: None,
prime_handle_to_fd: None,
prime_fd_to_handle: None,
gem_prime_import: None,
gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table),
dumb_create: Some(bindings::drm_gem_shmem_dumb_create),
dumb_map_offset: None,
};
}
+328
View File
@@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
#![cfg(CONFIG_RUST_DRM_GPUVM)]
//! DRM GPUVM in immediate mode
//!
//! Rust abstractions for using GPUVM in immediate mode. This is when the GPUVM state is updated
//! during `run_job()`, i.e., in the DMA fence signalling critical path, to ensure that the GPUVM
//! and the GPU's virtual address space has the same state at all times.
//!
//! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h)
use kernel::{
alloc::{
AllocError,
Flags as AllocFlags, //
},
bindings,
drm,
drm::gem::IntoGEMObject,
error::to_result,
prelude::*,
sync::aref::{
ARef,
AlwaysRefCounted, //
},
types::Opaque, //
};
use core::{
cell::UnsafeCell,
marker::PhantomData,
mem::{
ManuallyDrop,
MaybeUninit, //
},
ops::{
Deref,
DerefMut,
Range, //
},
ptr::{
self,
NonNull, //
}, //
};
mod sm_ops;
pub use self::sm_ops::*;
mod vm_bo;
pub use self::vm_bo::*;
mod va;
pub use self::va::*;
/// A DRM GPU VA manager.
///
/// This object is refcounted, but the locations of mapped ranges may only be accessed or changed
/// via the special unique handle [`UniqueRefGpuVm`].
///
/// # Invariants
///
/// * Stored in an allocation managed by the refcount in `self.vm`.
/// * Access to `data` and the gpuvm interval tree is controlled via the [`UniqueRefGpuVm`] type.
/// * Does not contain any sparse [`GpuVa<T>`] instances.
#[pin_data]
pub struct GpuVm<T: DriverGpuVm> {
#[pin]
vm: Opaque<bindings::drm_gpuvm>,
/// Accessed only through the [`UniqueRefGpuVm`] reference.
data: UnsafeCell<T>,
}
// SAFETY: The GPUVM api does not assume that it is tied to a specific thread. The destructor will
// drop the `data` field, which is okay because it is guaranteed `Send` by the `DriverGpuVm` trait.
unsafe impl<T: DriverGpuVm> Send for GpuVm<T> {}
// SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel.
unsafe impl<T: DriverGpuVm> Sync for GpuVm<T> {}
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVm<T> {
fn inc_ref(&self) {
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
unsafe { bindings::drm_gpuvm_get(self.vm.get()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
unsafe { bindings::drm_gpuvm_put((*obj.as_ptr()).vm.get()) };
}
}
impl<T: DriverGpuVm> PartialEq for GpuVm<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.as_raw(), other.as_raw())
}
}
impl<T: DriverGpuVm> Eq for GpuVm<T> {}
impl<T: DriverGpuVm> GpuVm<T> {
const fn vtable() -> &'static bindings::drm_gpuvm_ops {
&bindings::drm_gpuvm_ops {
vm_free: Some(Self::vm_free),
op_alloc: None,
op_free: None,
vm_bo_alloc: GpuVmBo::<T>::ALLOC_FN,
vm_bo_free: GpuVmBo::<T>::FREE_FN,
vm_bo_validate: None,
sm_step_map: Some(Self::sm_step_map),
sm_step_unmap: Some(Self::sm_step_unmap),
sm_step_remap: Some(Self::sm_step_remap),
}
}
/// Creates a GPUVM instance.
#[expect(clippy::new_ret_no_self)]
pub fn new<E>(
name: &'static CStr,
dev: &drm::Device<T::Driver>,
r_obj: &T::Object,
range: Range<u64>,
reserve_range: Range<u64>,
data: T,
) -> Result<UniqueRefGpuVm<T>, E>
where
E: From<AllocError>,
E: From<core::convert::Infallible>,
{
let obj = KBox::try_pin_init::<E>(
try_pin_init!(Self {
data: UnsafeCell::new(data),
vm <- Opaque::ffi_init(|vm| {
// SAFETY: These arguments are valid. `vm` is valid until refcount drops to
// zero. The `vm` is zeroed before calling this method by `__GFP_ZERO` flag
// below.
unsafe {
bindings::drm_gpuvm_init(
vm,
name.as_char_ptr(),
bindings::drm_gpuvm_flags_DRM_GPUVM_IMMEDIATE_MODE
| bindings::drm_gpuvm_flags_DRM_GPUVM_RESV_PROTECTED,
dev.as_raw(),
r_obj.as_raw(),
range.start,
range.end - range.start,
reserve_range.start,
reserve_range.end - reserve_range.start,
const { Self::vtable() },
)
}
}),
}? E),
GFP_KERNEL | __GFP_ZERO,
)?;
// SAFETY: This transfers the initial refcount to the ARef.
let aref = unsafe {
ARef::from_raw(NonNull::new_unchecked(KBox::into_raw(
Pin::into_inner_unchecked(obj),
)))
};
// INVARIANT: This reference is unique.
Ok(UniqueRefGpuVm(aref))
}
/// Access this [`GpuVm`] from a raw pointer.
///
/// # Safety
///
/// The pointer must reference the `struct drm_gpuvm` in a valid [`GpuVm<T>`] that remains
/// valid for at least `'a`.
#[inline]
pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm) -> &'a Self {
// SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. Caller ensures the
// pointer is valid for 'a.
unsafe { &*kernel::container_of!(Opaque::cast_from(ptr), Self, vm) }
}
/// Returns a raw pointer to the embedded `struct drm_gpuvm`.
#[inline]
pub fn as_raw(&self) -> *mut bindings::drm_gpuvm {
self.vm.get()
}
/// The start of the VA space.
#[inline]
pub fn va_start(&self) -> u64 {
// SAFETY: The `mm_start` field is immutable.
unsafe { (*self.as_raw()).mm_start }
}
/// The length of the GPU's virtual address space.
#[inline]
pub fn va_length(&self) -> u64 {
// SAFETY: The `mm_range` field is immutable.
unsafe { (*self.as_raw()).mm_range }
}
/// Returns the range of the GPU virtual address space.
#[inline]
pub fn va_range(&self) -> Range<u64> {
let start = self.va_start();
// OVERFLOW: This reconstructs the Range<u64> passed to the constructor, so it won't fail.
let end = start + self.va_length();
Range { start, end }
}
/// Get or create the [`GpuVmBo`] for this gem object.
#[inline]
pub fn obtain(
&self,
obj: &T::Object,
data: impl PinInit<T::VmBoData>,
) -> Result<ARef<GpuVmBo<T>>, AllocError> {
Ok(GpuVmBoAlloc::new(self, obj, data)?.obtain())
}
/// Clean up buffer objects that are no longer used.
#[inline]
pub fn deferred_cleanup(&self) {
// SAFETY: This GPUVM uses immediate mode.
unsafe { bindings::drm_gpuvm_bo_deferred_cleanup(self.as_raw()) }
}
/// Check if this GEM object is an external object for this GPUVM.
#[inline]
pub fn is_extobj(&self, obj: &T::Object) -> bool {
// SAFETY: We may call this with any GPUVM and GEM object.
unsafe { bindings::drm_gpuvm_is_extobj(self.as_raw(), obj.as_raw()) }
}
/// Free this GPUVM.
///
/// # Safety
///
/// Called when refcount hits zero.
unsafe extern "C" fn vm_free(me: *mut bindings::drm_gpuvm) {
// SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`.
let me = unsafe { kernel::container_of!(Opaque::cast_from(me), Self, vm).cast_mut() };
// SAFETY: By type invariants we can free it when refcount hits zero.
drop(unsafe { KBox::from_raw(me) })
}
#[inline]
fn raw_resv(&self) -> *mut bindings::dma_resv {
// SAFETY: `r_obj` is immutable and valid for duration of GPUVM.
unsafe { (*(*self.as_raw()).r_obj).resv }
}
}
/// The manager for a GPUVM.
pub trait DriverGpuVm: Sized + Send {
/// Parent `Driver` for this object.
type Driver: drm::Driver<Object = Self::Object>;
/// The kind of GEM object stored in this GPUVM.
type Object: IntoGEMObject;
/// Data stored with each [`struct drm_gpuva`](struct@GpuVa).
type VaData;
/// Data stored with each [`struct drm_gpuvm_bo`](struct@GpuVmBo).
type VmBoData;
/// The private data passed to callbacks.
type SmContext<'ctx>;
/// Indicates that a new mapping should be created.
fn sm_step_map<'op, 'ctx>(
&mut self,
op: OpMap<'op, Self>,
context: &mut Self::SmContext<'ctx>,
) -> Result<OpMapped<'op, Self>, Error>;
/// Indicates that an existing mapping should be removed.
fn sm_step_unmap<'op, 'ctx>(
&mut self,
op: OpUnmap<'op, Self>,
context: &mut Self::SmContext<'ctx>,
) -> Result<OpUnmapped<'op, Self>, Error>;
/// Indicates that an existing mapping should be split up.
fn sm_step_remap<'op, 'ctx>(
&mut self,
op: OpRemap<'op, Self>,
context: &mut Self::SmContext<'ctx>,
) -> Result<OpRemapped<'op, Self>, Error>;
}
/// The core of the DRM GPU VA manager.
///
/// This object is a unique reference to the VM that can access the interval tree and the Rust
/// `data` field.
///
/// # Invariants
///
/// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference.
pub struct UniqueRefGpuVm<T: DriverGpuVm>(ARef<GpuVm<T>>);
// SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel, and
// concurrent access to `data` is safe due to the `T: Sync` requirement.
unsafe impl<T: DriverGpuVm + Sync> Sync for UniqueRefGpuVm<T> {}
impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
/// Access the data owned by this `UniqueRefGpuVm` immutably.
#[inline]
pub fn data_ref(&self) -> &T {
// SAFETY: By the type invariants we may access `data`.
unsafe { &*self.0.data.get() }
}
/// Access the data owned by this `UniqueRefGpuVm` mutably.
#[inline]
pub fn data(&mut self) -> &mut T {
// SAFETY: By the type invariants we may access `data`.
unsafe { &mut *self.0.data.get() }
}
}
impl<T: DriverGpuVm> Deref for UniqueRefGpuVm<T> {
type Target = GpuVm<T>;
#[inline]
fn deref(&self) -> &GpuVm<T> {
&self.0
}
}
+429
View File
@@ -0,0 +1,429 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
use super::*;
/// The actual data that gets threaded through the callbacks.
struct SmData<'a, 'ctx, T: DriverGpuVm> {
gpuvm: &'a mut UniqueRefGpuVm<T>,
user_context: &'a mut T::SmContext<'ctx>,
}
/// Adds an extra field to `SmData` for `sm_map()` callbacks.
///
/// # Invariants
///
/// `self.vm_bo.gpuvm() == self.sm_data.gpuvm`.
#[repr(C)]
struct SmMapData<'a, 'ctx, T: DriverGpuVm> {
sm_data: SmData<'a, 'ctx, T>,
vm_bo: &'a GpuVmBo<T>,
}
/// The argument for [`UniqueRefGpuVm::sm_map`].
pub struct OpMapRequest<'a, 'ctx, T: DriverGpuVm> {
/// Address in GPU virtual address space.
pub addr: u64,
/// Length of mapping to create.
pub range: u64,
/// Offset in GEM object.
pub gem_offset: u64,
/// The GEM object to map.
pub vm_bo: &'a GpuVmBo<T>,
/// The user-provided context type.
pub context: &'a mut T::SmContext<'ctx>,
}
impl<'a, 'ctx, T: DriverGpuVm> OpMapRequest<'a, 'ctx, T> {
fn raw_request(&self) -> bindings::drm_gpuvm_map_req {
bindings::drm_gpuvm_map_req {
map: bindings::drm_gpuva_op_map {
va: bindings::drm_gpuva_op_map__bindgen_ty_1 {
addr: self.addr,
range: self.range,
},
gem: bindings::drm_gpuva_op_map__bindgen_ty_2 {
offset: self.gem_offset,
obj: self.vm_bo.obj().as_raw(),
},
},
}
}
}
/// Represents an `sm_step_map` operation that has not yet been completed.
pub struct OpMap<'op, T: DriverGpuVm> {
op: &'op bindings::drm_gpuva_op_map,
// Since these abstractions are designed for immediate mode, the VM BO needs to be
// pre-allocated, so we always have it available when we reach this point.
vm_bo: &'op GpuVmBo<T>,
// This ensures that 'op is invariant, so that `OpMap<'long, T>` does not
// coerce to `OpMap<'short, T>`. This ensures that the user can't return
// the wrong `OpMapped` value.
_invariant: PhantomData<*mut &'op mut T>,
}
impl<'op, T: DriverGpuVm> OpMap<'op, T> {
/// The base address of the new mapping.
pub fn addr(&self) -> u64 {
self.op.va.addr
}
/// The length of the new mapping.
pub fn length(&self) -> u64 {
self.op.va.range
}
/// The offset within the [`drm_gem_object`](DriverGpuVm::Object).
pub fn gem_offset(&self) -> u64 {
self.op.gem.offset
}
/// The [`drm_gem_object`](DriverGpuVm::Object) to map.
pub fn obj(&self) -> &T::Object {
// SAFETY: The `obj` pointer is guaranteed to be valid.
unsafe { <T::Object as IntoGEMObject>::from_raw(self.op.gem.obj) }
}
/// The [`GpuVmBo`] that the new VA will be associated with.
pub fn vm_bo(&self) -> &GpuVmBo<T> {
self.vm_bo
}
/// Use the pre-allocated VA to carry out this map operation.
pub fn insert(self, va: GpuVaAlloc<T>, va_data: impl PinInit<T::VaData>) -> OpMapped<'op, T> {
let va = va.prepare(va_data);
// SAFETY: By the type invariants we may access the interval tree.
unsafe { bindings::drm_gpuva_map(self.vm_bo.gpuvm().as_raw(), va, self.op) };
let _gpuva_guard = self.vm_bo().lock_gpuva();
// SAFETY: The va is prepared for insertion, and we hold the GEM lock.
unsafe { bindings::drm_gpuva_link(va, self.vm_bo.as_raw()) };
OpMapped {
_invariant: self._invariant,
}
}
}
/// Represents a completed [`OpMap`] operation.
pub struct OpMapped<'op, T> {
_invariant: PhantomData<*mut &'op mut T>,
}
/// Represents an `sm_step_unmap` operation that has not yet been completed.
pub struct OpUnmap<'op, T: DriverGpuVm> {
op: &'op bindings::drm_gpuva_op_unmap,
// This ensures that 'op is invariant, so that `OpUnmap<'long, T>` does not
// coerce to `OpUnmap<'short, T>`. This ensures that the user can't return the
// wrong`OpUnmapped` value.
_invariant: PhantomData<*mut &'op mut T>,
}
impl<'op, T: DriverGpuVm> OpUnmap<'op, T> {
/// Indicates whether this [`GpuVa`] is physically contiguous with the
/// original mapping request.
///
/// Optionally, if `keep` is set, drivers may keep the actual page table
/// mappings for this `drm_gpuva`, adding the missing page table entries
/// only and update the `drm_gpuvm` accordingly.
pub fn keep(&self) -> bool {
self.op.keep
}
/// The range being unmapped.
pub fn va(&self) -> &GpuVa<T> {
// SAFETY: This is a valid va. It's not the `kernel_alloc_node` because you can't unmap it,
// and it's not sparse by the `GpuVm<T>` type invariants.
unsafe { GpuVa::<T>::from_raw(self.op.va) }
}
/// Remove the VA.
pub fn remove(self) -> (OpUnmapped<'op, T>, GpuVaRemoved<T>) {
// SAFETY: The op references a valid drm_gpuva in the GPUVM.
unsafe { bindings::drm_gpuva_unmap(self.op) };
// SAFETY: The va is no longer in the interval tree so we may unlink it.
unsafe { bindings::drm_gpuva_unlink_defer(self.op.va) };
// SAFETY: We just removed this va from the `GpuVm<T>`.
let va = unsafe { GpuVaRemoved::from_raw(self.op.va) };
(
OpUnmapped {
_invariant: self._invariant,
},
va,
)
}
}
/// Represents a completed [`OpUnmap`] operation.
pub struct OpUnmapped<'op, T> {
_invariant: PhantomData<*mut &'op mut T>,
}
/// Represents an `sm_step_remap` operation that has not yet been completed.
pub struct OpRemap<'op, T: DriverGpuVm> {
op: &'op bindings::drm_gpuva_op_remap,
// This ensures that 'op is invariant, so that `OpRemap<'long, T>` does not
// coerce to `OpRemap<'short, T>`. This ensures that the user can't return the
// wrong`OpRemapped` value.
_invariant: PhantomData<*mut &'op mut T>,
}
impl<'op, T: DriverGpuVm> OpRemap<'op, T> {
/// The preceding part of a split mapping.
#[inline]
pub fn prev(&self) -> Option<&OpRemapMapData> {
// SAFETY: We checked for null, so the pointer must be valid.
NonNull::new(self.op.prev).map(|ptr| unsafe { OpRemapMapData::from_raw(ptr) })
}
/// The subsequent part of a split mapping.
#[inline]
pub fn next(&self) -> Option<&OpRemapMapData> {
// SAFETY: We checked for null, so the pointer must be valid.
NonNull::new(self.op.next).map(|ptr| unsafe { OpRemapMapData::from_raw(ptr) })
}
/// Indicates whether the `drm_gpuva` being removed is physically contiguous with the original
/// mapping request.
///
/// Optionally, if `keep` is set, drivers may keep the actual page table mappings for this
/// `drm_gpuva`, adding the missing page table entries only and update the `drm_gpuvm`
/// accordingly.
#[inline]
pub fn keep(&self) -> bool {
// SAFETY: The unmap pointer is always valid.
unsafe { (*self.op.unmap).keep }
}
/// The range being unmapped.
#[inline]
pub fn va_to_unmap(&self) -> &GpuVa<T> {
// SAFETY: This is a valid va. It's not the `kernel_alloc_node` because you can't unmap it,
// and it's not sparse by the `GpuVm<T>` type invariants.
unsafe { GpuVa::<T>::from_raw((*self.op.unmap).va) }
}
/// The [`drm_gem_object`](DriverGpuVm::Object) whose VA is being remapped.
#[inline]
pub fn obj(&self) -> &T::Object {
self.va_to_unmap().obj()
}
/// The [`GpuVmBo`] that is being remapped.
#[inline]
pub fn vm_bo(&self) -> &GpuVmBo<T> {
self.va_to_unmap().vm_bo()
}
/// Update the GPUVM to perform the remapping.
pub fn remap(
self,
va_alloc: [GpuVaAlloc<T>; 2],
prev_data: impl PinInit<T::VaData>,
next_data: impl PinInit<T::VaData>,
) -> (OpRemapped<'op, T>, OpRemapRet<T>) {
let [va1, va2] = va_alloc;
let mut unused_va = None;
let mut prev_ptr = ptr::null_mut();
let mut next_ptr = ptr::null_mut();
if self.prev().is_some() {
prev_ptr = va1.prepare(prev_data);
} else {
unused_va = Some(va1);
}
if self.next().is_some() {
next_ptr = va2.prepare(next_data);
} else {
unused_va = Some(va2);
}
// SAFETY: the pointers are non-null when required
unsafe { bindings::drm_gpuva_remap(prev_ptr, next_ptr, self.op) };
let gpuva_guard = self.vm_bo().lock_gpuva();
if !prev_ptr.is_null() {
// SAFETY: The prev_ptr is a valid drm_gpuva prepared for insertion. The vm_bo is still
// valid as the not-yet-unlinked gpuva holds a refcount on the vm_bo.
unsafe { bindings::drm_gpuva_link(prev_ptr, self.vm_bo().as_raw()) };
}
if !next_ptr.is_null() {
// SAFETY: The next_ptr is a valid drm_gpuva prepared for insertion. The vm_bo is still
// valid as the not-yet-unlinked gpuva holds a refcount on the vm_bo.
unsafe { bindings::drm_gpuva_link(next_ptr, self.vm_bo().as_raw()) };
}
drop(gpuva_guard);
// SAFETY: The va is no longer in the interval tree so we may unlink it.
unsafe { bindings::drm_gpuva_unlink_defer((*self.op.unmap).va) };
(
OpRemapped {
_invariant: self._invariant,
},
OpRemapRet {
// SAFETY: We just removed this va from the `GpuVm<T>`.
unmapped_va: unsafe { GpuVaRemoved::from_raw((*self.op.unmap).va) },
unused_va,
},
)
}
}
/// Part of an [`OpRemap`] that represents a new mapping.
#[repr(transparent)]
pub struct OpRemapMapData(bindings::drm_gpuva_op_map);
impl OpRemapMapData {
/// # Safety
/// Must reference a valid `drm_gpuva_op_map` for duration of `'a`.
unsafe fn from_raw<'a>(ptr: NonNull<bindings::drm_gpuva_op_map>) -> &'a Self {
// SAFETY: ok per safety requirements
unsafe { ptr.cast().as_ref() }
}
/// The base address of the new mapping.
pub fn addr(&self) -> u64 {
self.0.va.addr
}
/// The length of the new mapping.
pub fn length(&self) -> u64 {
self.0.va.range
}
/// The offset within the [`drm_gem_object`](DriverGpuVm::Object).
pub fn gem_offset(&self) -> u64 {
self.0.gem.offset
}
}
/// Struct containing objects removed or not used by [`OpRemap::remap`].
pub struct OpRemapRet<T: DriverGpuVm> {
/// The `drm_gpuva` that was removed.
pub unmapped_va: GpuVaRemoved<T>,
/// If the remap did not split the region into two pieces, then the unused `drm_gpuva` is
/// returned here.
pub unused_va: Option<GpuVaAlloc<T>>,
}
/// Represents a completed [`OpRemap`] operation.
pub struct OpRemapped<'op, T> {
_invariant: PhantomData<*mut &'op mut T>,
}
impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
/// Create a mapping, removing or remapping anything that overlaps.
///
/// Internally calls the [`DriverGpuVm`] callbacks similar to [`Self::sm_unmap`], except that
/// the [`DriverGpuVm::sm_step_map`] is called once to create the requested mapping.
#[inline]
pub fn sm_map(&mut self, req: OpMapRequest<'_, '_, T>) -> Result {
if req.vm_bo.gpuvm() != &**self {
return Err(EINVAL);
}
let gpuvm = self.as_raw();
let raw_req = req.raw_request();
// INVARIANT: Checked above that `vm_bo.gpuvm() == self`.
let mut p = SmMapData {
sm_data: SmData {
gpuvm: self,
user_context: req.context,
},
vm_bo: req.vm_bo,
};
// SAFETY:
// * raw_request() creates a valid request.
// * The private data is valid to be interpreted as both SmData and SmMapData since the
// first field of SmMapData is SmData.
to_result(unsafe {
bindings::drm_gpuvm_sm_map(gpuvm, (&raw mut p).cast(), &raw const raw_req)
})
}
/// Remove any mappings in the given region.
///
/// Internally calls [`DriverGpuVm::sm_step_unmap`] for ranges entirely contained within the
/// given range, and [`DriverGpuVm::sm_step_remap`] for ranges that overlap with the range.
#[inline]
pub fn sm_unmap(&mut self, addr: u64, length: u64, context: &mut T::SmContext<'_>) -> Result {
let gpuvm = self.as_raw();
let mut p = SmData {
gpuvm: self,
user_context: context,
};
// SAFETY:
// * raw_request() creates a valid request.
// * The private data is a valid SmData.
to_result(unsafe { bindings::drm_gpuvm_sm_unmap(gpuvm, (&raw mut p).cast(), addr, length) })
}
}
impl<T: DriverGpuVm> GpuVm<T> {
/// # Safety
/// Must be called from `sm_map` with a pointer to `SmMapData`.
pub(super) unsafe extern "C" fn sm_step_map(
op: *mut bindings::drm_gpuva_op,
p: *mut c_void,
) -> c_int {
// SAFETY: If we reach `sm_step_map` then we were called from `sm_map` which always passes
// an `SmMapData` as private data.
let p = unsafe { &mut *p.cast::<SmMapData<'_, '_, T>>() };
let op = OpMap {
// SAFETY: sm_step_map is called with a map operation.
op: unsafe { &(*op).__bindgen_anon_1.map },
vm_bo: p.vm_bo,
_invariant: PhantomData,
};
match p
.sm_data
.gpuvm
.data()
.sm_step_map(op, p.sm_data.user_context)
{
Ok(OpMapped { .. }) => 0,
Err(err) => err.to_errno(),
}
}
/// # Safety
/// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.
pub(super) unsafe extern "C" fn sm_step_unmap(
op: *mut bindings::drm_gpuva_op,
p: *mut c_void,
) -> c_int {
// SAFETY: The caller provides a pointer that can be treated as `SmData`.
let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };
let op = OpUnmap {
// SAFETY: sm_step_unmap is called with an unmap operation.
op: unsafe { &(*op).__bindgen_anon_1.unmap },
_invariant: PhantomData,
};
match p.gpuvm.data().sm_step_unmap(op, p.user_context) {
Ok(OpUnmapped { .. }) => 0,
Err(err) => err.to_errno(),
}
}
/// # Safety
/// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.
pub(super) unsafe extern "C" fn sm_step_remap(
op: *mut bindings::drm_gpuva_op,
p: *mut c_void,
) -> c_int {
// SAFETY: The caller provides a pointer that can be treated as `SmData`.
let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };
let op = OpRemap {
// SAFETY: sm_step_remap is called with a remap operation.
op: unsafe { &(*op).__bindgen_anon_1.remap },
_invariant: PhantomData,
};
match p.gpuvm.data().sm_step_remap(op, p.user_context) {
Ok(OpRemapped { .. }) => 0,
Err(err) => err.to_errno(),
}
}
}
+168
View File
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
use super::*;
/// Represents that a range of a GEM object is mapped in this [`GpuVm`] instance.
///
/// Does not assume that GEM lock is held.
///
/// # Invariants
///
/// * This is a valid `drm_gpuva` object that is resident in a [`GpuVm<T>`] instance.
/// * It is associated with a [`GpuVmBo<T>`]. Or in other words, it's not an
/// `gpuvm->kernel_alloc_node` and `DRM_GPUVA_SPARSE` is not set.
/// * The associated [`GpuVmBo<T>`] is part of the GEM list.
#[repr(C)]
#[pin_data]
pub struct GpuVa<T: DriverGpuVm> {
#[pin]
inner: Opaque<bindings::drm_gpuva>,
#[pin]
data: T::VaData,
}
impl<T: DriverGpuVm> PartialEq for GpuVa<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.as_raw(), other.as_raw())
}
}
impl<T: DriverGpuVm> Eq for GpuVa<T> {}
impl<T: DriverGpuVm> GpuVa<T> {
/// Access this [`GpuVa`] from a raw pointer.
///
/// # Safety
///
/// * For the duration of `'a`, the pointer must reference a valid `drm_gpuva` associated with
/// a [`GpuVm<T>`].
/// * It must be associated with a [`GpuVmBo<T>`].
/// * The associated [`GpuVmBo<T>`] is part of the GEM list.
#[inline]
pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuva) -> &'a Self {
// CAST: `drm_gpuva` is first field and `repr(C)`.
// SAFETY: The safety requirements match the invariants of `GpuVa`.
unsafe { &*ptr.cast() }
}
/// Returns a raw pointer to underlying C value.
#[inline]
pub fn as_raw(&self) -> *mut bindings::drm_gpuva {
self.inner.get()
}
/// Returns the address of this mapping in the GPU virtual address space.
#[inline]
pub fn addr(&self) -> u64 {
// SAFETY: The `va.addr` field of `drm_gpuva` is immutable.
unsafe { (*self.as_raw()).va.addr }
}
/// Returns the length of this mapping.
#[inline]
pub fn length(&self) -> u64 {
// SAFETY: The `va.range` field of `drm_gpuva` is immutable.
unsafe { (*self.as_raw()).va.range }
}
/// Returns `addr..addr+length`.
#[inline]
pub fn range(&self) -> Range<u64> {
let addr = self.addr();
addr..addr + self.length()
}
/// Returns the offset within the GEM object.
#[inline]
pub fn gem_offset(&self) -> u64 {
// SAFETY: The `gem.offset` field of `drm_gpuva` is immutable.
unsafe { (*self.as_raw()).gem.offset }
}
/// Returns the GEM object.
#[inline]
pub fn obj(&self) -> &T::Object {
// SAFETY: The `gem.obj` field of `drm_gpuva` is immutable. We know that it's not null
// because this VA is associated with a `GpuVmBo<T>`.
unsafe { <T::Object as IntoGEMObject>::from_raw((*self.as_raw()).gem.obj) }
}
/// Returns the underlying [`GpuVmBo`] object that backs this [`GpuVa`].
#[inline]
pub fn vm_bo(&self) -> &GpuVmBo<T> {
// SAFETY: The `vm_bo` field of `drm_gpuva` is immutable. We know that it's not null
// because this VA is associated with a `GpuVmBo<T>`. The BO is in the GEM list by the type
// invariants.
unsafe { GpuVmBo::from_raw((*self.as_raw()).vm_bo) }
}
}
/// A pre-allocated [`GpuVa`] object.
///
/// # Invariants
///
/// The memory is zeroed.
pub struct GpuVaAlloc<T: DriverGpuVm>(KBox<MaybeUninit<GpuVa<T>>>);
impl<T: DriverGpuVm> GpuVaAlloc<T> {
/// Pre-allocate a [`GpuVa`] object.
pub fn new(flags: AllocFlags) -> Result<GpuVaAlloc<T>, AllocError> {
// INVARIANTS: Memory allocated with __GFP_ZERO.
Ok(GpuVaAlloc(KBox::new_uninit(flags | __GFP_ZERO)?))
}
/// Prepare this `drm_gpuva` for insertion into the GPUVM.
#[must_use]
pub(super) fn prepare(mut self, va_data: impl PinInit<T::VaData>) -> *mut bindings::drm_gpuva {
let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0);
// SAFETY: The `data` field is pinned.
let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) };
KBox::into_raw(self.0).cast()
}
}
/// A [`GpuVa`] object that has been removed.
///
/// # Invariants
///
/// The `drm_gpuva` is not resident in the [`GpuVm`].
pub struct GpuVaRemoved<T: DriverGpuVm>(KBox<GpuVa<T>>);
impl<T: DriverGpuVm> GpuVaRemoved<T> {
/// Convert a raw pointer into a [`GpuVaRemoved`].
///
/// # Safety
///
/// * Must have been removed from a [`GpuVm<T>`].
/// * It must not be a `gpuvm->kernel_alloc_node` va.
pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self {
// SAFETY: Since it used to be a VA in a `GpuVm<T>` and it's not a kernel_alloc_node, this
// pointer references a `GpuVa<T>` with a valid `T::VaData`. Since it has been removed, we
// can take ownership of the allocation.
GpuVaRemoved(unsafe { KBox::from_raw(ptr.cast()) })
}
/// Take ownership of the VA data.
pub fn into_inner(self) -> T::VaData
where
T::VaData: Unpin,
{
KBox::into_inner(self.0).data
}
}
impl<T: DriverGpuVm> Deref for GpuVaRemoved<T> {
type Target = T::VaData;
fn deref(&self) -> &T::VaData {
&self.0.data
}
}
impl<T: DriverGpuVm> DerefMut for GpuVaRemoved<T>
where
T::VaData: Unpin,
{
fn deref_mut(&mut self) -> &mut T::VaData {
&mut self.0.data
}
}
+249
View File
@@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
use super::*;
/// Represents that a given GEM object has at least one mapping on this [`GpuVm`] instance.
///
/// Does not assume that GEM lock is held.
///
/// # Invariants
///
/// * Allocated with `kmalloc` and refcounted via `inner`.
/// * Is present in the gem list.
#[repr(C)]
#[pin_data]
pub struct GpuVmBo<T: DriverGpuVm> {
#[pin]
inner: Opaque<bindings::drm_gpuvm_bo>,
#[pin]
data: T::VmBoData,
}
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.inner`.
unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVmBo<T> {
fn inc_ref(&self) {
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.inner`.
unsafe { bindings::drm_gpuvm_bo_get(self.inner.get()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
// CAST: `drm_gpuvm_bo` is first field of repr(C) struct.
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.inner`.
// This GPUVM instance uses immediate mode, so we may put the refcount using the deferred
// mechanism.
unsafe { bindings::drm_gpuvm_bo_put_deferred(obj.as_ptr().cast()) };
}
}
impl<T: DriverGpuVm> PartialEq for GpuVmBo<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.as_raw(), other.as_raw())
}
}
impl<T: DriverGpuVm> Eq for GpuVmBo<T> {}
impl<T: DriverGpuVm> GpuVmBo<T> {
/// The function pointer for allocating a GpuVmBo stored in the gpuvm vtable.
///
/// Allocation is always implemented according to [`Self::vm_bo_alloc`], but it is set to
/// `None` if the default gpuvm behavior is the same as `vm_bo_alloc`.
///
/// This may be `Some` even if `FREE_FN` is `None`, or vice-versa.
pub(super) const ALLOC_FN: Option<unsafe extern "C" fn() -> *mut bindings::drm_gpuvm_bo> = {
use core::alloc::Layout;
let base = Layout::new::<bindings::drm_gpuvm_bo>();
let rust = Layout::new::<Self>();
assert!(base.size() <= rust.size());
if base.size() != rust.size() || base.align() != rust.align() {
Some(Self::vm_bo_alloc)
} else {
// This causes GPUVM to allocate a `GpuVmBo<T>` with `kzalloc(sizeof(drm_gpuvm_bo))`.
None
}
};
/// The function pointer for freeing a GpuVmBo stored in the gpuvm vtable.
///
/// Freeing is always implemented according to [`Self::vm_bo_free`], but it is set to `None` if
/// the default gpuvm behavior is the same as `vm_bo_free`.
///
/// This may be `Some` even if `ALLOC_FN` is `None`, or vice-versa.
pub(super) const FREE_FN: Option<unsafe extern "C" fn(*mut bindings::drm_gpuvm_bo)> = {
if core::mem::needs_drop::<Self>() {
Some(Self::vm_bo_free)
} else {
// This causes GPUVM to free a `GpuVmBo<T>` with `kfree`.
None
}
};
/// Custom function for allocating a `drm_gpuvm_bo`.
///
/// # Safety
///
/// Always safe to call.
unsafe extern "C" fn vm_bo_alloc() -> *mut bindings::drm_gpuvm_bo {
let raw_ptr = KBox::<Self>::new_uninit(GFP_KERNEL | __GFP_ZERO)
.map(KBox::into_raw)
.unwrap_or(ptr::null_mut());
// CAST: `drm_gpuvm_bo` is first field of `Self`.
raw_ptr.cast()
}
/// Custom function for freeing a `drm_gpuvm_bo`.
///
/// # Safety
///
/// The pointer must have been allocated with [`GpuVmBo::ALLOC_FN`], and must not be used after
/// this call.
unsafe extern "C" fn vm_bo_free(ptr: *mut bindings::drm_gpuvm_bo) {
// CAST: `drm_gpuvm_bo` is first field of `Self`.
// SAFETY:
// * The ptr was allocated from kmalloc with the layout of `GpuVmBo<T>`.
// * `ptr->inner` has no destructor.
// * `ptr->data` contains a valid `T::VmBoData` that we can drop.
drop(unsafe { KBox::<Self>::from_raw(ptr.cast()) });
}
/// Access this [`GpuVmBo`] from a raw pointer.
///
/// # Safety
///
/// For the duration of `'a`, the pointer must reference a valid `drm_gpuvm_bo` associated with
/// a [`GpuVm<T>`]. The BO must also be present in the GEM list.
#[inline]
pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm_bo) -> &'a Self {
// SAFETY: `drm_gpuvm_bo` is first field and `repr(C)`.
unsafe { &*ptr.cast() }
}
/// Returns a raw pointer to underlying C value.
#[inline]
pub fn as_raw(&self) -> *mut bindings::drm_gpuvm_bo {
self.inner.get()
}
/// The [`GpuVm`] that this GEM object is mapped in.
#[inline]
pub fn gpuvm(&self) -> &GpuVm<T> {
// SAFETY: The `obj` pointer is guaranteed to be valid.
unsafe { GpuVm::<T>::from_raw((*self.inner.get()).vm) }
}
/// The [`drm_gem_object`](DriverGpuVm::Object) for these mappings.
#[inline]
pub fn obj(&self) -> &T::Object {
// SAFETY: The `obj` pointer is guaranteed to be valid.
unsafe { <T::Object as IntoGEMObject>::from_raw((*self.inner.get()).obj) }
}
/// The driver data with this buffer object.
#[inline]
pub fn data(&self) -> &T::VmBoData {
&self.data
}
pub(super) fn lock_gpuva(&self) -> crate::sync::MutexGuard<'_, ()> {
// SAFETY: The GEM object is valid.
let ptr = unsafe { &raw mut (*self.obj().as_raw()).gpuva.lock };
// SAFETY: The GEM object is valid, so the mutex is properly initialized.
let mutex = unsafe { crate::sync::Mutex::from_raw(ptr) };
mutex.lock()
}
}
/// A pre-allocated [`GpuVmBo`] object.
///
/// # Invariants
///
/// Points at a `drm_gpuvm_bo` that contains a valid `T::VmBoData`, has a refcount of one, and is
/// absent from any gem, extobj, or evict lists.
pub(super) struct GpuVmBoAlloc<T: DriverGpuVm>(NonNull<GpuVmBo<T>>);
impl<T: DriverGpuVm> GpuVmBoAlloc<T> {
/// Create a new pre-allocated [`GpuVmBo`].
///
/// It's intentional that the initializer is infallible because `drm_gpuvm_bo_put` will call
/// drop on the data, so we don't have a way to free it when the data is missing.
#[inline]
pub(super) fn new(
gpuvm: &GpuVm<T>,
gem: &T::Object,
value: impl PinInit<T::VmBoData>,
) -> Result<GpuVmBoAlloc<T>, AllocError> {
// CAST: `GpuVmBoAlloc::vm_bo_alloc` ensures that this memory was allocated with the layout
// of `GpuVmBo<T>`. The type is repr(C), so `container_of` is not required.
// SAFETY: The provided gpuvm and gem ptrs are valid for the duration of this call.
let raw_ptr = unsafe {
bindings::drm_gpuvm_bo_create(gpuvm.as_raw(), gem.as_raw()).cast::<GpuVmBo<T>>()
};
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
// SAFETY: `ptr->data` is a valid pinned location.
let Ok(()) = unsafe { value.__pinned_init(&raw mut (*raw_ptr).data) };
// INVARIANTS: We just created the vm_bo so it's absent from lists, and the data is valid
// as we just initialized it.
Ok(GpuVmBoAlloc(ptr))
}
/// Returns a raw pointer to underlying C value.
#[inline]
pub(super) fn as_raw(&self) -> *mut bindings::drm_gpuvm_bo {
// SAFETY: The pointer references a valid `drm_gpuvm_bo`.
unsafe { (*self.0.as_ptr()).inner.get() }
}
/// Look up whether there is an existing [`GpuVmBo`] for this gem object.
///
/// The caller should not hold the GEM mutex or DMA resv lock.
#[inline]
pub(super) fn obtain(self) -> ARef<GpuVmBo<T>> {
let me = ManuallyDrop::new(self);
// SAFETY: Valid `drm_gpuvm_bo` not already in the lists. We do not access `me` after this
// call.
let ptr = unsafe { bindings::drm_gpuvm_bo_obtain_prealloc(me.as_raw()) };
// SAFETY: `drm_gpuvm_bo_obtain_prealloc` always returns a non-null ptr
let nonnull = unsafe { NonNull::new_unchecked(ptr.cast()) };
// INVARIANTS: `drm_gpuvm_bo_obtain_prealloc` ensures that the bo is in the GEM list.
// SAFETY: We received one refcount from `drm_gpuvm_bo_obtain_prealloc`.
let ret = unsafe { ARef::<GpuVmBo<T>>::from_raw(nonnull) };
// Ensure that external objects are in the extobj list.
//
// Note that we must call `extobj_add` even if `ptr != me` to avoid a race condition where
// we could end up using the extobj before the thread with `ptr == me` calls extobj_add.
if ret.gpuvm().is_extobj(ret.obj()) {
let resv_lock = ret.gpuvm().raw_resv();
// TODO: Use a proper lock guard here once a dma_resv lock abstraction exists.
// SAFETY: The GPUVM is still alive, so its resv lock is too.
unsafe { bindings::dma_resv_lock(resv_lock, ptr::null_mut()) };
// SAFETY: We hold the GPUVMs resv lock.
unsafe { bindings::drm_gpuvm_bo_extobj_add(ptr) };
// SAFETY: We took the lock, so we can unlock it.
unsafe { bindings::dma_resv_unlock(resv_lock) };
}
ret
}
}
impl<T: DriverGpuVm> Deref for GpuVmBoAlloc<T> {
type Target = GpuVmBo<T>;
#[inline]
fn deref(&self) -> &GpuVmBo<T> {
// SAFETY: By the type invariants we may deref while `Self` exists.
unsafe { self.0.as_ref() }
}
}
impl<T: DriverGpuVm> Drop for GpuVmBoAlloc<T> {
#[inline]
fn drop(&mut self) {
// TODO: Call drm_gpuvm_bo_destroy_not_in_lists() directly.
// SAFETY: It's safe to perform a deferred put in any context.
unsafe { bindings::drm_gpuvm_bo_put_deferred(self.as_raw()) };
}
}
+167
View File
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
//! DRM IOCTL definitions.
//!
//! C header: [`include/drm/drm_ioctl.h`](srctree/include/drm/drm_ioctl.h)
use crate::ioctl;
const BASE: u32 = uapi::DRM_IOCTL_BASE as u32;
/// Construct a DRM ioctl number with no argument.
#[allow(non_snake_case)]
#[inline(always)]
pub const fn IO(nr: u32) -> u32 {
ioctl::_IO(BASE, nr)
}
/// Construct a DRM ioctl number with a read-only argument.
#[allow(non_snake_case)]
#[inline(always)]
pub const fn IOR<T>(nr: u32) -> u32 {
ioctl::_IOR::<T>(BASE, nr)
}
/// Construct a DRM ioctl number with a write-only argument.
#[allow(non_snake_case)]
#[inline(always)]
pub const fn IOW<T>(nr: u32) -> u32 {
ioctl::_IOW::<T>(BASE, nr)
}
/// Construct a DRM ioctl number with a read-write argument.
#[allow(non_snake_case)]
#[inline(always)]
pub const fn IOWR<T>(nr: u32) -> u32 {
ioctl::_IOWR::<T>(BASE, nr)
}
/// Descriptor type for DRM ioctls. Use the `declare_drm_ioctls!{}` macro to construct them.
pub type DrmIoctlDescriptor = bindings::drm_ioctl_desc;
/// This is for ioctl which are used for rendering, and require that the file descriptor is either
/// for a render node, or if it’s a legacy/primary node, then it must be authenticated.
pub const AUTH: u32 = bindings::drm_ioctl_flags_DRM_AUTH;
/// This must be set for any ioctl which can change the modeset or display state. Userspace must
/// call the ioctl through a primary node, while it is the active master.
///
/// Note that read-only modeset ioctl can also be called by unauthenticated clients, or when a
/// master is not the currently active one.
pub const MASTER: u32 = bindings::drm_ioctl_flags_DRM_MASTER;
/// Anything that could potentially wreak a master file descriptor needs to have this flag set.
///
/// Current that’s only for the SETMASTER and DROPMASTER ioctl, which e.g. logind can call to
/// force a non-behaving master (display compositor) into compliance.
///
/// This is equivalent to callers with the SYSADMIN capability.
pub const ROOT_ONLY: u32 = bindings::drm_ioctl_flags_DRM_ROOT_ONLY;
/// This is used for all ioctl needed for rendering only, for drivers which support render nodes.
/// This should be all new render drivers, and hence it should be always set for any ioctl with
/// `AUTH` set. Note though that read-only query ioctl might have this set, but have not set
/// DRM_AUTH because they do not require authentication.
pub const RENDER_ALLOW: u32 = bindings::drm_ioctl_flags_DRM_RENDER_ALLOW;
/// Internal structures used by the `declare_drm_ioctls!{}` macro. Do not use directly.
#[doc(hidden)]
pub mod internal {
pub use bindings::drm_device;
pub use bindings::drm_file;
pub use bindings::drm_ioctl_desc;
}
/// Declare the DRM ioctls for a driver.
///
/// Each entry in the list should have the form:
///
/// `(ioctl_number, argument_type, flags, user_callback),`
///
/// `argument_type` is the type name within the `bindings` crate.
/// `user_callback` should have the following prototype:
///
/// ```ignore
/// fn foo(device: &kernel::drm::Device<Self>,
/// data: &mut uapi::argument_type,
/// file: &kernel::drm::File<Self::File>,
/// ) -> Result<u32>
/// ```
/// where `Self` is the drm::drv::Driver implementation these ioctls are being declared within.
///
/// # Examples
///
/// ```ignore
/// kernel::declare_drm_ioctls! {
/// (FOO_GET_PARAM, drm_foo_get_param, ioctl::RENDER_ALLOW, my_get_param_handler),
/// }
/// ```
///
#[macro_export]
macro_rules! declare_drm_ioctls {
( $(($cmd:ident, $struct:ident, $flags:expr, $func:expr)),* $(,)? ) => {
const IOCTLS: &'static [$crate::drm::ioctl::DrmIoctlDescriptor] = {
use $crate::uapi::*;
const _:() = {
let i: u32 = $crate::uapi::DRM_COMMAND_BASE;
// Assert that all the IOCTLs are in the right order and there are no gaps,
// and that the size of the specified type is correct.
$(
let cmd: u32 = $crate::macros::concat_idents!(DRM_IOCTL_, $cmd);
::core::assert!(i == $crate::ioctl::_IOC_NR(cmd));
::core::assert!(core::mem::size_of::<$crate::uapi::$struct>() ==
$crate::ioctl::_IOC_SIZE(cmd));
let i: u32 = i + 1;
)*
};
let ioctls = &[$(
$crate::drm::ioctl::internal::drm_ioctl_desc {
cmd: $crate::macros::concat_idents!(DRM_IOCTL_, $cmd) as u32,
func: {
#[allow(non_snake_case)]
unsafe extern "C" fn $cmd(
raw_dev: *mut $crate::drm::ioctl::internal::drm_device,
raw_data: *mut ::core::ffi::c_void,
raw_file: *mut $crate::drm::ioctl::internal::drm_file,
) -> core::ffi::c_int {
// SAFETY:
// - The DRM core ensures the device lives while callbacks are being
// called.
// - The DRM device must have been registered when we're called through
// an IOCTL.
//
// FIXME: Currently there is nothing enforcing that the types of the
// dev/file match the current driver these ioctls are being declared
// for, and it's not clear how to enforce this within the type system.
let dev = $crate::drm::device::Device::from_raw(raw_dev);
// SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we
// asserted above matches the size of this type, and all bit patterns of
// UAPI structs must be valid.
// The `ioctl` argument is exclusively owned by the handler
// and guaranteed by the C implementation (`drm_ioctl()`) to remain
// valid for the entire lifetime of the reference taken here.
// There is no concurrent access or aliasing; no other references
// to this object exist during this call.
let data = unsafe { &mut *(raw_data.cast::<$crate::uapi::$struct>()) };
// SAFETY: This is just the DRM file structure
let file = unsafe { $crate::drm::File::from_raw(raw_file) };
match $func(dev, data, file) {
Err(e) => e.to_errno(),
Ok(i) => i.try_into()
.unwrap_or($crate::error::code::ERANGE.to_errno()),
}
}
Some($cmd)
},
flags: $flags,
name: $crate::str::as_char_ptr_in_const_context(
$crate::c_str!(::core::stringify!($cmd)),
),
}
),*];
ioctls
};
};
}
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
//! DRM subsystem abstractions.
pub mod device;
pub mod driver;
pub mod file;
pub mod gem;
pub mod gpuvm;
pub mod ioctl;
pub use self::device::Device;
pub use self::device::DeviceContext;
pub use self::device::Registered;
pub use self::device::Uninit;
pub use self::device::UnregisteredDevice;
pub use self::driver::Driver;
pub use self::driver::DriverInfo;
pub use self::driver::Registration;
pub use self::file::File;
pub(crate) mod private {
pub trait Sealed {}
}