forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 465
rust/kernel/of: construct of_match_table
structure at build time
#380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
wedsonaf
merged 1 commit into
Rust-for-Linux:rust
from
TheSven73:rust-for-linux-static-of-table
Jun 23, 2021
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -4,80 +4,97 @@ | |||||||||||
//! | ||||||||||||
//! C header: [`include/linux/of_*.h`](../../../../include/linux/of_*.h) | ||||||||||||
|
||||||||||||
use alloc::boxed::Box; | ||||||||||||
use crate::{bindings, c_types, str::CStr}; | ||||||||||||
|
||||||||||||
use crate::{ | ||||||||||||
bindings, c_types, | ||||||||||||
error::{Error, Result}, | ||||||||||||
str::CStr, | ||||||||||||
types::PointerWrapper, | ||||||||||||
}; | ||||||||||||
use core::ops::Deref; | ||||||||||||
use core::ptr; | ||||||||||||
|
||||||||||||
use core::mem::transmute; | ||||||||||||
|
||||||||||||
type InnerTable = Box<[bindings::of_device_id; 2]>; | ||||||||||||
|
||||||||||||
/// Wraps a kernel Open Firmware / devicetree match table. | ||||||||||||
/// A kernel Open Firmware / devicetree match table. | ||||||||||||
/// | ||||||||||||
/// Rust drivers may create this structure to match against devices | ||||||||||||
/// described in the devicetree. | ||||||||||||
/// | ||||||||||||
/// The ['PointerWrapper'] trait provides conversion to/from a raw pointer, | ||||||||||||
/// suitable to be assigned to a `bindings::device_driver::of_match_table`. | ||||||||||||
/// Can only exist as an `&OfMatchTable` reference (akin to `&str` or | ||||||||||||
/// `&Path` in Rust std). | ||||||||||||
/// | ||||||||||||
/// # Invariants | ||||||||||||
/// | ||||||||||||
/// The final array element is always filled with zeros (the default). | ||||||||||||
pub struct OfMatchTable(InnerTable); | ||||||||||||
/// The inner reference points to a sentinel-terminated C array. | ||||||||||||
#[repr(transparent)] | ||||||||||||
pub struct OfMatchTable(bindings::of_device_id); | ||||||||||||
|
||||||||||||
impl OfMatchTable { | ||||||||||||
/// Creates a [`OfMatchTable`] from a single `compatible` string. | ||||||||||||
pub fn new(compatible: &'static CStr) -> Result<Self> { | ||||||||||||
let tbl = Box::try_new([ | ||||||||||||
Self::new_of_device_id(compatible)?, | ||||||||||||
bindings::of_device_id::default(), | ||||||||||||
])?; | ||||||||||||
// INVARIANTS: we allocated an array with `default()` as its final | ||||||||||||
// element, therefore that final element will be filled with zeros, | ||||||||||||
// and the invariant above will hold. | ||||||||||||
Ok(Self(tbl)) | ||||||||||||
/// Returns the table as a reference to a static lifetime, sentinel-terminated C array. | ||||||||||||
/// | ||||||||||||
/// This is suitable to be coerced into the kernel's `of_match_table` field. | ||||||||||||
pub fn as_ptr(&'static self) -> &'static bindings::of_device_id { | ||||||||||||
// The inner reference points to a sentinel-terminated C array, as per | ||||||||||||
// the type invariant. | ||||||||||||
&self.0 | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
fn new_of_device_id(compatible: &'static CStr) -> Result<bindings::of_device_id> { | ||||||||||||
let mut buf = [0_u8; 128]; | ||||||||||||
if compatible.len() > buf.len() { | ||||||||||||
return Err(Error::EINVAL); | ||||||||||||
} | ||||||||||||
buf.get_mut(..compatible.len()) | ||||||||||||
.ok_or(Error::EINVAL)? | ||||||||||||
.copy_from_slice(compatible.as_bytes()); | ||||||||||||
Ok(bindings::of_device_id { | ||||||||||||
// SAFETY: re-interpretation from [u8] to [c_types::c_char] of same length is always safe. | ||||||||||||
compatible: unsafe { transmute::<[u8; 128], [c_types::c_char; 128]>(buf) }, | ||||||||||||
..Default::default() | ||||||||||||
}) | ||||||||||||
} | ||||||||||||
/// An Open Firmware Match Table that can be constructed at build time. | ||||||||||||
/// | ||||||||||||
/// # Invariants | ||||||||||||
/// | ||||||||||||
/// `sentinel` always contains zeroes. | ||||||||||||
#[repr(C)] | ||||||||||||
pub struct ConstOfMatchTable<const N: usize> { | ||||||||||||
table: [bindings::of_device_id; N], | ||||||||||||
sentinel: bindings::of_device_id, | ||||||||||||
} | ||||||||||||
|
||||||||||||
impl PointerWrapper for OfMatchTable { | ||||||||||||
type Borrowed = <InnerTable as PointerWrapper>::Borrowed; | ||||||||||||
impl<const N: usize> ConstOfMatchTable<N> { | ||||||||||||
/// Creates a new Open Firmware Match Table from a list of compatible strings. | ||||||||||||
pub const fn new_const(compatibles: [&'static CStr; N]) -> Self { | ||||||||||||
let mut table = [Self::zeroed_of_device_id(); N]; | ||||||||||||
let mut i = 0; | ||||||||||||
while i < N { | ||||||||||||
table[i] = Self::new_of_device_id(compatibles[i]); | ||||||||||||
i += 1; | ||||||||||||
} | ||||||||||||
Self { | ||||||||||||
table, | ||||||||||||
// INVARIANTS: we zero the sentinel here, and never change it | ||||||||||||
// anywhere. Therefore it always contains zeroes. | ||||||||||||
sentinel: Self::zeroed_of_device_id(), | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
fn into_pointer(self) -> *const c_types::c_void { | ||||||||||||
// Per the invariant above, the generated pointer points to an | ||||||||||||
// array of `bindings::of_device_id`, where the final element is | ||||||||||||
// filled with zeros (the sentinel). Therefore, it's suitable to | ||||||||||||
// be assigned to `bindings::device_driver::of_match_table`. | ||||||||||||
self.0.into_pointer() | ||||||||||||
const fn zeroed_of_device_id() -> bindings::of_device_id { | ||||||||||||
bindings::of_device_id { | ||||||||||||
name: [0; 32], | ||||||||||||
type_: [0; 32], | ||||||||||||
compatible: [0; 128], | ||||||||||||
data: ptr::null(), | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed { | ||||||||||||
// SAFETY: The safety requirements for this function are the same as the ones for | ||||||||||||
// `InnerTable::borrow`. | ||||||||||||
unsafe { InnerTable::borrow(ptr) } | ||||||||||||
const fn new_of_device_id(compatible: &'static CStr) -> bindings::of_device_id { | ||||||||||||
let mut id = Self::zeroed_of_device_id(); | ||||||||||||
let compatible = compatible.as_bytes_with_nul(); | ||||||||||||
let mut i = 0; | ||||||||||||
while i < compatible.len() { | ||||||||||||
// if `compatible` does not fit in `id.compatible`, an | ||||||||||||
// "index out of bounds" build time exception will be triggered. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
id.compatible[i] = compatible[i] as c_types::c_char; | ||||||||||||
i += 1; | ||||||||||||
} | ||||||||||||
id | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
impl<const N: usize> Deref for ConstOfMatchTable<N> { | ||||||||||||
type Target = OfMatchTable; | ||||||||||||
|
||||||||||||
unsafe fn from_pointer(p: *const c_types::c_void) -> Self { | ||||||||||||
// SAFETY: The passed pointer comes from a previous call to [`InnerTable::into_pointer()`]. | ||||||||||||
Self(unsafe { InnerTable::from_pointer(p) }) | ||||||||||||
fn deref(&self) -> &OfMatchTable { | ||||||||||||
// INVARIANTS: `head` points to a sentinel-terminated C array, | ||||||||||||
// as per the `ConstOfMatchTable` type invariant, therefore | ||||||||||||
// `&OfMatchTable`'s inner reference will point to a sentinel-terminated C array. | ||||||||||||
let head = &self.table[0] as *const bindings::of_device_id as *const OfMatchTable; | ||||||||||||
// SAFETY: The returned reference must remain valid for the lifetime of `self`. | ||||||||||||
Comment on lines
+92
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
// The raw pointer `head` points to memory inside `self`. So the reference created | ||||||||||||
// from this raw pointer has the same lifetime as `self`. | ||||||||||||
// Therefore this reference remains valid for the lifetime of `self`, and | ||||||||||||
// is safe to return. | ||||||||||||
unsafe { &*head } | ||||||||||||
} | ||||||||||||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -21,7 +21,6 @@ use core::{marker::PhantomPinned, pin::Pin}; | |||||
#[derive(Default)] | ||||||
pub struct Registration { | ||||||
registered: bool, | ||||||
of_table: Option<*const c_types::c_void>, | ||||||
pdrv: bindings::platform_driver, | ||||||
_pin: PhantomPinned, | ||||||
} | ||||||
|
@@ -83,7 +82,7 @@ impl Registration { | |||||
fn register<P: PlatformDriver>( | ||||||
self: Pin<&mut Self>, | ||||||
name: &'static CStr, | ||||||
of_match_table: Option<OfMatchTable>, | ||||||
of_match_table: Option<&'static OfMatchTable>, | ||||||
module: &'static crate::ThisModule, | ||||||
) -> Result { | ||||||
// SAFETY: We must ensure that we never move out of `this`. | ||||||
|
@@ -94,9 +93,7 @@ impl Registration { | |||||
} | ||||||
this.pdrv.driver.name = name.as_char_ptr(); | ||||||
if let Some(tbl) = of_match_table { | ||||||
let ptr = tbl.into_pointer(); | ||||||
this.of_table = Some(ptr); | ||||||
this.pdrv.driver.of_match_table = ptr.cast(); | ||||||
this.pdrv.driver.of_match_table = tbl.as_ptr(); | ||||||
} | ||||||
this.pdrv.probe = Some(probe_callback::<P>); | ||||||
this.pdrv.remove = Some(remove_callback::<P>); | ||||||
|
@@ -105,10 +102,9 @@ impl Registration { | |||||
// - `name` pointer has static lifetime. | ||||||
// - `module.0` lives at least as long as the module. | ||||||
// - `probe()` and `remove()` are static functions. | ||||||
// - `of_match_table` is either: | ||||||
// - a raw pointer which lives until after the call to | ||||||
// `bindings::platform_driver_unregister()`, or | ||||||
// - null. | ||||||
// - `of_match_table` is either a raw pointer with static lifetime, | ||||||
// as guaranteed by the [`of::OfMatchTable::as_ptr()`] invariant, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? |
||||||
// or null. | ||||||
let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) }; | ||||||
if ret < 0 { | ||||||
return Err(Error::from_kernel_errno(ret)); | ||||||
|
@@ -122,7 +118,7 @@ impl Registration { | |||||
/// Returns a pinned heap-allocated representation of the registration. | ||||||
pub fn new_pinned<P: PlatformDriver>( | ||||||
name: &'static CStr, | ||||||
of_match_tbl: Option<OfMatchTable>, | ||||||
of_match_tbl: Option<&'static OfMatchTable>, | ||||||
module: &'static crate::ThisModule, | ||||||
) -> Result<Pin<Box<Self>>> { | ||||||
let mut r = Pin::from(Box::try_new(Self::default())?); | ||||||
|
@@ -139,11 +135,6 @@ impl Drop for Registration { | |||||
// safe to call. | ||||||
unsafe { bindings::platform_driver_unregister(&mut self.pdrv) } | ||||||
} | ||||||
if let Some(ptr) = self.of_table { | ||||||
// SAFETY: `ptr` came from an `OfMatchTable`. | ||||||
let tbl = unsafe { OfMatchTable::from_pointer(ptr) }; | ||||||
drop(tbl); | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.