Skip to content

Commit 806b8da

Browse files
authored
Merge pull request #380 from TheSven73/rust-for-linux-static-of-table
rust/kernel/of: construct `of_match_table` structure at build time
2 parents 3c47616 + 5b2ed14 commit 806b8da

File tree

3 files changed

+85
-76
lines changed

3 files changed

+85
-76
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: 75 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,97 @@
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-
};
9+
use core::ops::Deref;
10+
use core::ptr;
1511

16-
use core::mem::transmute;
17-
18-
type InnerTable = Box<[bindings::of_device_id; 2]>;
19-
20-
/// Wraps a kernel Open Firmware / devicetree match table.
12+
/// A kernel Open Firmware / devicetree match table.
2113
///
22-
/// Rust drivers may create this structure to match against devices
23-
/// 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`.
14+
/// Can only exist as an `&OfMatchTable` reference (akin to `&str` or
15+
/// `&Path` in Rust std).
2716
///
2817
/// # Invariants
2918
///
30-
/// The final array element is always filled with zeros (the default).
31-
pub struct OfMatchTable(InnerTable);
19+
/// The inner reference points to a sentinel-terminated C array.
20+
#[repr(transparent)]
21+
pub struct OfMatchTable(bindings::of_device_id);
3222

3323
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))
24+
/// Returns the table as a reference to a static lifetime, sentinel-terminated C array.
25+
///
26+
/// This is suitable to be coerced into the kernel's `of_match_table` field.
27+
pub fn as_ptr(&'static self) -> &'static bindings::of_device_id {
28+
// The inner reference points to a sentinel-terminated C array, as per
29+
// the type invariant.
30+
&self.0
4431
}
32+
}
4533

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);
50-
}
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-
})
59-
}
34+
/// An Open Firmware Match Table that can be constructed at build time.
35+
///
36+
/// # Invariants
37+
///
38+
/// `sentinel` always contains zeroes.
39+
#[repr(C)]
40+
pub struct ConstOfMatchTable<const N: usize> {
41+
table: [bindings::of_device_id; N],
42+
sentinel: bindings::of_device_id,
6043
}
6144

62-
impl PointerWrapper for OfMatchTable {
63-
type Borrowed = <InnerTable as PointerWrapper>::Borrowed;
45+
impl<const N: usize> ConstOfMatchTable<N> {
46+
/// Creates a new Open Firmware Match Table from a list of compatible strings.
47+
pub const fn new_const(compatibles: [&'static CStr; N]) -> Self {
48+
let mut table = [Self::zeroed_of_device_id(); N];
49+
let mut i = 0;
50+
while i < N {
51+
table[i] = Self::new_of_device_id(compatibles[i]);
52+
i += 1;
53+
}
54+
Self {
55+
table,
56+
// INVARIANTS: we zero the sentinel here, and never change it
57+
// anywhere. Therefore it always contains zeroes.
58+
sentinel: Self::zeroed_of_device_id(),
59+
}
60+
}
6461

65-
fn into_pointer(self) -> *const c_types::c_void {
66-
// Per the invariant above, the generated pointer points to an
67-
// array of `bindings::of_device_id`, where the final element is
68-
// filled with zeros (the sentinel). Therefore, it's suitable to
69-
// be assigned to `bindings::device_driver::of_match_table`.
70-
self.0.into_pointer()
62+
const fn zeroed_of_device_id() -> bindings::of_device_id {
63+
bindings::of_device_id {
64+
name: [0; 32],
65+
type_: [0; 32],
66+
compatible: [0; 128],
67+
data: ptr::null(),
68+
}
7169
}
7270

73-
unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed {
74-
// SAFETY: The safety requirements for this function are the same as the ones for
75-
// `InnerTable::borrow`.
76-
unsafe { InnerTable::borrow(ptr) }
71+
const fn new_of_device_id(compatible: &'static CStr) -> bindings::of_device_id {
72+
let mut id = Self::zeroed_of_device_id();
73+
let compatible = compatible.as_bytes_with_nul();
74+
let mut i = 0;
75+
while i < compatible.len() {
76+
// if `compatible` does not fit in `id.compatible`, an
77+
// "index out of bounds" build time exception will be triggered.
78+
id.compatible[i] = compatible[i] as c_types::c_char;
79+
i += 1;
80+
}
81+
id
7782
}
83+
}
84+
85+
impl<const N: usize> Deref for ConstOfMatchTable<N> {
86+
type Target = OfMatchTable;
7887

79-
unsafe fn from_pointer(p: *const c_types::c_void) -> Self {
80-
// SAFETY: The passed pointer comes from a previous call to [`InnerTable::into_pointer()`].
81-
Self(unsafe { InnerTable::from_pointer(p) })
88+
fn deref(&self) -> &OfMatchTable {
89+
// INVARIANTS: `head` points to a sentinel-terminated C array,
90+
// as per the `ConstOfMatchTable` type invariant, therefore
91+
// `&OfMatchTable`'s inner reference will point to a sentinel-terminated C array.
92+
let head = &self.table[0] as *const bindings::of_device_id as *const OfMatchTable;
93+
// SAFETY: The returned reference must remain valid for the lifetime of `self`.
94+
// The raw pointer `head` points to memory inside `self`. So the reference created
95+
// from this raw pointer has the same lifetime as `self`.
96+
// Therefore this reference remains valid for the lifetime of `self`, and
97+
// is safe to return.
98+
unsafe { &*head }
8299
}
83100
}

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 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 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)