Skip to content

Commit 2f36f22

Browse files
author
Maciej Falkowski
committed
rust: add Hardware Random Number Generator subsystem
This patch adds abstractions over the hwrng subsystem: - trait `HwrngOperations` that defines operations of a hwrng driver. - declare_hwrng_operations!() macro that populates callback pointers located in hwrng structure. - Registeration<T> structure for hwrng's drivers. Signed-off-by: Maciej Falkowski <[email protected]>
1 parent 675846f commit 2f36f22

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed

rust/kernel/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <linux/cdev.h>
44
#include <linux/errname.h>
55
#include <linux/fs.h>
6+
#include <linux/hw_random.h>
67
#include <linux/module.h>
78
#include <linux/random.h>
89
#include <linux/slab.h>

rust/kernel/hw_random.rs

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Hardware Random Number Generator.
4+
//!
5+
//! C header: [`include/linux/hw_random.h`](../../../../include/linux/hw_random.h)
6+
7+
use alloc::{boxed::Box, slice::from_raw_parts_mut};
8+
9+
use crate::{
10+
bindings, c_types, error::from_kernel_result, str::CStr, types::PointerWrapper, Error, Result,
11+
};
12+
13+
use core::{cell::UnsafeCell, marker::PhantomData, pin::Pin};
14+
15+
/// This trait is implemented in order to provide callbacks to `struct hwrng`.
16+
pub trait HwrngOperations: Sized + 'static {
17+
/// The methods to use to populate [`struct hwrng`].
18+
const TO_USE: ToUse;
19+
20+
/// The pointer type that will be used to hold user-defined data type.
21+
type Data: PointerWrapper = Box<Self>;
22+
23+
/// Initialization callback, can be left undefined.
24+
fn init(_data: <Self::Data as PointerWrapper>::Borrowed<'_>) -> Result {
25+
Err(Error::EINVAL)
26+
}
27+
28+
/// Cleanup callback, can be left undefined.
29+
fn cleanup(_data: <Self::Data as PointerWrapper>::Borrowed<'_>) {}
30+
31+
/// Read data into the provided buffer.
32+
/// Drivers can fill up to max bytes of data into the buffer.
33+
/// The buffer is aligned for any type and max is a multiple of 4 and >= 32 bytes.
34+
fn read(
35+
data: <Self::Data as PointerWrapper>::Borrowed<'_>,
36+
buffer: &mut [i8],
37+
wait: bool,
38+
) -> Result<i32>;
39+
}
40+
41+
/// Registration structure for Hardware Random Number Generator driver.
42+
pub struct Registration<T: HwrngOperations> {
43+
hwrng: UnsafeCell<bindings::hwrng>,
44+
_p: PhantomData<T>,
45+
}
46+
47+
impl<'a, T: HwrngOperations> Registration<T> {
48+
fn init_hwrng(
49+
hwrng: &mut bindings::hwrng,
50+
name: &'a CStr,
51+
quality: u16,
52+
data: *const core::ffi::c_void,
53+
) {
54+
hwrng.name = name.as_char_ptr();
55+
56+
hwrng.init = if T::TO_USE.init {
57+
Some(init_callback::<T>)
58+
} else {
59+
None
60+
};
61+
hwrng.cleanup = if T::TO_USE.cleanup {
62+
Some(cleanup_callback::<T>)
63+
} else {
64+
None
65+
};
66+
hwrng.data_present = None;
67+
hwrng.data_read = None;
68+
hwrng.read = Some(read_callback::<T>);
69+
70+
hwrng.priv_ = data as _;
71+
hwrng.quality = quality;
72+
73+
// SAFETY: All fields are properly initialized as
74+
// remaining fields `list`, `ref` and `cleanup_done` are already
75+
// zeroed by `bindings::hwrng::default()` call.
76+
}
77+
78+
/// Registers a hwrng device within the rest of the kernel.
79+
///
80+
/// It must be pinned because the memory block that represents the registration.
81+
fn register(&self) -> Result {
82+
// SAFETY: register function requires initialized `bindings::hwrng`.
83+
// It is called in `Registration<T>::new_pinned` after initialization of the structure
84+
// that quarantees safety.
85+
let ret = unsafe { bindings::hwrng_register(&mut *self.hwrng.get()) };
86+
87+
if ret < 0 {
88+
return Err(Error::from_kernel_errno(ret));
89+
}
90+
Ok(())
91+
}
92+
93+
/// Returns a registered and pinned, heap-allocated representation of the registration.
94+
pub fn new_pinned(name: &'a CStr, quality: u16, data: T::Data) -> Result<Pin<Box<Self>>> {
95+
let data_pointer = <T::Data as PointerWrapper>::into_pointer(data);
96+
97+
let registeration = Self {
98+
hwrng: UnsafeCell::new(bindings::hwrng::default()),
99+
_p: PhantomData::default(),
100+
};
101+
102+
// SAFETY: registeration contains allocated and set to zero `bindings::hwrng` structure.
103+
Self::init_hwrng(
104+
unsafe { &mut *registeration.hwrng.get() },
105+
name,
106+
quality,
107+
data_pointer,
108+
);
109+
110+
let pinned = Pin::from(Box::try_new(registeration)?);
111+
112+
pinned.as_ref().register()?;
113+
114+
Ok(pinned)
115+
}
116+
}
117+
118+
/// Represents which callbacks of [`struct hwrng`] should be populated with pointers.
119+
pub struct ToUse {
120+
/// The `init` field of [`struct hwrng`].
121+
pub init: bool,
122+
123+
/// The `cleanup` field of [`struct hwrng`].
124+
pub cleanup: bool,
125+
}
126+
127+
/// A constant version where all values are to set to `false`, that is, all supported fields will
128+
/// be set to null pointers.
129+
pub const USE_NONE: ToUse = ToUse {
130+
init: false,
131+
cleanup: false,
132+
};
133+
134+
/// Defines the [`HwrngOperations::TO_USE`] field based on a list of fields to be populated.
135+
#[macro_export]
136+
macro_rules! declare_hwrng_operations {
137+
() => {
138+
const TO_USE: $crate::hw_random::ToUse = $crate::hw_random::USE_NONE;
139+
};
140+
($($i:ident),+) => {
141+
const TO_USE: kernel::hw_random::ToUse =
142+
$crate::hw_random::ToUse {
143+
$($i: true),+ ,
144+
..$crate::hw_random::USE_NONE
145+
};
146+
};
147+
}
148+
149+
unsafe extern "C" fn init_callback<T: HwrngOperations>(
150+
rng: *mut bindings::hwrng,
151+
) -> c_types::c_int {
152+
from_kernel_result! {
153+
// SAFETY: `priv` private data field was initialized during creation of
154+
// the `bindings::hwrng` in `Self::init_hwrng` function. This callback
155+
// is only called once `new_pinned` suceeded previously which guarantees safety.
156+
let data = unsafe { T::Data::borrow((*rng).priv_ as *const core::ffi::c_void) };
157+
T::init(data)?;
158+
Ok(0)
159+
}
160+
}
161+
162+
unsafe extern "C" fn cleanup_callback<T: HwrngOperations>(rng: *mut bindings::hwrng) {
163+
// SAFETY: `priv` private data field was initialized during creation of
164+
// the `bindings::hwrng` in `Self::init_hwrng` function. This callback
165+
// is only called once `new_pinned` suceeded previously which guarantees safety.
166+
let data = unsafe { T::Data::borrow((*rng).priv_ as *const core::ffi::c_void) };
167+
T::cleanup(data);
168+
}
169+
170+
unsafe extern "C" fn read_callback<T: HwrngOperations>(
171+
rng: *mut bindings::hwrng,
172+
data: *mut c_types::c_void,
173+
max: usize,
174+
wait: bindings::bool_,
175+
) -> c_types::c_int {
176+
from_kernel_result! {
177+
// SAFETY: `priv` private data field was initialized during creation of
178+
// the `bindings::hwrng` in `Self::init_hwrng` function. This callback
179+
// is only called once `new_pinned` suceeded previously which guarantees safety.
180+
let drv_data = unsafe { T::Data::borrow((*rng).priv_ as *const core::ffi::c_void) };
181+
182+
// SAFETY: Slice is created from `data` and `max` arguments that are C api buffer
183+
// and its size in bytes which are safe for this conversion.
184+
let buffer = unsafe { from_raw_parts_mut(data as *mut c_types::c_char, max) };
185+
let ret = T::read(drv_data, buffer, wait)?;
186+
Ok(ret)
187+
}
188+
}
189+
190+
// SAFETY: `Registration` does not expose any of its state across threads.
191+
unsafe impl<T: HwrngOperations> Sync for Registration<T> {}
192+
193+
impl<T: HwrngOperations> Drop for Registration<T> {
194+
/// Removes the registration from the kernel if it has completed successfully before.
195+
fn drop(&mut self) {
196+
// SAFETY: The instance of Registration<T> is returned from `new_pinned`
197+
// function after being initialized and registered.
198+
unsafe { bindings::hwrng_unregister(&mut *self.hwrng.get()) };
199+
}
200+
}

rust/kernel/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ mod error;
5151
pub mod file;
5252
pub mod file_operations;
5353
pub mod gpio;
54+
pub mod hw_random;
5455
pub mod irq;
5556
pub mod miscdev;
5657
pub mod pages;

0 commit comments

Comments
 (0)