Skip to main content

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}