From 3e62840f5bec75ccd3708a73541c857ca21e8adb Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Sat, 8 Jan 2022 00:38:21 +0000 Subject: [PATCH 1/3] rust: update `amba` to use the new constant device id table Also remove the non-const id table processing as `amba` was the only user. Signed-off-by: Wedson Almeida Filho --- drivers/gpio/gpio_pl061_rust.rs | 10 ++-- rust/kernel/amba.rs | 102 +++++++++++++++++--------------- rust/kernel/driver.rs | 71 +++------------------- rust/kernel/lib.rs | 1 - 4 files changed, 67 insertions(+), 117 deletions(-) diff --git a/drivers/gpio/gpio_pl061_rust.rs b/drivers/gpio/gpio_pl061_rust.rs index 5441a80c539461..40dbd7bed84969 100644 --- a/drivers/gpio/gpio_pl061_rust.rs +++ b/drivers/gpio/gpio_pl061_rust.rs @@ -8,7 +8,7 @@ #![feature(global_asm, allocator_api)] use kernel::{ - amba, bit, bits_iter, declare_amba_id_table, device, gpio, + amba, bit, bits_iter, define_amba_id_table, device, gpio, io_mem::IoMem, irq::{self, ExtraResult, IrqData, LockedIrqData}, power, @@ -261,11 +261,11 @@ impl amba::Driver for PL061Device { type Data = Ref; type PowerOps = Self; - declare_amba_id_table! [ - { id: 0x00041061, mask: 0x000fffff, data: () }, - ]; + define_amba_id_table! {(), [ + ({id: 0x00041061, mask: 0x000fffff}, None), + ]} - fn probe(dev: &mut amba::Device, _id: &amba::DeviceId) -> Result> { + fn probe(dev: &mut amba::Device, _data: Option<&Self::IdInfo>) -> Result> { let res = dev.take_resource().ok_or(Error::ENXIO)?; let irq = dev.irq(0).ok_or(Error::ENXIO)?; diff --git a/rust/kernel/amba.rs b/rust/kernel/amba.rs index 7e16cb44cde24a..1775eda7dce6cd 100644 --- a/rust/kernel/amba.rs +++ b/rust/kernel/amba.rs @@ -1,28 +1,43 @@ // SPDX-License-Identifier: GPL-2.0 -//! Amba devices drivers. +//! Amba devices and drivers. //! //! C header: [`include/linux/amba/bus.h`](../../../../include/linux/amba/bus.h) use crate::{ bindings, c_types, device, driver, error::from_kernel_result, io_mem::Resource, power, - str::CStr, to_result, types::PointerWrapper, Error, Result, ThisModule, + str::CStr, to_result, types::PointerWrapper, Result, ThisModule, }; /// A registration of an amba driver. pub type Registration = driver::Registration>; /// Id of an Amba device. -pub struct DeviceId { +#[derive(Clone, Copy)] +pub struct DeviceId { /// Device id. pub id: u32, /// Mask that identifies which bits are valid in the device id. pub mask: u32, +} + +// SAFETY: `ZERO` is all zeroed-out and `to_rawid` stores `offset` in `amba_id::data`. +unsafe impl const driver::RawDeviceId for DeviceId { + type RawType = bindings::amba_id; + const ZERO: Self::RawType = bindings::amba_id { + id: 0, + mask: 0, + data: core::ptr::null_mut(), + }; - /// Context data to be associated with the device id. This is carried over to [`Driver::probe`] - /// so that drivers can encode any information they may need then. - pub data: T, + fn to_rawid(&self, offset: isize) -> Self::RawType { + bindings::amba_id { + id: self.id, + mask: self.mask, + data: offset as _, + } + } } /// An amba driver. @@ -39,11 +54,11 @@ pub trait Driver { /// The type holding information about each device id supported by the driver. type IdInfo: 'static = (); - /// The table of device ids supported by the drivers. - const ID_TABLE: &'static [DeviceId]; + /// The table of device ids supported by the driver. + const ID_TABLE: Option> = None; /// Probes for the device with the given id. - fn probe(dev: &mut Device, id: &DeviceId) -> Result; + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result; /// Cleans any resources up that are associated with the device. /// @@ -56,24 +71,22 @@ pub struct Adapter(T); impl driver::DriverOps for Adapter { type RegType = bindings::amba_driver; - type RawIdType = bindings::amba_id; - type IdType = DeviceId; - const ID_TABLE: &'static [Self::IdType] = T::ID_TABLE; unsafe fn register( reg: *mut bindings::amba_driver, name: &'static CStr, module: &'static ThisModule, - id_table: *const bindings::amba_id, ) -> Result { // SAFETY: By the safety requirements of this function (defined in the trait defintion), // `reg` is non-null and valid. let amba = unsafe { &mut *reg }; amba.drv.name = name.as_char_ptr(); amba.drv.owner = module.0; - amba.id_table = id_table; amba.probe = Some(probe_callback::); amba.remove = Some(remove_callback::); + if let Some(t) = T::ID_TABLE { + amba.id_table = t.as_ref(); + } if cfg!(CONFIG_PM) { // SAFETY: `probe_callback` sets the driver data after calling `T::Data::into_pointer`, // and we guarantee that `T::Data` is the same as `T::PowerOps::Data` by a constraint @@ -90,14 +103,6 @@ impl driver::DriverOps for Adapter { // `reg` was passed (and updated) by a previous successful call to `amba_driver_register`. unsafe { bindings::amba_driver_unregister(reg) }; } - - fn to_raw_id(index: usize, id: &Self::IdType) -> Self::RawIdType { - bindings::amba_id { - id: id.id, - mask: id.mask, - data: index as _, - } - } } unsafe extern "C" fn probe_callback( @@ -109,11 +114,18 @@ unsafe extern "C" fn probe_callback( // duration of this call, so it is guaranteed to remain alive for the lifetime of `dev`. let mut dev = unsafe { Device::from_ptr(adev) }; // SAFETY: `aid` is valid by the requirements the contract with the C code. - let index = unsafe { (*aid).data } as usize; - if index >= T::ID_TABLE.len() { - return Err(Error::ENXIO); - } - let data = T::probe(&mut dev, &T::ID_TABLE[index])?; + let offset = unsafe { (*aid).data }; + let info = if offset.is_null() { + None + } else { + // SAFETY: The offset comes from a previous call to `offset_from` in `IdArray::new`, + // which guarantees that the resulting pointer is within the table. + let ptr = unsafe { aid.cast::().offset(offset as _).cast::>() }; + // SAFETY: The id table has a static lifetime, so `ptr` is guaranteed to be valid for + // read. + unsafe { (&*ptr).as_ref() } + }; + let data = T::probe(&mut dev, info)?; let ptr = T::Data::into_pointer(data); // SAFETY: `adev` is valid for write by the contract with the C code. unsafe { bindings::amba_set_drvdata(adev, ptr as _) }; @@ -193,17 +205,17 @@ unsafe impl device::RawDevice for Device { /// /// ```ignore /// # use kernel::prelude::*; -/// # use kernel::{amba, declare_amba_id_table, module_amba_driver}; +/// # use kernel::{amba, define_amba_id_table, module_amba_driver}; /// # /// struct MyDriver; /// impl amba::Driver for MyDriver { /// // [...] -/// # fn probe(_dev: &mut amba::Device, _id: &amba::DeviceId) -> Result { +/// # fn probe(_dev: &mut amba::Device, _id: Option<&Self::IdInfo>) -> Result { /// # Ok(()) /// # } -/// # declare_amba_id_table! [ -/// # { id: 0x00041061, mask: 0x000fffff, data: () }, -/// # ]; +/// # define_amba_id_table! {(), [ +/// # ({ id: 0x00041061, mask: 0x000fffff }, None), +/// # ]} /// } /// /// module_amba_driver! { @@ -220,34 +232,28 @@ macro_rules! module_amba_driver { }; } -/// Declares the id table for amba devices. +/// Defines the id table for amba devices. /// /// # Examples /// /// ``` /// # use kernel::prelude::*; -/// # use kernel::{amba, declare_amba_id_table}; +/// # use kernel::{amba, define_amba_id_table}; /// # /// # struct Sample; /// # impl kernel::amba::Driver for Sample { -/// # fn probe(_dev: &mut amba::Device, _id: &amba::DeviceId) -> Result { +/// # fn probe(_dev: &mut amba::Device, _id: Option<&Self::IdInfo>) -> Result { /// # Ok(()) /// # } -/// declare_amba_id_table! [ -/// { id: 0x00041061, mask: 0x000fffff, data: () }, -/// ]; +/// define_amba_id_table! {(), [ +/// ({ id: 0x00041061, mask: 0x000fffff }, None), +/// ]} /// # } /// ``` #[macro_export] -macro_rules! declare_amba_id_table { - ($({$($entry:tt)*},)*) => { - const ID_TABLE: &'static [$crate::amba::DeviceId] = &[ - $( $crate::amba::DeviceId { $($entry)* },)* - ]; +macro_rules! define_amba_id_table { + ($data_type:ty, $($t:tt)*) => { + type IdInfo = $data_type; + $crate::define_id_table!(ID_TABLE, $crate::amba::DeviceId, $data_type, $($t)*); }; - - // Cover case without a trailing comma. - ($(($($entry:tt)*)),*) => { - $crate::declare_amba_id_table!{ $({$($entry)*},)*} - } } diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 3c8a6abd4be362..9424c47e5e20a7 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -5,9 +5,9 @@ //! Each bus/subsystem is expected to implement [`DriverOps`], which allows drivers to register //! using the [`Registration`] class. -use crate::{str::CStr, sync::Ref, Error, KernelModule, Result, ScopeGuard, ThisModule}; -use alloc::{boxed::Box, vec::Vec}; -use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ops::Deref, pin::Pin}; +use crate::{str::CStr, sync::Ref, Error, KernelModule, Result, ThisModule}; +use alloc::boxed::Box; +use core::{cell::UnsafeCell, marker::PhantomData, ops::Deref, pin::Pin}; /// A subsystem (e.g., PCI, Platform, Amba, etc.) that allows drivers to be written for it. pub trait DriverOps { @@ -15,21 +15,6 @@ pub trait DriverOps { /// by the C portion of the kernel. type RegType: Default; - /// The type that holds identification data for the devices supported by the driver. In - /// addition to the information required by the bus, it may also store device-specific data - /// using Rust types. - type IdType: 'static; - - /// The table of ids containing all supported devices. - const ID_TABLE: &'static [Self::IdType]; - - /// The raw type that holds identification data for the devices supported by the driver. This - /// is typically a struct defined by the C portion of the kernel. - /// - /// A zero-terminated array of this type is produced and passed to the C portion during - /// registration. - type RawIdType; - /// Registers a driver. /// /// # Safety @@ -37,15 +22,12 @@ pub trait DriverOps { /// `reg` must point to valid, initialised, and writable memory. It may be modified by this /// function to hold registration state. /// - /// `id_table` must point to a valid for read zero-terminated array of ids. - /// - /// On success, `reg` and `id_table` must remain pinned and valid until the matching call to + /// On success, `reg` must remain pinned and valid until the matching call to /// [`DriverOps::unregister`]. unsafe fn register( reg: *mut Self::RegType, name: &'static CStr, module: &'static ThisModule, - id_table: *const Self::RawIdType, ) -> Result; /// Unregisters a driver previously registered with [`DriverOps::register`]. @@ -55,18 +37,12 @@ pub trait DriverOps { /// `reg` must point to valid writable memory, initialised by a previous successful call to /// [`DriverOps::register`]. unsafe fn unregister(reg: *mut Self::RegType); - - /// Converts an id into a raw id. - /// - /// This is used when building a zero-terminated array from the Rust array. - fn to_raw_id(index: usize, id: &Self::IdType) -> Self::RawIdType; } /// The registration of a driver. pub struct Registration { is_registered: bool, concrete_reg: UnsafeCell, - id_table: Vec>, } // SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to @@ -79,7 +55,6 @@ impl Registration { Self { is_registered: false, concrete_reg: UnsafeCell::new(T::RegType::default()), - id_table: Vec::new(), } } @@ -108,42 +83,13 @@ impl Registration { return Err(Error::EINVAL); } - if this.id_table.is_empty() { - this.build_table()?; - } - - // SAFETY: `concrete_reg` was initialised via its default constructor. `id_table` was just - // initialised above with a zero terminating entry. Both are only freed after `Self::drop` - // is called, which first calls `T::unregister`. - unsafe { - T::register( - this.concrete_reg.get(), - name, - module, - &this.id_table[0] as *const _ as *const _, - ) - }?; + // SAFETY: `concrete_reg` was initialised via its default constructor. It is only freed + // after `Self::drop` is called, which first calls `T::unregister`. + unsafe { T::register(this.concrete_reg.get(), name, module) }?; this.is_registered = true; Ok(()) } - - /// Builds the zero-terminated raw-type array of supported devices. - /// - /// This is not ideal because the table is built at runtime. Once Rust fully supports const - /// generics, we can build the table at compile time. - fn build_table(&mut self) -> Result { - // Clear the table on failure, to indicate that the table isn't initialised. - let mut table = ScopeGuard::new_with_data(&mut self.id_table, |t| t.clear()); - - table.try_reserve_exact(T::ID_TABLE.len() + 1)?; - for (i, id) in T::ID_TABLE.iter().enumerate() { - table.try_push(MaybeUninit::new(T::to_raw_id(i, id)))?; - } - table.try_push(MaybeUninit::zeroed())?; - table.dismiss(); - Ok(()) - } } impl Default for Registration { @@ -394,7 +340,6 @@ macro_rules! define_id_array { }; } -// TODO: Remove `ignore` tag from example once we go to 1.58.x. /// Defines a new constant [`IdTable`] with a concise syntax. /// /// It is meant to be used by buses and subsystems to create a similar macro with their device id @@ -402,7 +347,7 @@ macro_rules! define_id_array { /// /// # Examples /// -/// ```ignore +/// ``` /// #![feature(const_trait_impl)] /// # use kernel::{define_id_table, driver::RawDeviceId}; /// diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 8a47b90a67cdd6..1a541847ba4f16 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -18,7 +18,6 @@ concat_idents, const_fn_trait_bound, const_mut_refs, - const_precise_live_drops, // TODO: Remove once we go to 1.58.x. const_ptr_offset_from, const_refs_to_cell, const_trait_impl, From cffb910f12fda59baa3eb50463917af3fd52bfd4 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Fri, 7 Jan 2022 03:26:33 +0000 Subject: [PATCH 2/3] rust: convert `platform` to use `driver` A platform driver is now defined by implementing a trait, very much like `amba`. There is also a module macro used that can used if a module is just a platform driver. Lastly, drivers can specify additional data with each supported id (to be passed back to them on `probe`). Signed-off-by: Wedson Almeida Filho --- drivers/char/hw_random/bcm2835_rng_rust.rs | 60 ++--- rust/helpers.c | 8 + rust/kernel/of.rs | 122 ++++----- rust/kernel/platform.rs | 280 +++++++++++++-------- 4 files changed, 253 insertions(+), 217 deletions(-) diff --git a/drivers/char/hw_random/bcm2835_rng_rust.rs b/drivers/char/hw_random/bcm2835_rng_rust.rs index e29778899ce022..d11e76c31261d9 100644 --- a/drivers/char/hw_random/bcm2835_rng_rust.rs +++ b/drivers/char/hw_random/bcm2835_rng_rust.rs @@ -6,12 +6,12 @@ #![feature(allocator_api, global_asm)] use kernel::{ - c_str, file::File, file_operations::FileOperations, io_buffer::IoBufferWriter, miscdev, - of::ConstOfMatchTable, platform, platform::PlatformDriver, prelude::*, + c_str, device, file::File, file_operations::FileOperations, io_buffer::IoBufferWriter, miscdev, + module_platform_driver, of, platform, prelude::*, sync::Ref, }; -module! { - type: RngModule, +module_platform_driver! { + type: RngDriver, name: b"bcm2835_rng_rust", author: b"Rust for Linux Contributors", description: b"BCM2835 Random Number Generator (RNG) driver", @@ -38,35 +38,29 @@ impl FileOperations for RngDevice { } } -struct RngDriver; - -impl PlatformDriver for RngDriver { - type DrvData = Pin>>; - - fn probe(device_id: i32) -> Result { - pr_info!("probing discovered hwrng with id {}\n", device_id); - let drv_data = miscdev::Registration::new_pinned(c_str!("rust_hwrng"), None, ())?; - Ok(drv_data) - } +type DeviceData = device::Data, (), ()>; - fn remove(device_id: i32, _drv_data: Self::DrvData) -> Result { - pr_info!("removing hwrng with id {}\n", device_id); - Ok(()) - } -} - -struct RngModule { - _pdev: Pin>, -} - -impl KernelModule for RngModule { - fn init(name: &'static CStr, module: &'static ThisModule) -> Result { - const OF_MATCH_TBL: ConstOfMatchTable<1> = - ConstOfMatchTable::new_const([c_str!("brcm,bcm2835-rng")]); - - let pdev = - platform::Registration::new_pinned::(name, Some(&OF_MATCH_TBL), module)?; - - Ok(RngModule { _pdev: pdev }) +struct RngDriver; +impl platform::Driver for RngDriver { + type Data = Ref; + + kernel::define_of_id_table! {(), [ + (of::DeviceId::Compatible(b"brcm,bcm2835-rng"), None), + ]} + + fn probe(dev: &mut platform::Device, _id_info: Option<&Self::IdInfo>) -> Result { + pr_info!("probing discovered hwrng with id {}\n", dev.id()); + let data = kernel::new_device_data!( + miscdev::Registration::new(), + (), + (), + "BCM2835::Registrations" + )?; + + data.registrations() + .ok_or(Error::ENXIO)? + .as_pinned_mut() + .register(c_str!("rust_hwrng"), None, ())?; + Ok(data.into()) } } diff --git a/rust/helpers.c b/rust/helpers.c index b3188f327e0e6b..324ed4dc27d50d 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -17,6 +17,7 @@ #include #include #include +#include __noreturn void rust_helper_BUG(void) { @@ -477,6 +478,13 @@ void rust_helper_put_cred(const struct cred *cred) { } EXPORT_SYMBOL_GPL(rust_helper_put_cred); +const struct of_device_id *rust_helper_of_match_device( + const struct of_device_id *matches, const struct device *dev) +{ + return of_match_device(matches, dev); +} +EXPORT_SYMBOL_GPL(rust_helper_of_match_device); + /* We use bindgen's --size_t-is-usize option to bind the C size_t type * as the Rust usize type, so we can use it in contexts where Rust * expects a usize like slice (array) indices. usize is defined to be diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs index 78aa5956f03feb..cdcd8324433769 100644 --- a/rust/kernel/of.rs +++ b/rust/kernel/of.rs @@ -4,98 +4,60 @@ //! //! C header: [`include/linux/of_*.h`](../../../../include/linux/of_*.h) -use crate::{bindings, c_types, str::CStr}; +use crate::{bindings, driver, str::BStr}; -use core::ops::Deref; -use core::ptr; +/// An open firmware device id. +#[derive(Clone, Copy)] +pub enum DeviceId { + /// An open firmware device id where only a compatible string is specified. + Compatible(&'static BStr), +} -/// A kernel Open Firmware / devicetree match table. -/// -/// Can only exist as an `&OfMatchTable` reference (akin to `&str` or -/// `&Path` in Rust std). +/// Defines a const open firmware device id table that also carries per-entry data/context/info. /// -/// # Invariants +/// The name of the const is `OF_DEVICE_ID_TABLE`, which is what buses are expected to name their +/// open firmware tables. /// -/// The inner reference points to a sentinel-terminated C array. -#[repr(transparent)] -pub struct OfMatchTable(bindings::of_device_id); - -impl OfMatchTable { - /// 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 - } -} - -/// An Open Firmware Match Table that can be constructed at build time. +/// # Examples /// -/// # Invariants +/// ``` +/// # use kernel::define_of_id_table; +/// use kernel::of; /// -/// `sentinel` always contains zeroes. -#[repr(C)] -pub struct ConstOfMatchTable { - table: [bindings::of_device_id; N], - sentinel: bindings::of_device_id, +/// define_of_id_table! {u32, [ +/// (of::DeviceId::Compatible(b"test-device1,test-device2"), Some(0xff)), +/// (of::DeviceId::Compatible(b"test-device3"), None), +/// ]}; +/// ``` +#[macro_export] +macro_rules! define_of_id_table { + ($data_type:ty, $($t:tt)*) => { + $crate::define_id_table!(OF_DEVICE_ID_TABLE, $crate::of::DeviceId, $data_type, $($t)*); + }; } -impl ConstOfMatchTable { - /// 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(), - } - } - - 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(), - } - } - - 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(); +// SAFETY: `ZERO` is all zeroed-out and `to_rawid` stores `offset` in `of_device_id::data`. +unsafe impl const driver::RawDeviceId for DeviceId { + type RawType = bindings::of_device_id; + const ZERO: Self::RawType = bindings::of_device_id { + name: [0; 32], + type_: [0; 32], + compatible: [0; 128], + data: core::ptr::null(), + }; + + fn to_rawid(&self, offset: isize) -> Self::RawType { + let DeviceId::Compatible(compatible) = self; + let mut id = Self::ZERO; let mut i = 0; while i < compatible.len() { - // If `compatible` does not fit in `id.compatible`, an - // "index out of bounds" build time error will be triggered. - id.compatible[i] = compatible[i] as c_types::c_char; + // If `compatible` does not fit in `id.compatible`, an "index out of bounds" build time + // error will be triggered. + id.compatible[i] = compatible[i] as _; i += 1; } + id.compatible[i] = b'\0' as _; + id.data = offset as _; id } } - -impl Deref for ConstOfMatchTable { - type Target = OfMatchTable; - - 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`. - // 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 } - } -} diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 852607dfe7f8bb..8b912c21068b59 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -//! Platform devices. +//! Platform devices and drivers. //! //! Also called `platdev`, `pdev`. //! @@ -8,145 +8,217 @@ use crate::{ bindings, c_types, - error::{from_kernel_result, Error, Result}, - of::OfMatchTable, + device::{self, RawDevice}, + driver, + error::{from_kernel_result, Result}, + of, str::CStr, + to_result, types::PointerWrapper, + ThisModule, }; -use alloc::boxed::Box; -use core::{marker::PhantomPinned, pin::Pin}; - -/// A registration of a platform device. -#[derive(Default)] -pub struct Registration { - registered: bool, - pdrv: bindings::platform_driver, - _pin: PhantomPinned, -} -// SAFETY: `Registration` does not expose any of its state across threads -// (it is fine for multiple threads to have a shared reference to it). -unsafe impl Sync for Registration {} - -extern "C" fn probe_callback( - pdev: *mut bindings::platform_device, -) -> c_types::c_int { - from_kernel_result! { - // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. - let device_id = unsafe { (*pdev).id }; - let drv_data = P::probe(device_id)?; - let drv_data = drv_data.into_pointer() as *mut c_types::c_void; - // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. - unsafe { - bindings::platform_set_drvdata(pdev, drv_data); - } - Ok(0) - } -} +/// A registration of a platform driver. +pub type Registration = driver::Registration>; -extern "C" fn remove_callback( - pdev: *mut bindings::platform_device, -) -> c_types::c_int { - from_kernel_result! { - // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. - let device_id = unsafe { (*pdev).id }; - // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. - let ptr = unsafe { bindings::platform_get_drvdata(pdev) }; - // SAFETY: - // - we allocated this pointer using `P::DrvData::into_pointer`, - // so it is safe to turn back into a `P::DrvData`. - // - the allocation happened in `probe`, no-one freed the memory, - // `remove` is the canonical kernel location to free driver data. so OK - // to convert the pointer back to a Rust structure here. - let drv_data = unsafe { P::DrvData::from_pointer(ptr) }; - P::remove(device_id, drv_data)?; - Ok(0) - } -} +/// An adapter for the registration of platform drivers. +pub struct Adapter(T); -impl Registration { - fn register( - self: Pin<&mut Self>, +impl driver::DriverOps for Adapter { + type RegType = bindings::platform_driver; + + unsafe fn register( + reg: *mut bindings::platform_driver, name: &'static CStr, - of_match_table: Option<&'static OfMatchTable>, - module: &'static crate::ThisModule, + module: &'static ThisModule, ) -> Result { - // SAFETY: We must ensure that we never move out of `this`. - let this = unsafe { self.get_unchecked_mut() }; - if this.registered { - // Already registered. - return Err(Error::EINVAL); - } - this.pdrv.driver.name = name.as_char_ptr(); - if let Some(tbl) = of_match_table { - this.pdrv.driver.of_match_table = tbl.as_ptr(); + // SAFETY: By the safety requirements of this function (defined in the trait defintion), + // `reg` is non-null and valid. + let pdrv = unsafe { &mut *reg }; + + pdrv.driver.name = name.as_char_ptr(); + pdrv.probe = Some(Self::probe_callback); + pdrv.remove = Some(Self::remove_callback); + if let Some(t) = T::OF_DEVICE_ID_TABLE { + pdrv.driver.of_match_table = t.as_ref(); } - this.pdrv.probe = Some(probe_callback::

); - this.pdrv.remove = Some(remove_callback::

); // SAFETY: - // - `this.pdrv` lives at least until the call to `platform_driver_unregister()` returns. + // - `pdrv` lives at least until the call to `platform_driver_unregister()` returns. // - `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 with static lifetime, - // as guaranteed by the [`of::OfMatchTable::as_ptr()`] return type, - // or null. - let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) }; - if ret < 0 { - return Err(Error::from_kernel_errno(ret)); - } - this.registered = true; - Ok(()) + // as guaranteed by the [`driver::IdTable`] type, or null. + to_result(|| unsafe { bindings::__platform_driver_register(reg, module.0) }) } - /// Registers a platform device. - /// - /// Returns a pinned heap-allocated representation of the registration. - pub fn new_pinned( - name: &'static CStr, - of_match_tbl: Option<&'static OfMatchTable>, - module: &'static crate::ThisModule, - ) -> Result>> { - let mut r = Pin::from(Box::try_new(Self::default())?); - r.as_mut().register::

