Skip to content

Commit a3ce5a6

Browse files
author
Sven Van Asbroeck
committed
rust/kernel/of: construct of_match_table structure at build time
Use const generics and const fns to construct our `of_match_table` structure at build time. A few runtime errors now become build time errors, which is a good thing. To avoid the const generics propagating into the platdev's `Registration` structure definition, use a `dyn trait` to pass the table into the registration. Signed-off-by: Sven Van Asbroeck <[email protected]>
1 parent cbd2a83 commit a3ce5a6

File tree

3 files changed

+73
-69
lines changed

3 files changed

+73
-69
lines changed

drivers/char/hw_random/bcm2835_rng_rust.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use kernel::{
1212
file_operations::{FileOpener, FileOperations},
1313
io_buffer::IoBufferWriter,
1414
miscdev,
15-
of::OfMatchTable,
15+
of::ConstOfMatchTable,
1616
platdev::PlatformDriver,
1717
prelude::*,
1818
{c_str, platdev},
@@ -72,11 +72,12 @@ struct RngModule {
7272

7373
impl KernelModule for RngModule {
7474
fn init() -> Result<Self> {
75-
let of_match_tbl = OfMatchTable::new(&c_str!("brcm,bcm2835-rng"))?;
75+
const OF_MATCH_TBL: ConstOfMatchTable<1> =
76+
ConstOfMatchTable::new_const([&c_str!("brcm,bcm2835-rng")]);
7677

7778
let pdev = platdev::Registration::new_pinned::<RngDriver>(
7879
c_str!("bcm2835-rng-rust"),
79-
Some(of_match_tbl),
80+
Some(&OF_MATCH_TBL),
8081
&THIS_MODULE,
8182
)?;
8283

rust/kernel/of.rs

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,84 @@
44
//!
55
//! C header: [`include/linux/of_*.h`](../../../../include/linux/of_*.h)
66
7-
use alloc::boxed::Box;
7+
use crate::{bindings, c_types, str::CStr};
88

9-
use crate::{
10-
bindings, c_types,
11-
error::{Error, Result},
12-
str::CStr,
13-
types::PointerWrapper,
14-
};
15-
16-
use core::mem::transmute;
17-
18-
type InnerTable = Box<[bindings::of_device_id; 2]>;
9+
use core::ptr;
1910

2011
/// Wraps a kernel Open Firmware / devicetree match table.
2112
///
22-
/// Rust drivers may create this structure to match against devices
13+
/// Rust drivers may use this trait to match against devices
2314
/// described in the devicetree.
24-
///
25-
/// The ['PointerWrapper'] trait provides conversion to/from a raw pointer,
26-
/// suitable to be assigned to a `bindings::device_driver::of_match_table`.
15+
pub trait OfMatchTable {
16+
/// Return the table as a static lifetime, sentinel-terminated C array.
17+
///
18+
/// This is suitable to be assigned to the kernel's `of_match_table` field.
19+
fn as_ptr(&'static self) -> *const bindings::of_device_id;
20+
}
21+
22+
/// An Open Firmware Match Table that can be constructed at build time.
2723
///
2824
/// # Invariants
2925
///
30-
/// The final array element is always filled with zeros (the default).
31-
pub struct OfMatchTable(InnerTable);
26+
/// `sentinel` always contains zeroes.
27+
#[repr(C)]
28+
pub struct ConstOfMatchTable<const N: usize> {
29+
table: [bindings::of_device_id; N],
30+
sentinel: bindings::of_device_id,
31+
}
3232

33-
impl OfMatchTable {
34-
/// Creates a [`OfMatchTable`] from a single `compatible` string.
35-
pub fn new(compatible: &'static CStr) -> Result<Self> {
36-
let tbl = Box::try_new([
37-
Self::new_of_device_id(compatible)?,
38-
bindings::of_device_id::default(),
39-
])?;
40-
// INVARIANTS: we allocated an array with `default()` as its final
41-
// element, therefore that final element will be filled with zeros,
42-
// and the invariant above will hold.
43-
Ok(Self(tbl))
33+
impl<const N: usize> ConstOfMatchTable<N> {
34+
/// Create a new Open Firmware Match Table from a list of compatible strings.
35+
pub const fn new_const(compatibles: [&'static CStr; N]) -> Self {
36+
let mut table = [Self::zeroed_of_device_id(); N];
37+
let mut i = 0;
38+
loop {
39+
if i == N {
40+
break;
41+
}
42+
table[i] = Self::new_of_device_id(compatibles[i]);
43+
i += 1;
44+
}
45+
Self {
46+
table,
47+
// INVARIANT: we zero the sentinel here, and never change it
48+
// anwhere. Therefore it always contains zeroes.
49+
sentinel: Self::zeroed_of_device_id(),
50+
}
4451
}
4552

46-
fn new_of_device_id(compatible: &'static CStr) -> Result<bindings::of_device_id> {
47-
let mut buf = [0_u8; 128];
48-
if compatible.len() > buf.len() {
49-
return Err(Error::EINVAL);
53+
const fn zeroed_of_device_id() -> bindings::of_device_id {
54+
bindings::of_device_id {
55+
name: [0; 32usize],
56+
type_: [0; 32usize],
57+
compatible: [0; 128usize],
58+
data: ptr::null(),
5059
}
51-
buf.get_mut(..compatible.len())
52-
.ok_or(Error::EINVAL)?
53-
.copy_from_slice(compatible.as_bytes());
54-
Ok(bindings::of_device_id {
55-
// SAFETY: re-interpretation from [u8] to [c_types::c_char] of same length is always safe.
56-
compatible: unsafe { transmute::<[u8; 128], [c_types::c_char; 128]>(buf) },
57-
..Default::default()
58-
})
5960
}
60-
}
6161

62-
impl PointerWrapper for OfMatchTable {
63-
fn into_pointer(self) -> *const c_types::c_void {
64-
// Per the invariant above, the generated pointer points to an
65-
// array of `bindings::of_device_id`, where the final element is
66-
// filled with zeros (the sentinel). Therefore, it's suitable to
67-
// be assigned to `bindings::device_driver::of_match_table`.
68-
self.0.into_pointer()
62+
const fn new_of_device_id(compatible: &'static CStr) -> bindings::of_device_id {
63+
let mut id = Self::zeroed_of_device_id();
64+
let compatible = compatible.as_bytes_with_nul();
65+
let mut i = 0;
66+
loop {
67+
if i == compatible.len() {
68+
break;
69+
}
70+
// if `compatible` does not fit in `id.compatible`, an
71+
// "index out of bounds" build time exception will be triggered.
72+
id.compatible[i] = compatible[i] as c_types::c_char;
73+
i += 1;
74+
}
75+
id
6976
}
77+
}
7078

71-
unsafe fn from_pointer(p: *const c_types::c_void) -> Self {
72-
// SAFETY: The passed pointer comes from a previous call to [`InnerTable::into_pointer()`].
73-
Self(unsafe { InnerTable::from_pointer(p) })
79+
impl<const N: usize> OfMatchTable for ConstOfMatchTable<N> {
80+
fn as_ptr(&'static self) -> *const bindings::of_device_id {
81+
// The array is sentinel-terminated, by the invariant above.
82+
// The returned pointer is created by dereferencing
83+
// a structure with static lifetime, therefore the pointer itself
84+
// has static lifetime.
85+
&self.table[0]
7486
}
7587
}

rust/kernel/platdev.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use core::{marker::PhantomPinned, pin::Pin};
2121
#[derive(Default)]
2222
pub struct Registration {
2323
registered: bool,
24-
of_table: Option<*const c_types::c_void>,
2524
pdrv: bindings::platform_driver,
2625
_pin: PhantomPinned,
2726
}
@@ -83,7 +82,7 @@ impl Registration {
8382
fn register<P: PlatformDriver>(
8483
self: Pin<&mut Self>,
8584
name: &'static CStr,
86-
of_match_table: Option<OfMatchTable>,
85+
of_match_table: Option<&'static dyn OfMatchTable>,
8786
module: &'static crate::ThisModule,
8887
) -> Result {
8988
// SAFETY: We must ensure that we never move out of `this`.
@@ -94,9 +93,7 @@ impl Registration {
9493
}
9594
this.pdrv.driver.name = name.as_char_ptr();
9695
if let Some(tbl) = of_match_table {
97-
let ptr = tbl.into_pointer();
98-
this.of_table = Some(ptr);
99-
this.pdrv.driver.of_match_table = ptr.cast();
96+
this.pdrv.driver.of_match_table = tbl.as_ptr();
10097
}
10198
this.pdrv.probe = Some(probe_callback::<P>);
10299
this.pdrv.remove = Some(remove_callback::<P>);
@@ -105,10 +102,9 @@ impl Registration {
105102
// - `name` pointer has static lifetime.
106103
// - `module.0` lives at least as long as the module.
107104
// - `probe()` and `remove()` are static functions.
108-
// - `of_match_table` is either:
109-
// - a raw pointer which lives until after the call to
110-
// `bindings::platform_driver_unregister()`, or
111-
// - null.
105+
// - `of_match_table` is either a raw pointer with static lifetime,
106+
// as guaranteed by the [`of::OfMatchTable::as_ptr()`] invariant,
107+
// or null.
112108
let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) };
113109
if ret < 0 {
114110
return Err(Error::from_kernel_errno(ret));
@@ -122,7 +118,7 @@ impl Registration {
122118
/// Returns a pinned heap-allocated representation of the registration.
123119
pub fn new_pinned<P: PlatformDriver>(
124120
name: &'static CStr,
125-
of_match_tbl: Option<OfMatchTable>,
121+
of_match_tbl: Option<&'static dyn OfMatchTable>,
126122
module: &'static crate::ThisModule,
127123
) -> Result<Pin<Box<Self>>> {
128124
let mut r = Pin::from(Box::try_new(Self::default())?);
@@ -139,11 +135,6 @@ impl Drop for Registration {
139135
// safe to call.
140136
unsafe { bindings::platform_driver_unregister(&mut self.pdrv) }
141137
}
142-
if let Some(ptr) = self.of_table {
143-
// SAFETY: `ptr` came from an `OfMatchTable`.
144-
let tbl = unsafe { OfMatchTable::from_pointer(ptr) };
145-
drop(tbl);
146-
}
147138
}
148139
}
149140

0 commit comments

Comments
 (0)