From fe8049906e33d53407892690fb34f6053c252e79 Mon Sep 17 00:00:00 2001 From: Maciej Falkowski Date: Wed, 27 Oct 2021 15:55:39 +0200 Subject: [PATCH] rust: add Hardware Random Number Generator subsystem This patch adds abstractions over the hwrng subsystem: - trait `hwrng::Operations` that defines operations of a hwrng driver. - `declare_hwrng_operations!` macro that populates callback pointers located in hwrng structure. - Registration structure for hwrng's drivers. Signed-off-by: Maciej Falkowski --- rust/kernel/bindings_helper.h | 1 + rust/kernel/hwrng.rs | 242 ++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 244 insertions(+) create mode 100644 rust/kernel/hwrng.rs diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index a79f3f398b9369..2a5433100c19ad 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/hwrng.rs b/rust/kernel/hwrng.rs new file mode 100644 index 00000000000000..62bb01fe16ecfe --- /dev/null +++ b/rust/kernel/hwrng.rs @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Hardware Random Number Generator. +//! +//! C header: [`include/linux/hw_random.h`](../../../../include/linux/hw_random.h) + +use alloc::{boxed::Box, slice::from_raw_parts_mut}; + +use crate::{ + bindings, c_types, error::from_kernel_result, str::CString, to_result, types::PointerWrapper, + Error, Result, ScopeGuard, +}; + +use core::{cell::UnsafeCell, fmt, marker::PhantomData, pin::Pin}; + +/// This trait is implemented in order to provide callbacks to `struct hwrng`. +pub trait Operations { + /// The methods to use to populate [`struct hwrng`]. + const TO_USE: ToUse; + + /// The pointer type that will be used to hold user-defined data type. + type Data: PointerWrapper + Send + Sync = (); + + /// Initialization callback, can be left undefined. + fn init(_data: ::Borrowed<'_>) -> Result { + Err(Error::EINVAL) + } + + /// Cleanup callback, can be left undefined. + fn cleanup(_data: Self::Data) {} + + /// Read data into the provided buffer. + /// Drivers can fill up to max bytes of data into the buffer. + /// The buffer is aligned for any type and its size is a multiple of 4 and >= 32 bytes. + fn read( + data: ::Borrowed<'_>, + buffer: &mut [u8], + wait: bool, + ) -> Result; +} + +/// Registration structure for Hardware Random Number Generator driver. +pub struct Registration { + hwrng: UnsafeCell, + name: Option, + registered: bool, + _p: PhantomData, +} + +impl Registration { + /// Creates new instance of registration. + /// + /// The data must be registered. + pub fn new() -> Self { + Self { + hwrng: UnsafeCell::new(bindings::hwrng::default()), + name: None, + registered: false, + _p: PhantomData, + } + } + + /// Returns a registered and pinned, heap-allocated representation of the registration. + pub fn new_pinned( + name: fmt::Arguments<'_>, + quality: u16, + data: T::Data, + ) -> Result>> { + let mut reg = Pin::from(Box::try_new(Self::new())?); + reg.as_mut().register(name, quality, data)?; + Ok(reg) + } + + /// Registers a hwrng device within the rest of the kernel. + /// + /// It must be pinned because the memory block that represents + /// the registration may be self-referential. + pub fn register( + self: Pin<&mut Self>, + name: fmt::Arguments<'_>, + quality: u16, + data: T::Data, + ) -> Result { + // SAFETY: We never move out of `this`. + let this = unsafe { self.get_unchecked_mut() }; + + if this.registered { + return Err(Error::EINVAL); + } + + let data_pointer = data.into_pointer(); + + // SAFETY: `data_pointer` comes from the call to `data.into_pointer()` above. + let guard = ScopeGuard::new(|| unsafe { + T::Data::from_pointer(data_pointer); + }); + + let name = CString::try_from_fmt(name)?; + + // SAFETY: Registration is pinned and contains allocated and set to zero `bindings::hwrng` structure. + Self::init_hwrng( + unsafe { &mut *this.hwrng.get() }, + &name, + quality, + data_pointer, + ); + + // SAFETY: `bindings::hwrng` is initialized above which guarantees safety. + to_result(|| unsafe { bindings::hwrng_register(this.hwrng.get()) })?; + + this.registered = true; + this.name = Some(name); + guard.dismiss(); + Ok(()) + } + + fn init_hwrng( + hwrng: &mut bindings::hwrng, + name: &CString, + quality: u16, + data: *const c_types::c_void, + ) { + hwrng.name = name.as_char_ptr(); + + hwrng.init = if T::TO_USE.init { + Some(Self::init_callback) + } else { + None + }; + hwrng.cleanup = if T::TO_USE.cleanup { + Some(Self::cleanup_callback) + } else { + None + }; + hwrng.data_present = None; + hwrng.data_read = None; + hwrng.read = Some(Self::read_callback); + + hwrng.priv_ = data as _; + hwrng.quality = quality; + + // SAFETY: All fields are properly initialized as + // remaining fields `list`, `ref` and `cleanup_done` are already + // zeroed by `bindings::hwrng::default()` call. + } + + unsafe extern "C" fn init_callback(rng: *mut bindings::hwrng) -> c_types::c_int { + from_kernel_result! { + // SAFETY: `priv` private data field was initialized during creation of + // the `bindings::hwrng` in `Self::init_hwrng` method. This callback is only + // called once the driver is registered. + let data = unsafe { T::Data::borrow((*rng).priv_ as *const _) }; + T::init(data)?; + Ok(0) + } + } + + unsafe extern "C" fn cleanup_callback(rng: *mut bindings::hwrng) { + // SAFETY: `priv` private data field was initialized during creation of + // the `bindings::hwrng` in `Self::init_hwrng` method. This callback is only + // called once the driver is registered. + let data = unsafe { T::Data::from_pointer((*rng).priv_ as *const _) }; + T::cleanup(data); + } + + unsafe extern "C" fn read_callback( + rng: *mut bindings::hwrng, + data: *mut c_types::c_void, + max: usize, + wait: bindings::bool_, + ) -> c_types::c_int { + from_kernel_result! { + // SAFETY: `priv` private data field was initialized during creation of + // the `bindings::hwrng` in `Self::init_hwrng` method. This callback is only + // called once the driver is registered. + let drv_data = unsafe { T::Data::borrow((*rng).priv_ as *const _) }; + + // SAFETY: Slice is created from `data` and `max` arguments that are C's buffer + // along with its size in bytes that are safe for this conversion. + let buffer = unsafe { from_raw_parts_mut(data as *mut u8, max) }; + let ret = T::read(drv_data, buffer, wait)?; + Ok(ret as _) + } + } +} + +impl Default for Registration { + fn default() -> Self { + Self::new() + } +} + +/// Represents which callbacks of [`struct hwrng`] should be populated with pointers. +pub struct ToUse { + /// The `init` field of [`struct hwrng`]. + pub init: bool, + + /// The `cleanup` field of [`struct hwrng`]. + pub cleanup: bool, +} + +/// A constant version where all values are to set to `false`, that is, all supported fields will +/// be set to null pointers. +pub const USE_NONE: ToUse = ToUse { + init: false, + cleanup: false, +}; + +/// Defines the [`Operations::TO_USE`] field based on a list of fields to be populated. +#[macro_export] +macro_rules! declare_hwrng_operations { + () => { + const TO_USE: $crate::hwrng::ToUse = $crate::hwrng::USE_NONE; + }; + ($($i:ident),+) => { + #[allow(clippy::needless_update)] + const TO_USE: kernel::hwrng::ToUse = + $crate::hwrng::ToUse { + $($i: true),+ , + ..$crate::hwrng::USE_NONE + }; + }; +} + +// SAFETY: `Registration` does not expose any of its state across threads. +unsafe impl Sync for Registration {} + +// SAFETY: `Registration` is not restricted to a single thread, +// its `T::Data` is also `Send` so it may be moved to different threads. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for Registration {} + +impl Drop for Registration { + /// Removes the registration from the kernel if it has completed successfully before. + fn drop(&mut self) { + // SAFETY: The instance of Registration is unregistered only + // after being initialized and registered before. + if self.registered { + unsafe { bindings::hwrng_unregister(self.hwrng.get()) }; + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 2209f779fa99b6..4aa4fe45861174 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -54,6 +54,7 @@ mod error; pub mod file; pub mod file_operations; pub mod gpio; +pub mod hwrng; pub mod irq; pub mod miscdev; pub mod mm;