kernel/device_id.rs
1// SPDX-License-Identifier: GPL-2.0
2
3//! Generic implementation of device IDs.
4//!
5//! Each bus / subsystem that matches device and driver through a bus / subsystem specific ID is
6//! expected to implement [`RawDeviceId`].
7
8use core::{
9 marker::PhantomData,
10 mem::MaybeUninit, //
11};
12
13/// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type.
14///
15/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
16/// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
17///
18/// # Safety
19///
20/// Implementers must ensure that `Self` is layout-compatible with [`RawDeviceId::RawType`];
21/// i.e. it's safe to transmute to `RawType`.
22///
23/// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building
24/// the ID table.
25///
26/// Ideally, this should be achieved using a const function that does conversion instead of
27/// transmute; however, const trait functions relies on `const_trait_impl` unstable feature,
28/// which is broken/gone in Rust 1.73.
29pub unsafe trait RawDeviceId {
30 /// The raw type that holds the device id.
31 ///
32 /// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
33 type RawType: Copy;
34}
35
36/// Extension trait for [`RawDeviceId`] for devices that embed an index or context value.
37///
38/// This is typically used when the device ID struct includes a field like `driver_data`
39/// that is used to store a pointer-sized value (e.g., an index or context pointer).
40///
41/// # Safety
42///
43/// Implementers must ensure that `DRIVER_DATA_OFFSET` is the correct offset (in bytes) to
44/// the context/data field (e.g., the `driver_data` field) within the raw device ID structure.
45/// This field must be correctly sized to hold a `usize`.
46///
47/// Ideally, the data should be added during `Self` to `RawType` conversion,
48/// but there's currently no way to do it when using traits in const.
49pub unsafe trait RawDeviceIdIndex: RawDeviceId {
50 /// The offset (in bytes) to the context/data field in the raw device ID.
51 const DRIVER_DATA_OFFSET: usize;
52
53 /// Obtain the data pointer stored inside the device ID.
54 ///
55 /// # Safety
56 ///
57 /// `&Self` must be stored inside a `IdArray<Self, U>`.
58 unsafe fn info_unchecked<U>(&self) -> &'static U {
59 // SAFETY: By safety requirement of the trait, this is `self.driver_data as *const U` and by
60 // the safety requirement of the function, this is stored in `IdArray<Self, U>` so is
61 // convertible to `&'static U`.
62 unsafe {
63 core::ptr::from_ref(self)
64 .byte_add(Self::DRIVER_DATA_OFFSET)
65 .cast::<&U>()
66 .read()
67 }
68 }
69
70 /// Obtain the data pointer stored inside the device ID.
71 ///
72 /// # Safety
73 ///
74 /// `&Self` must be stored inside a `IdArray<Self, U>`, or has NULL (or 0) as driver data.
75 unsafe fn info_unchecked_opt<U>(&self) -> Option<&'static U> {
76 // SAFETY: By safety requirement of the trait, this is `self.driver_data as *const U` and by
77 // the safety requirement of the function, if this is stored in `IdArray<Self, U>`, this is
78 // convertible to `Option<&'static U>`. Otherwise it is NULL which is `None` as
79 // `Option<&U>`.
80 unsafe {
81 core::ptr::from_ref(self)
82 .byte_add(Self::DRIVER_DATA_OFFSET)
83 .cast::<Option<&U>>()
84 .read()
85 }
86 }
87}
88
89/// A zero-terminated device id array, followed by context data.
90#[repr(C)]
91pub struct IdArray<T: RawDeviceId, U: 'static, const N: usize> {
92 // This is `MaybeUninit<T::RawType>` so any bytes inside it can carry provenance in CTFE.
93 // If this were `T::RawType`, integer fields would not be able to contain pointers.
94 ids: [MaybeUninit<T::RawType>; N],
95 sentinel: MaybeUninit<T::RawType>,
96 phantom: PhantomData<&'static U>,
97}
98
99// SAFETY: device ID is plain data plus a `&'static U` and can thus be sent between threads safely
100// if `&U` can.
101unsafe impl<T: RawDeviceId, U: Sync + 'static, const N: usize> Send for IdArray<T, U, N> {}
102
103// SAFETY: device ID is plain data plus a `&'static U` and can thus be shared between threads safely
104// if `&U` can.
105unsafe impl<T: RawDeviceId, U: Sync + 'static, const N: usize> Sync for IdArray<T, U, N> {}
106
107impl<T: RawDeviceId + RawDeviceIdIndex, U: 'static, const N: usize> IdArray<T, U, N> {
108 /// Creates a new instance of the array.
109 ///
110 /// The contents are derived from the given identifiers and context information.
111 pub const fn new(ids: [(T, &'static U); N]) -> Self {
112 let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N];
113
114 let mut i = 0usize;
115 while i < N {
116 // SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is
117 // layout-wise compatible with `RawType`.
118 raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) };
119 // SAFETY: by the safety requirement of `RawDeviceIdIndex`, this would be effectively
120 // `raw_ids[i].driver_data = ids[i].1;`.
121 unsafe {
122 raw_ids[i]
123 .as_mut_ptr()
124 .byte_add(T::DRIVER_DATA_OFFSET)
125 .cast::<&U>()
126 .write(ids[i].1);
127 }
128
129 i += 1;
130 }
131
132 core::mem::forget(ids);
133
134 Self {
135 ids: raw_ids,
136 sentinel: MaybeUninit::zeroed(),
137 phantom: PhantomData,
138 }
139 }
140}
141
142impl<T: RawDeviceId, const N: usize> IdArray<T, (), N> {
143 /// Creates a new instance of the array without writing index values.
144 ///
145 /// The contents are derived from the given identifiers and context information.
146 /// If the device implements [`RawDeviceIdIndex`], consider using [`IdArray::new`] instead.
147 pub const fn new_without_index(ids: [T; N]) -> Self {
148 // SAFETY: `T` is layout-wise compatible with `T::RawType`, so is the array of them.
149 let raw_ids: [MaybeUninit<T::RawType>; N] = unsafe { core::mem::transmute_copy(&ids) };
150 core::mem::forget(ids);
151
152 Self {
153 ids: raw_ids,
154 sentinel: MaybeUninit::zeroed(),
155 phantom: PhantomData,
156 }
157 }
158}
159
160/// A device id table.
161///
162/// This trait is only implemented by `IdArray`.
163///
164/// The purpose of this trait is to allow `&'static dyn IdArray<T, U>` to be in context when `N` in
165/// `IdArray` doesn't matter.
166pub trait IdTable<T: RawDeviceId, U> {
167 /// Obtain the pointer to the ID table.
168 fn as_ptr(&self) -> *const T::RawType;
169}
170
171impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
172 fn as_ptr(&self) -> *const T::RawType {
173 // This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
174 // to access the sentinel.
175 core::ptr::from_ref(self).cast()
176 }
177}
178
179/// Create device table alias for modpost.
180#[macro_export]
181macro_rules! module_device_table {
182 (
183 $table_type: literal, $device_id_ty: ty,
184 $table_name: ident, $id_info_type: ty,
185 [$(($id: expr, $info:expr $(,)?)),* $(,)?]
186 ) => {
187 #[export_name =
188 concat!("__mod_device_table__", ::core::line!(),
189 "__kmod_", module_path!(),
190 "__", $table_type,
191 "__", stringify!($table_name))
192 ]
193 static $table_name: $crate::device_id::IdArray<
194 $device_id_ty,
195 $id_info_type,
196 { <[$device_id_ty]>::len(&[$($id,)*]) },
197 > = $crate::device_id::IdArray::new([$(($id, &$info),)*]);
198 };
199
200 // Case for no ID info.
201 (
202 $table_type: literal, $device_id_ty: ty,
203 $table_name: ident, @none,
204 [$($id: expr),* $(,)?]
205 ) => {
206 #[export_name =
207 concat!("__mod_device_table__", ::core::line!(),
208 "__kmod_", module_path!(),
209 "__", $table_type,
210 "__", stringify!($table_name))
211 ]
212 static $table_name: $crate::device_id::IdArray<
213 $device_id_ty,
214 (),
215 { <[$device_id_ty]>::len(&[$($id,)*]) },
216 > = $crate::device_id::IdArray::new_without_index([$($id),*]);
217 };
218}