From 1c7a51e58c2fd3d79a467cd4760fbcaab70ed9e0 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Thu, 27 May 2021 14:42:02 -0400 Subject: [PATCH 1/2] rust/kernel/platdev: add support for device driver data Device driver data corresponds to per-device context data or state. It is allocated on `probe()` and de-allocated in `remove()`. Signed-off-by: Sven Van Asbroeck --- rust/helpers.c | 16 +++++++++++++++ rust/kernel/platdev.rs | 45 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index cf4ddd705f222c..5450392e6036f2 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -9,6 +9,7 @@ #include #include #include +#include void rust_helper_BUG(void) { @@ -130,6 +131,21 @@ void rust_helper_mutex_lock(struct mutex *lock) } EXPORT_SYMBOL_GPL(rust_helper_mutex_lock); +void * +rust_helper_platform_get_drvdata(const struct platform_device *pdev) +{ + return platform_get_drvdata(pdev); +} +EXPORT_SYMBOL_GPL(rust_helper_platform_get_drvdata); + +void +rust_helper_platform_set_drvdata(struct platform_device *pdev, + void *data) +{ + return platform_set_drvdata(pdev, data); +} +EXPORT_SYMBOL_GPL(rust_helper_platform_set_drvdata); + /* 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/platdev.rs b/rust/kernel/platdev.rs index 1b79445c04df3c..868a0dceaec7ee 100644 --- a/rust/kernel/platdev.rs +++ b/rust/kernel/platdev.rs @@ -30,13 +30,31 @@ pub struct Registration { // (it is fine for multiple threads to have a shared reference to it). unsafe impl Sync for Registration {} +extern "C" { + #[allow(improper_ctypes)] + fn rust_helper_platform_get_drvdata( + pdev: *const bindings::platform_device, + ) -> *mut c_types::c_void; + + #[allow(improper_ctypes)] + fn rust_helper_platform_set_drvdata( + pdev: *mut bindings::platform_device, + data: *mut c_types::c_void, + ); +} + 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 }; - P::probe(device_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 { + rust_helper_platform_set_drvdata(pdev, drv_data); + } Ok(0) } } @@ -47,7 +65,16 @@ extern "C" fn remove_callback( from_kernel_result! { // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. let device_id = unsafe { (*pdev).id }; - P::remove(device_id)?; + // SAFETY: `pdev` is guaranteed to be a valid, non-null pointer. + let ptr = unsafe { rust_helper_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) } } @@ -124,15 +151,25 @@ impl Drop for Registration { /// /// Implement this trait whenever you create a platform driver. pub trait PlatformDriver { + /// Device driver data. + /// + /// Corresponds to the data set or retrieved via the kernel's + /// `platform_{set,get}_drvdata()` functions. + /// + /// Require that `DrvData` 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; + /// 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(device_id: i32) -> 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) -> Result; + fn remove(device_id: i32, drv_data: Self::DrvData) -> Result; } From 615cc5861e387518a353245a6e4edd49a66a60db Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Thu, 27 May 2021 14:42:55 -0400 Subject: [PATCH 2/2] bcm2835_rng_rust: add support for `DrvData` To demonstrate device driver data in a non-trivial way, create a `miscdev` for each discovered device. This `miscdev` can only be opened and read. When read from userspace, it returns four zero bytes at a time. Note that `DrvData` consists of a `Pin>`, which is a pinned structure. This demonstrates that pinned or self- referential structures may be used in `DrvData`. Signed-off-by: Sven Van Asbroeck --- drivers/char/hw_random/bcm2835_rng_rust.rs | 46 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/drivers/char/hw_random/bcm2835_rng_rust.rs b/drivers/char/hw_random/bcm2835_rng_rust.rs index 4c586c8bac838e..ade4ee95950d24 100644 --- a/drivers/char/hw_random/bcm2835_rng_rust.rs +++ b/drivers/char/hw_random/bcm2835_rng_rust.rs @@ -7,10 +7,16 @@ use alloc::boxed::Box; use core::pin::Pin; -use kernel::of::OfMatchTable; -use kernel::platdev::PlatformDriver; -use kernel::prelude::*; -use kernel::{c_str, platdev}; +use kernel::{ + file::File, + file_operations::{FileOpener, FileOperations}, + io_buffer::IoBufferWriter, + miscdev, + of::OfMatchTable, + platdev::PlatformDriver, + prelude::*, + {c_str, platdev}, +}; module! { type: RngModule, @@ -20,15 +26,41 @@ module! { license: b"GPL v2", } +struct RngDevice; + +impl FileOpener<()> for RngDevice { + fn open(_state: &()) -> Result { + Ok(Box::try_new(RngDevice)?) + } +} + +impl FileOperations for RngDevice { + kernel::declare_file_operations!(read); + + fn read(&self, _: &File, data: &mut T, offset: u64) -> Result { + // Succeed if the caller doesn't provide a buffer or if not at the start. + if data.is_empty() || offset != 0 { + return Ok(0); + } + + data.write(&0_u32)?; + Ok(4) + } +} + struct RngDriver; impl PlatformDriver for RngDriver { - fn probe(device_id: i32) -> Result { + type DrvData = Pin>>; + + fn probe(device_id: i32) -> Result { pr_info!("probing discovered hwrng with id {}\n", device_id); - Ok(()) + let drv_data = + miscdev::Registration::new_pinned::(c_str!("rust_hwrng"), None, ())?; + Ok(drv_data) } - fn remove(device_id: i32) -> Result { + fn remove(device_id: i32, _drv_data: Self::DrvData) -> Result { pr_info!("removing hwrng with id {}\n", device_id); Ok(()) }