Skip to content

Simple Rust driver that touches real hardware: PR 6 #435

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions drivers/char/hw_random/bcm2835_rng_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use kernel::{
of::ConstOfMatchTable,
platdev::PlatformDriver,
prelude::*,
sync::Ref,
{c_str, platdev},
};

Expand All @@ -24,44 +25,68 @@ module! {
license: b"GPL v2",
}

struct RngDevice;
struct RngDevice {
zero: u32,
}

impl RngDevice {
fn new() -> Self {
Self { zero: 0 }
}
}

impl FileOpener<()> for RngDevice {
fn open(_state: &()) -> Result<Self::Wrapper> {
Ok(Box::try_new(RngDevice)?)
impl FileOpener<Ref<Self>> for RngDevice {
fn open(state: &Ref<Self>) -> Result<Self::Wrapper> {
Ok(state.clone())
}
}

impl FileOperations for RngDevice {
type Wrapper = Ref<Self>;

kernel::declare_file_operations!(read);

fn read<T: IoBufferWriter>(_: &Self, _: &File, data: &mut T, offset: u64) -> Result<usize> {
fn read<T: IoBufferWriter>(
this: &Ref<Self>,
_: &File,
data: &mut T,
offset: u64,
) -> Result<usize> {
// 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)?;
data.write(&this.zero)?;
Ok(4)
}
}

struct RngDriver;

impl PlatformDriver for RngDriver {
type DrvData = Pin<Box<miscdev::Registration<()>>>;
type DrvData = Pin<Box<miscdev::Registration<Ref<RngDevice>>>>;

fn probe(device_id: i32) -> Result<Self::DrvData> {
pr_info!("probing discovered hwrng with id {}\n", device_id);
let device = Ref::try_new(RngDevice::new())?;
let drv_data =
miscdev::Registration::new_pinned::<RngDevice>(c_str!("rust_hwrng"), None, ())?;
miscdev::Registration::new_pinned::<RngDevice>(c_str!("rust_hwrng"), None, device)?;
Ok(drv_data)
}

fn remove(device_id: i32, _drv_data: Self::DrvData) -> Result {
pr_info!("removing hwrng with id {}\n", device_id);
Ok(())
}

fn shutdown(device_id: i32, drv_data: &miscdev::Registration<Ref<RngDevice>>) {
pr_info!(
"shutting down hwrng with id {}, zero {}\n",
device_id,
drv_data.context.zero
);
}
}

struct RngModule {
Expand Down
35 changes: 32 additions & 3 deletions rust/kernel/platdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
types::PointerWrapper,
};
use alloc::boxed::Box;
use core::{marker::PhantomPinned, pin::Pin};
use core::{marker::PhantomPinned, ops::Deref, pin::Pin};

/// A registration of a platform device.
#[derive(Default)]
Expand Down Expand Up @@ -70,14 +70,33 @@ extern "C" fn remove_callback<P: PlatformDriver>(
// - 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.
// `remove` is the canonical kernel location to free driver data,
// no borrows are outstanding as the driver model guarantees that
// `remove` is called only while no other `bindings::platform_driver`
// callbacks are in progress,
// 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)
}
}

extern "C" fn shutdown_callback<P: PlatformDriver>(pdev: *mut bindings::platform_device) {
// 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 { rust_helper_platform_get_drvdata(pdev) };
// SAFETY:
// - `ptr` was returned by a previous call to `P::DrvData::into_pointer`;
// - the structure which `ptr` points at, is guaranteed to exist: it's
// deallocated in `remove`, which is guaranteed by the driver model to
// execute after all other `bindings::platform_driver` callbacks have
// returned;
// so `ptr` is safe to borrow here.
let drv_data = unsafe { P::DrvData::borrow(ptr) };
P::shutdown(device_id, &drv_data);
}

impl Registration {
fn register<P: PlatformDriver>(
self: Pin<&mut Self>,
Expand All @@ -97,6 +116,7 @@ impl Registration {
}
this.pdrv.probe = Some(probe_callback::<P>);
this.pdrv.remove = Some(remove_callback::<P>);
this.pdrv.shutdown = Some(shutdown_callback::<P>);
// SAFETY:
// - `this.pdrv` lives at least until the call to `platform_driver_unregister()` returns.
// - `name` pointer has static lifetime.
Expand Down Expand Up @@ -163,4 +183,13 @@ pub trait PlatformDriver {
/// 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;

/// Platform driver shutdown.
///
/// Called at shut-down time to quiesce the device.
fn shutdown(
_device_id: i32,
_drv_data: &<<Self::DrvData as PointerWrapper>::Borrowed as Deref>::Target,
) {
}
}