xref: /linux/rust/kernel/device_id.rs (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
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 
8 use 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.
29 pub 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.
49 pub 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)]
91 pub 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.
101 unsafe 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.
105 unsafe impl<T: RawDeviceId, U: Sync + 'static, const N: usize> Sync for IdArray<T, U, N> {}
106 
107 impl<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 
142 impl<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.
166 pub trait IdTable<T: RawDeviceId, U> {
167     /// Obtain the pointer to the ID table.
168     fn as_ptr(&self) -> *const T::RawType;
169 }
170 
171 impl<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]
181 macro_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 }
219