Skip to content

Commit 29db987

Browse files
author
Danilo Krummrich
committed
rust: implement IdArray, IdTable and RawDeviceId
Most subsystems use some kind of ID to match devices and drivers. Hence, we have to provide Rust drivers an abstraction to register an ID table for the driver to match. Generally, those IDs are subsystem specific and hence need to be implemented by the corresponding subsystem. However, the `IdArray`, `IdTable` and `RawDeviceId` types provide a generalized implementation that makes the life of subsystems easier to do so. Co-developed-by: Wedson Almeida Filho <[email protected]> Signed-off-by: Wedson Almeida Filho <[email protected]> Co-developed-by: Gary Guo <[email protected]> Signed-off-by: Gary Guo <[email protected]> Co-developed-by: Fabien Parent <[email protected]> Signed-off-by: Fabien Parent <[email protected]> Signed-off-by: Danilo Krummrich <[email protected]>
1 parent 45b13f3 commit 29db987

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7031,6 +7031,7 @@ F: include/linux/kobj*
70317031
F: include/linux/property.h
70327032
F: lib/kobj*
70337033
F: rust/kernel/device.rs
7034+
F: rust/kernel/device_id.rs
70347035
F: rust/kernel/driver.rs
70357036

70367037
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)

rust/kernel/device_id.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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_", $table_type,
158+
"__", module_path!(),
159+
"_", line!(),
160+
"_", stringify!($table_name),
161+
"_device_table")
162+
]
163+
static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
164+
unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
165+
};
166+
}

rust/kernel/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
#![feature(inline_const)]
1919
#![feature(lint_reasons)]
2020
#![feature(unsize)]
21+
// Stable in Rust 1.83
22+
#![feature(const_maybe_uninit_as_mut_ptr)]
23+
#![feature(const_mut_refs)]
24+
#![feature(const_ptr_write)]
25+
#![feature(const_refs_to_cell)]
2126

2227
// Ensure conditional compilation based on the kernel configuration works;
2328
// otherwise we may silently break things like initcall handling.
@@ -35,6 +40,7 @@ pub mod block;
3540
mod build_assert;
3641
pub mod cred;
3742
pub mod device;
43+
pub mod device_id;
3844
pub mod driver;
3945
pub mod error;
4046
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]

0 commit comments

Comments
 (0)