Skip to content

Commit 10ccf32

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]> Signed-off-by: Danilo Krummrich <[email protected]>
1 parent 28f06ce commit 10ccf32

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

rust/kernel/device_id.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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 offest to the context/data field.
40+
const DRIVER_DATA_OFFSET: usize;
41+
}
42+
43+
/// A zero-terminated device id array, followed by context data.
44+
#[repr(C)]
45+
pub struct IdArray<T: RawDeviceId, U, const N: usize> {
46+
ids: [T::RawType; N],
47+
sentinel: MaybeUninit<T::RawType>,
48+
id_infos: [U; N],
49+
}
50+
51+
impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
52+
/// Creates a new instance of the array.
53+
///
54+
/// The contents are derived from the given identifiers and context information.
55+
pub const fn new(ids: [(T, U); N]) -> Self {
56+
let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N];
57+
let mut infos = [const { MaybeUninit::uninit() }; N];
58+
59+
let mut i = 0usize;
60+
while i < N {
61+
// SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is
62+
// layout-wise compatible with `RawType`.
63+
raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) };
64+
// SAFETY: by the safety requirement of `RawDeviceId`, this would be effectively
65+
// `raw_ids[i].driver_data = i;`.
66+
unsafe {
67+
raw_ids[i]
68+
.as_mut_ptr()
69+
.byte_offset(T::DRIVER_DATA_OFFSET as _)
70+
.cast::<usize>()
71+
.write(i);
72+
}
73+
74+
// SAFETY: this is effectively a move: `infos[i] = ids[i].1`. We make a copy here but
75+
// later forget `ids`.
76+
infos[i] = MaybeUninit::new(unsafe { core::ptr::read(&ids[i].1) });
77+
i += 1;
78+
}
79+
80+
core::mem::forget(ids);
81+
82+
Self {
83+
// SAFETY: this is effectively `array_assume_init`, which is unstable, so we use
84+
// `transmute_copy` instead. We have initialized all elements of `raw_ids` so this
85+
// `array_assume_init` is safe.
86+
ids: unsafe { core::mem::transmute_copy(&raw_ids) },
87+
sentinel: MaybeUninit::zeroed(),
88+
// SAFETY: We have initialized all elements of `infos` so this `array_assume_init` is
89+
// safe.
90+
id_infos: unsafe { core::mem::transmute_copy(&infos) },
91+
}
92+
}
93+
}
94+
95+
/// A device id table.
96+
///
97+
/// This trait is only implemented by `IdArray`.
98+
///
99+
/// The purpose of this trait is to allow `&'static dyn IdArray<T, U>` to be in context when `N` in
100+
/// `IdArray` doesn't matter.
101+
pub trait IdTable<T: RawDeviceId, U> {
102+
/// Obtain the pointer to the ID table.
103+
fn as_ptr(&self) -> *const T::RawType;
104+
105+
/// Obtain the pointer to the bus specific device ID from an index.
106+
fn id(&self, index: usize) -> &T::RawType;
107+
108+
/// Obtain the pointer to the driver-specific information from an index.
109+
fn info(&self, index: usize) -> &U;
110+
}
111+
112+
impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
113+
fn as_ptr(&self) -> *const T::RawType {
114+
// This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
115+
// to access the sentinel.
116+
(self as *const Self).cast()
117+
}
118+
119+
fn id(&self, index: usize) -> &T::RawType {
120+
&self.ids[index]
121+
}
122+
123+
fn info(&self, index: usize) -> &U {
124+
&self.id_infos[index]
125+
}
126+
}

rust/kernel/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414
#![no_std]
1515
#![feature(arbitrary_self_types)]
1616
#![feature(coerce_unsized)]
17+
#![feature(const_refs_to_cell)]
1718
#![feature(dispatch_from_dyn)]
1819
#![feature(inline_const)]
1920
#![feature(lint_reasons)]
2021
#![feature(unsize)]
22+
#![allow(stable_features)]
23+
// Stable in Rust 1.83
24+
#![feature(const_mut_refs)]
25+
#![feature(const_ptr_write)]
26+
#![feature(const_maybe_uninit_as_mut_ptr)]
2127

2228
// Ensure conditional compilation based on the kernel configuration works;
2329
// otherwise we may silently break things like initcall handling.
@@ -32,6 +38,7 @@ pub mod alloc;
3238
pub mod block;
3339
mod build_assert;
3440
pub mod device;
41+
pub mod device_id;
3542
pub mod driver;
3643
pub mod error;
3744
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]

0 commit comments

Comments
 (0)