1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
// 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,
drm,
error::to_result,
prelude::*,
sync::aref::ARef, //
};
use core::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;
/// Data owned by the [`Registration`] and accessible within a
/// [`RegistrationGuard`](drm::RegistrationGuard) critical section via
/// [`Device::registration_data_with()`](drm::Device::registration_data_with).
///
/// The lifetime parameter is tied to the [`Registration`] scope, which is enclosed in the
/// parent bus device binding scope but may be shorter.
type RegistrationData<'a>: Send + Sync + 'a;
/// The type used to manage memory for this driver.
type Object: AllocImpl;
/// The type used to represent a DRM File (client)
type File: drm::file::DriverFile;
/// The bus device type of the parent device that the DRM device is associated with.
type ParentDevice<Ctx: device::DeviceContext>: device::AsBusDevice<Ctx>;
/// 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-privilege 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<'a, T: Driver> {
drm: ARef<drm::Device<T>>,
_reg_data: Pin<KBox<T::RegistrationData<'a>>>,
}
impl<'a, T: Driver> Registration<'a, T> {
/// Register a new [`UnregisteredDevice`](drm::UnregisteredDevice) with userspace.
///
/// # Safety
///
/// The caller must not `mem::forget()` the returned [`Registration`] or otherwise prevent its
/// [`Drop`] implementation from running, since the registration data may contain borrowed
/// references that become invalid after `'a` ends.
pub unsafe fn new<E>(
dev: &'a device::Device<device::Bound>,
drm: drm::UnregisteredDevice<T>,
reg_data: impl PinInit<T::RegistrationData<'a>, E>,
flags: usize,
) -> Result<Self>
where
Error: From<E>,
{
let parent = drm.as_ref();
if parent.as_ref().as_raw() != dev.as_raw() {
return Err(EINVAL);
}
let reg_data: Pin<KBox<T::RegistrationData<'a>>> = KBox::pin_init(reg_data, GFP_KERNEL)?;
// Store the registration data pointer in the device before registration, so that it is
// visible once ioctls can be called.
let ptr: NonNull<T::RegistrationData<'static>> =
NonNull::from(Pin::get_ref(reg_data.as_ref())).cast();
// SAFETY: No concurrent access; the device is not yet registered.
unsafe { *drm.registration_data.get() = ptr };
// SAFETY: `drm` is a valid, initialized but not yet registered DRM device.
let ret = unsafe { bindings::drm_dev_register(drm.as_raw(), flags) };
if let Err(e) = to_result(ret) {
// SAFETY: `drm_dev_register()` synchronizes SRCU on failure, so no concurrent
// access to `registration_data` is possible at this point.
unsafe { *drm.registration_data.get() = NonNull::dangling() };
return Err(e);
}
Ok(Self {
drm: (&*drm).into(),
_reg_data: reg_data,
})
}
/// Returns a reference to the `Device` instance for this registration.
pub fn device(&self) -> &drm::Device<T> {
&self.drm
}
}
// 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) {
// Use `drm_dev_unplug` rather than `drm_dev_unregister` to ensure that existing
// `drm_dev_enter()` critical sections complete before unregistration proceeds. This
// is required for the safety of `RegistrationGuard`, which relies on the SRCU barrier in
// `drm_dev_unplug()` to guarantee that the parent device is still bound within the
// critical section.
//
// SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
// `Registration` also guarantees that this `drm::Device` is actually registered.
unsafe { bindings::drm_dev_unplug(self.drm.as_raw()) };
// After drm_dev_unplug(), the SRCU barrier guarantees that all RegistrationGuard critical
// sections have completed, so no one holds a reference to reg_data anymore.
// reg_data is dropped here automatically.
}
}
|