]> Git Repo - linux.git/blob - rust/kernel/device_id.rs
Linux 6.14-rc3
[linux.git] / rust / 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
8 use core::mem::MaybeUninit;
9
10 /// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type.
11 ///
12 /// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
13 /// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
14 ///
15 /// # Safety
16 ///
17 /// Implementers must ensure that:
18 ///   - `Self` is layout-compatible with [`RawDeviceId::RawType`]; i.e. it's safe to transmute to
19 ///     `RawDeviceId`.
20 ///
21 ///     This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building
22 ///     the ID table.
23 ///
24 ///     Ideally, this should be achieved using a const function that does conversion instead of
25 ///     transmute; however, const trait functions relies on `const_trait_impl` unstable feature,
26 ///     which is broken/gone in Rust 1.73.
27 ///
28 ///   - `DRIVER_DATA_OFFSET` is the offset of context/data field of the device ID (usually named
29 ///     `driver_data`) of the device ID, the field is suitable sized to write a `usize` value.
30 ///
31 ///     Similar to the previous requirement, the data should ideally be added during `Self` to
32 ///     `RawType` conversion, but there's currently no way to do it when using traits in const.
33 pub unsafe trait RawDeviceId {
34     /// The raw type that holds the device id.
35     ///
36     /// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
37     type RawType: Copy;
38
39     /// The offset to the context/data field.
40     const DRIVER_DATA_OFFSET: usize;
41
42     /// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceId`] trait.
43     fn index(&self) -> usize;
44 }
45
46 /// A zero-terminated device id array.
47 #[repr(C)]
48 pub struct RawIdArray<T: RawDeviceId, const N: usize> {
49     ids: [T::RawType; N],
50     sentinel: MaybeUninit<T::RawType>,
51 }
52
53 impl<T: RawDeviceId, const N: usize> RawIdArray<T, N> {
54     #[doc(hidden)]
55     pub const fn size(&self) -> usize {
56         core::mem::size_of::<Self>()
57     }
58 }
59
60 /// A zero-terminated device id array, followed by context data.
61 #[repr(C)]
62 pub struct IdArray<T: RawDeviceId, U, const N: usize> {
63     raw_ids: RawIdArray<T, N>,
64     id_infos: [U; N],
65 }
66
67 impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
68     /// Creates a new instance of the array.
69     ///
70     /// The contents are derived from the given identifiers and context information.
71     pub const fn new(ids: [(T, U); N]) -> Self {
72         let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N];
73         let mut infos = [const { MaybeUninit::uninit() }; N];
74
75         let mut i = 0usize;
76         while i < N {
77             // SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is
78             // layout-wise compatible with `RawType`.
79             raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) };
80             // SAFETY: by the safety requirement of `RawDeviceId`, this would be effectively
81             // `raw_ids[i].driver_data = i;`.
82             unsafe {
83                 raw_ids[i]
84                     .as_mut_ptr()
85                     .byte_offset(T::DRIVER_DATA_OFFSET as _)
86                     .cast::<usize>()
87                     .write(i);
88             }
89
90             // SAFETY: this is effectively a move: `infos[i] = ids[i].1`. We make a copy here but
91             // later forget `ids`.
92             infos[i] = MaybeUninit::new(unsafe { core::ptr::read(&ids[i].1) });
93             i += 1;
94         }
95
96         core::mem::forget(ids);
97
98         Self {
99             raw_ids: RawIdArray {
100                 // SAFETY: this is effectively `array_assume_init`, which is unstable, so we use
101                 // `transmute_copy` instead. We have initialized all elements of `raw_ids` so this
102                 // `array_assume_init` is safe.
103                 ids: unsafe { core::mem::transmute_copy(&raw_ids) },
104                 sentinel: MaybeUninit::zeroed(),
105             },
106             // SAFETY: We have initialized all elements of `infos` so this `array_assume_init` is
107             // safe.
108             id_infos: unsafe { core::mem::transmute_copy(&infos) },
109         }
110     }
111
112     /// Reference to the contained [`RawIdArray`].
113     pub const fn raw_ids(&self) -> &RawIdArray<T, N> {
114         &self.raw_ids
115     }
116 }
117
118 /// A device id table.
119 ///
120 /// This trait is only implemented by `IdArray`.
121 ///
122 /// The purpose of this trait is to allow `&'static dyn IdArray<T, U>` to be in context when `N` in
123 /// `IdArray` doesn't matter.
124 pub trait IdTable<T: RawDeviceId, U> {
125     /// Obtain the pointer to the ID table.
126     fn as_ptr(&self) -> *const T::RawType;
127
128     /// Obtain the pointer to the bus specific device ID from an index.
129     fn id(&self, index: usize) -> &T::RawType;
130
131     /// Obtain the pointer to the driver-specific information from an index.
132     fn info(&self, index: usize) -> &U;
133 }
134
135 impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
136     fn as_ptr(&self) -> *const T::RawType {
137         // This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
138         // to access the sentinel.
139         (self as *const Self).cast()
140     }
141
142     fn id(&self, index: usize) -> &T::RawType {
143         &self.raw_ids.ids[index]
144     }
145
146     fn info(&self, index: usize) -> &U {
147         &self.id_infos[index]
148     }
149 }
150
151 /// Create device table alias for modpost.
152 #[macro_export]
153 macro_rules! module_device_table {
154     ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
155         #[rustfmt::skip]
156         #[export_name =
157             concat!("__mod_device_table__", $table_type,
158                     "__", module_path!(),
159                     "_", line!(),
160                     "_", stringify!($table_name))
161         ]
162         static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
163             unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
164     };
165 }
This page took 0.037038 seconds and 4 git commands to generate.