xref: /linux/rust/kernel/drm/ioctl.rs (revision fdc290ff4ab19c7e0dde36c4cd1e2771b61f6bf5)
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 
3 //! DRM IOCTL definitions.
4 //!
5 //! C header: [`include/drm/drm_ioctl.h`](srctree/include/drm/drm_ioctl.h)
6 
7 use crate::ioctl;
8 
9 const BASE: u32 = uapi::DRM_IOCTL_BASE as u32;
10 
11 /// Construct a DRM ioctl number with no argument.
12 #[allow(non_snake_case)]
13 #[inline(always)]
14 pub const fn IO(nr: u32) -> u32 {
15     ioctl::_IO(BASE, nr)
16 }
17 
18 /// Construct a DRM ioctl number with a read-only argument.
19 #[allow(non_snake_case)]
20 #[inline(always)]
21 pub const fn IOR<T>(nr: u32) -> u32 {
22     ioctl::_IOR::<T>(BASE, nr)
23 }
24 
25 /// Construct a DRM ioctl number with a write-only argument.
26 #[allow(non_snake_case)]
27 #[inline(always)]
28 pub const fn IOW<T>(nr: u32) -> u32 {
29     ioctl::_IOW::<T>(BASE, nr)
30 }
31 
32 /// Construct a DRM ioctl number with a read-write argument.
33 #[allow(non_snake_case)]
34 #[inline(always)]
35 pub const fn IOWR<T>(nr: u32) -> u32 {
36     ioctl::_IOWR::<T>(BASE, nr)
37 }
38 
39 /// Descriptor type for DRM ioctls. Use the `declare_drm_ioctls!{}` macro to construct them.
40 pub type DrmIoctlDescriptor = bindings::drm_ioctl_desc;
41 
42 /// This is for ioctl which are used for rendering, and require that the file descriptor is either
43 /// for a render node, or if it’s a legacy/primary node, then it must be authenticated.
44 pub const AUTH: u32 = bindings::drm_ioctl_flags_DRM_AUTH;
45 
46 /// This must be set for any ioctl which can change the modeset or display state. Userspace must
47 /// call the ioctl through a primary node, while it is the active master.
48 ///
49 /// Note that read-only modeset ioctl can also be called by unauthenticated clients, or when a
50 /// master is not the currently active one.
51 pub const MASTER: u32 = bindings::drm_ioctl_flags_DRM_MASTER;
52 
53 /// Anything that could potentially wreak a master file descriptor needs to have this flag set.
54 ///
55 /// Current that’s only for the SETMASTER and DROPMASTER ioctl, which e.g. logind can call to
56 /// force a non-behaving master (display compositor) into compliance.
57 ///
58 /// This is equivalent to callers with the SYSADMIN capability.
59 pub const ROOT_ONLY: u32 = bindings::drm_ioctl_flags_DRM_ROOT_ONLY;
60 
61 /// This is used for all ioctl needed for rendering only, for drivers which support render nodes.
62 /// This should be all new render drivers, and hence it should be always set for any ioctl with
63 /// `AUTH` set. Note though that read-only query ioctl might have this set, but have not set
64 /// DRM_AUTH because they do not require authentication.
65 pub const RENDER_ALLOW: u32 = bindings::drm_ioctl_flags_DRM_RENDER_ALLOW;
66 
67 /// Internal structures used by the `declare_drm_ioctls!{}` macro. Do not use directly.
68 #[doc(hidden)]
69 pub mod internal {
70     pub use bindings::drm_device;
71     pub use bindings::drm_file;
72     pub use bindings::drm_ioctl_desc;
73 
74     /// Cast an [`Ioctl`] DRM device pointer to [`Registered`], preserving the driver type
75     /// parameter `T`.
76     ///
77     /// Used by [`declare_drm_ioctls!`] to anchor type inference.
78     #[doc(hidden)]
79     #[inline]
80     pub const fn __dev_ctx_cast<T: crate::drm::Driver>(
81         ptr: *const crate::drm::Device<T, crate::drm::Ioctl>,
82     ) -> *const crate::drm::Device<T, crate::drm::Registered> {
83         ptr.cast()
84     }
85 }
86 
87 /// Declare the DRM ioctls for a driver.
88 ///
89 /// Each entry in the list should have the form:
90 ///
91 /// `(ioctl_number, argument_type, flags, user_callback),`
92 ///
93 /// `argument_type` is the type name within the `bindings` crate.
94 /// `user_callback` should have the following prototype:
95 ///
96 /// ```ignore
97 /// fn foo(device: &kernel::drm::Device<Self, kernel::drm::Registered>,
98 ///        reg_data: &Self::RegistrationData<'_>,
99 ///        data: &mut uapi::argument_type,
100 ///        file: &kernel::drm::File<Self::File>,
101 /// ) -> Result<u32>
102 /// ```
103 /// where `Self` is the drm::drv::Driver implementation these ioctls are being declared within.
104 ///
105 /// # Examples
106 ///
107 /// ```ignore
108 /// kernel::declare_drm_ioctls! {
109 ///     (FOO_GET_PARAM, drm_foo_get_param, ioctl::RENDER_ALLOW, my_get_param_handler),
110 /// }
111 /// ```
112 ///
113 #[macro_export]
114 macro_rules! declare_drm_ioctls {
115     ( $(($cmd:ident, $struct:ident, $flags:expr, $func:expr)),* $(,)? ) => {
116         const IOCTLS: &'static [$crate::drm::ioctl::DrmIoctlDescriptor] = {
117             use $crate::uapi::*;
118             const _:() = {
119                 let i: u32 = $crate::uapi::DRM_COMMAND_BASE;
120                 // Assert that all the IOCTLs are in the right order and there are no gaps,
121                 // and that the size of the specified type is correct.
122                 $(
123                     let cmd: u32 = $crate::macros::concat_idents!(DRM_IOCTL_, $cmd);
124                     ::core::assert!(i == $crate::ioctl::_IOC_NR(cmd));
125                     ::core::assert!(core::mem::size_of::<$crate::uapi::$struct>() ==
126                                     $crate::ioctl::_IOC_SIZE(cmd));
127                     let i: u32 = i + 1;
128                 )*
129             };
130 
131             let ioctls = &[$(
132                 $crate::drm::ioctl::internal::drm_ioctl_desc {
133                     cmd: $crate::macros::concat_idents!(DRM_IOCTL_, $cmd) as u32,
134                     func: {
135                         #[allow(non_snake_case)]
136                         unsafe extern "C" fn $cmd(
137                                 raw_dev: *mut $crate::drm::ioctl::internal::drm_device,
138                                 raw_data: *mut ::core::ffi::c_void,
139                                 raw_file: *mut $crate::drm::ioctl::internal::drm_file,
140                         ) -> core::ffi::c_int {
141                             // SAFETY:
142                             // - The DRM core ensures the device lives while callbacks are being
143                             //   called.
144                             // - The DRM device must have been registered when we're called through
145                             //   an IOCTL.
146                             //
147                             // INVARIANT: The `Ioctl` context requires that the device has been
148                             // registered via `drm_dev_register()` at some point; the DRM core
149                             // guarantees this for ioctl dispatch callbacks.
150                             //
151                             // FIXME: Currently there is nothing enforcing that the types of the
152                             // dev/file match the current driver these ioctls are being declared
153                             // for, and it's not clear how to enforce this within the type system.
154                             let dev: &$crate::drm::device::Device<_, $crate::drm::Ioctl> =
155                                 $crate::drm::device::Device::from_raw(raw_dev);
156 
157                             // Type-inference anchor: the closure is never called but ties `dev`'s
158                             // type to `$func`'s first parameter, which the compiler cannot infer
159                             // through method resolution and associated-type projections alone.
160                             #[allow(unreachable_code)]
161                             let _ = || {
162                                 let __ptr = $crate::drm::ioctl::internal::__dev_ctx_cast(
163                                     ::core::ptr::from_ref(dev),
164                                 );
165 
166                                 $func(
167                                     // SAFETY: This closure is never executed; the dereference
168                                     // exists purely to unify the type parameter with `$func`.
169                                     // The pointer is valid regardless.
170                                     unsafe { &*__ptr },
171                                     unreachable!(),
172                                     unreachable!(),
173                                     unreachable!(),
174                                 )
175                             };
176 
177                             // Enforce that the handler accepts higher-ranked
178                             // lifetimes, preventing it from requiring 'static
179                             // references that could escape this scope.
180                             let _: for<'a> fn(&'a _, &'a _, &'a mut _, &'a _) -> _ = $func;
181 
182                             let Some(guard) = dev.registration_guard() else {
183                                 return $crate::error::code::ENODEV.to_errno();
184                             };
185 
186                             // SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we
187                             // asserted above matches the size of this type, and all bit patterns of
188                             // UAPI structs must be valid.
189                             // The `ioctl` argument is exclusively owned by the handler
190                             // and guaranteed by the C implementation (`drm_ioctl()`) to remain
191                             // valid for the entire lifetime of the reference taken here.
192                             // There is no concurrent access or aliasing; no other references
193                             // to this object exist during this call.
194                             let data = unsafe { &mut *(raw_data.cast::<$crate::uapi::$struct>()) };
195                             // SAFETY: This is just the DRM file structure
196                             let file = unsafe { $crate::drm::File::from_raw(raw_file) };
197 
198                             match guard.registration_data_with(|reg_data| {
199                                 $func(&*guard, reg_data, data, file)
200                             }) {
201                                 Err(e) => e.to_errno(),
202                                 Ok(i) => i.try_into()
203                                             .unwrap_or($crate::error::code::ERANGE.to_errno()),
204                             }
205                         }
206                         Some($cmd)
207                     },
208                     flags: $flags,
209                     name: $crate::str::as_char_ptr_in_const_context(
210                         $crate::c_str!(::core::stringify!($cmd)),
211                     ),
212                 }
213             ),*];
214             ioctls
215         };
216     };
217 }
218