(name, of_match_tbl, module)?; - Ok(r) + unsafe fn unregister(reg: *mut bindings::platform_driver) { + // SAFETY: By the safety requirements of this function (defined in the trait definition), + // `reg` was passed (and updated) by a previous successful call to + // `platform_driver_register`. + unsafe { bindings::platform_driver_unregister(reg) }; } } -impl Drop for Registration { - fn drop(&mut self) { - if self.registered { - // SAFETY: if `registered` is true, then `self.pdev` was registered - // previously, which means `platform_driver_unregister` is always - // safe to call. - unsafe { bindings::platform_driver_unregister(&mut self.pdrv) } +impl Adapter { + fn get_id_info(dev: &Device) -> Option<&'static T::IdInfo> { + let table = T::OF_DEVICE_ID_TABLE?; + + // SAFETY: `table` has static lifetime, so it is valid for read. `dev` is guaranteed to be + // valid while it's alive, so is the raw device returned by it. + let id = unsafe { bindings::of_match_device(table.as_ref(), dev.raw_device()) }; + if id.is_null() { + return None; + } + + // SAFETY: `id` is a pointer within the static table, so it's always valid. + let offset = unsafe { (*id).data }; + if offset.is_null() { + return None; + } + + // SAFETY: The offset comes from a previous call to `offset_from` in `IdArray::new`, which + // guarantees that the resulting pointer is within the table. + let ptr = unsafe { + id.cast::() + .offset(offset as _) + .cast::>() + }; + + // SAFETY: The id table has a static lifetime, so `ptr` is guaranteed to be valid for read. + unsafe { (&*ptr).as_ref() } + } + + extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> c_types::c_int { + from_kernel_result! { + // SAFETY: `pdev` is valid by the contract with the C code. `dev` is alive only for the + // duration of this call, so it is guaranteed to remain alive for the lifetime of + // `pdev`. + let mut dev = unsafe { Device::from_ptr(pdev) }; + let info = Self::get_id_info(&dev); + let data = T::probe(&mut dev, info)?; + // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. + unsafe { bindings::platform_set_drvdata(pdev, data.into_pointer() as _) }; + Ok(0) + } + } + + extern "C" fn remove_callback(pdev: *mut bindings::platform_device) -> c_types::c_int { + from_kernel_result! { + // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. + let ptr = unsafe { bindings::platform_get_drvdata(pdev) }; + // SAFETY: + // - we allocated this pointer using `T::Data::into_pointer`, + // so it is safe to turn back into a `T::Data`. + // - the allocation happened in `probe`, no-one freed the memory, + // `remove` is the canonical kernel location to free driver data. so OK + // to convert the pointer back to a Rust structure here. + let data = unsafe { T::Data::from_pointer(ptr) }; + let ret = T::remove(&data); + ::device_remove(&data); + ret?; + Ok(0) } } } -/// Trait for implementers of platform drivers. -/// -/// Implement this trait whenever you create a platform driver. -pub trait PlatformDriver { - /// Device driver data. +/// A platform driver. +pub trait Driver { + /// Data stored on device by driver. /// /// Corresponds to the data set or retrieved via the kernel's /// `platform_{set,get}_drvdata()` functions. /// - /// Require that `DrvData` implements `PointerWrapper`. We guarantee to + /// Require that `Data` implements `PointerWrapper`. We guarantee to /// never move the underlying wrapped data structure. This allows - /// driver writers to use pinned or self-referential data structures. - type DrvData: PointerWrapper; + type Data: PointerWrapper + Send + Sync + driver::DeviceRemoval = (); + + /// The type holding information about each device id supported by the driver. + type IdInfo: 'static = (); + + /// The table of device ids supported by the driver. + const OF_DEVICE_ID_TABLE: Option> = None; /// Platform driver probe. /// /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. - fn probe(device_id: i32) -> Result; + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result; /// Platform driver remove. /// /// Called when a platform device is removed. /// Implementers should prepare the device for complete removal here. - fn remove(device_id: i32, drv_data: Self::DrvData) -> Result; + fn remove(_data: &Self::Data) -> Result { + Ok(()) + } +} + +/// A platform device. +/// +/// # Invariants +/// +/// The field `ptr` is non-null and valid for the lifetime of the object. +pub struct Device { + ptr: *mut bindings::platform_device, +} + +impl Device { + /// Creates a new device from the given pointer. + /// + /// # Safety + /// + /// `ptr` must be non-null and valid. It must remain valid for the lifetime of the returned + /// instance. + unsafe fn from_ptr(ptr: *mut bindings::platform_device) -> Self { + // INVARIANT: The safety requirements of the function ensure the lifetime invariant. + Self { ptr } + } + + /// Returns id of the platform device. + pub fn id(&self) -> i32 { + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { (*self.ptr).id } + } +} + +// SAFETY: The device returned by `raw_device` is the raw platform device. +unsafe impl device::RawDevice for Device { + fn raw_device(&self) -> *mut bindings::device { + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { &mut (*self.ptr).dev } + } +} + +/// Declares a kernel module that exposes a single platform driver. +/// +/// # Examples +/// +/// ```ignore +/// # use kernel::prelude::*; +/// # use kernel::{platform, define_of_id_table, module_platform_driver}; +/// # +/// struct MyDriver; +/// impl platform::Driver for MyDriver { +/// // [...] +/// # fn probe(_dev: &mut platform::Device, _id_info: Option<&Self::IdInfo>) -> Result { +/// # Ok(()) +/// # } +/// # define_of_id_table! {(), [ +/// # (of::DeviceId::Compatible(b"brcm,bcm2835-rng"), None), +/// # ]} +/// } +/// +/// module_platform_driver! { +/// type: MyDriver, +/// name: b"module_name", +/// author: b"Author name", +/// license: b"GPL v2", +/// } +/// ``` +#[macro_export] +macro_rules! module_platform_driver { + ($($f:tt)*) => { + $crate::module_driver!(, $crate::platform::Adapter, { $($f)* }); + }; } From 2caae0089dd6e8f8796f3accb659fe1119ed288a Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Sun, 9 Jan 2022 04:49:08 +0000 Subject: [PATCH 3/3] samples/rust: add platform device driver sample This is a minimal sample of registering a driver for a platform device. The driver doesn't really do anything, and serves as a template for future new platform drivers. Signed-off-by: Wedson Almeida Filho --- samples/rust/Kconfig | 10 ++++++++++ samples/rust/Makefile | 1 + samples/rust/rust_platform.rs | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 samples/rust/rust_platform.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 183a3c4dc80cd7..e234be7c341ca1 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -110,4 +110,14 @@ config SAMPLE_RUST_RANDOM If unsure, say N. +config SAMPLE_RUST_PLATFORM + tristate "Platform device driver" + help + This option builds the Rust platform device driver sample. + + To compile this as a module, choose M here: + the module will be called rust_platform. + + If unsure, say N. + endif # SAMPLES_RUST diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 48bc871ea1f8ff..96ffeb7d334ac6 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_SAMPLE_RUST_STACK_PROBING) += rust_stack_probing.o obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE) += rust_semaphore.o obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE_C) += rust_semaphore_c.o obj-$(CONFIG_SAMPLE_RUST_RANDOM) += rust_random.o +obj-$(CONFIG_SAMPLE_RUST_PLATFORM) += rust_platform.o diff --git a/samples/rust/rust_platform.rs b/samples/rust/rust_platform.rs new file mode 100644 index 00000000000000..43ff1694a27852 --- /dev/null +++ b/samples/rust/rust_platform.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust platform device driver sample. + +#![no_std] +#![feature(allocator_api, global_asm)] + +use kernel::{module_platform_driver, of, platform, prelude::*}; + +module_platform_driver! { + type: Driver, + name: b"rust_platform", + license: b"GPL v2", +} + +struct Driver; +impl platform::Driver for Driver { + kernel::define_of_id_table! {(), [ + (of::DeviceId::Compatible(b"rust,sample"), None), + ]} + + fn probe(_dev: &mut platform::Device, _id_info: Option<&Self::IdInfo>) -> Result { + Ok(()) + } +}