Skip to content

virtio: provide a rust interface of virtio_driver in rust #841

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

Open
wants to merge 1 commit into
base: rust
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include <linux/sysctl.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/virtio.h>
#include <linux/vringh.h>
#include <linux/virtio_config.h>
#include <linux/virtio_ring.h>
#include <uapi/linux/android/binder.h>
#include <linux/fs_parser.h>

Expand Down
3 changes: 3 additions & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ pub mod user_ptr;
#[cfg(CONFIG_KUNIT)]
pub mod kunit;

#[cfg(CONFIG_VIRTIO)]
pub mod virtio;

#[doc(hidden)]
pub use build_error::build_error;

Expand Down
211 changes: 211 additions & 0 deletions rust/kernel/virtio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-2.0

//! Virtio.
//!
//! C header: [`include/linux/virtio.h`](../../../../include/linux/virtio.h)

use crate::{
bindings, device, driver, error::from_kernel_result, str::CStr, to_result,
types::PointerWrapper, Result, ThisModule,
};

/// A registration of a virtio driver.
pub type Registration<T> = driver::Registration<Adapter<T>>;

/// Id of a Virtio device.
#[derive(Clone, Copy)]
pub struct DeviceId {
/// Device id.
pub device: u32,

/// Vendor id of the virtio device.
pub vendor: u32,
}

// SAFETY: `ZERO` is all zeroed-out and `to_rawid` stores in `virtio_id::data`.
unsafe impl const driver::RawDeviceId for DeviceId {
type RawType = bindings::virtio_device_id;
const ZERO: Self::RawType = bindings::virtio_device_id {
device: 0,
vendor: 0,
};

fn to_rawid(&self, _offset: isize) -> Self::RawType {
bindings::virtio_device_id {
device: self.device,
vendor: self.vendor,
}
}
}

/// An adapter for the registration of virtio drivers.
pub struct Adapter<T: Driver>(T);

impl<T: Driver> driver::DriverOps for Adapter<T> {
type RegType = bindings::virtio_driver;

unsafe fn register(
reg: *mut bindings::virtio_driver,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
// SAFETY: By the safety requirements of this function (defined in the trait definition),
// `reg` is non-null and valid.
let vdrv = unsafe { &mut *reg };

vdrv.driver.name = name.as_char_ptr();
vdrv.driver.owner = module.0;
vdrv.probe = Some(Self::probe_callback);
vdrv.remove = Some(Self::remove_callback);
if let Some(t) = T::ID_TABLE {
vdrv.id_table = t.as_ref();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want of ids to work here, you must set vdrv.driver.of_match_table -- see platform.rs for an example.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that in the virtio drivers of C sides, the common way to match the driver with the device is to use 'id_table' rather than of. I write a macro define_virtio_id_table to implement this. It's more like define_amba_id_table in the amba.rs.

// SAFETY:
// - `vdrv` lives at least until the call to `register_virtio_driver()` returns.
// - `name` pointer has static lifetime.
// - `module.0` lives at least as long as the module.
// - `probe()` and `remove()` are static functions.
// - `id_table` is either a raw pointer with static lifetime,
// as guaranteed by the [`driver::IdTable`] type, or null.
to_result(unsafe { bindings::register_virtio_driver(reg) })
}

unsafe fn unregister(reg: *mut bindings::virtio_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
// `register_virtio_driver`.
unsafe { bindings::unregister_virtio_driver(reg) };
}
}

impl<T: Driver> Adapter<T> {
extern "C" fn probe_callback(vdev: *mut bindings::virtio_device) -> core::ffi::c_int {
from_kernel_result! {
// SAFETY: `vdev` 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
// `vdev`.
let mut dev = unsafe { Device::from_ptr(vdev) };
let data = T::probe(&mut dev)?;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of identifiers are allowed to carry some data with them. If the match happened through one of them, ideally you'll pass this data to the probe function. See get_id_info in platform.rs for an example of this. (Perhaps we should move this function to the driver module so all drivers that support of can use it.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The virtio drivers use id_table to match the driver with the device, which does not contain any data field. The probe function in the virtio Driver is more like the probe function in the amba Driver.
It's reasonable to move get_id_info into the driver module. I can open a new PR about it.

// SAFETY: `vdev` is guaranteed to be a valid, non-null pointer.
unsafe{(*vdev).priv_ = T::Data::into_pointer(data) as _;}
Ok(0)
}
}

extern "C" fn remove_callback(vdev: *mut bindings::virtio_device) {
// SAFETY: `vdev` is guaranteed to be a valid, non-null pointer.
let ptr = unsafe { *vdev }.priv_;
// 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) };
T::remove(&data);
<T::Data as driver::DeviceRemoval>::device_remove(&data);
}
}

/// A virtio driver.
pub trait Driver {
/// Data stored on device by driver.
type Data: PointerWrapper + Send + Sync + driver::DeviceRemoval = ();

/// The table of device ids supported by the driver.
const ID_TABLE: Option<driver::IdTable<'static, DeviceId, ()>> = None;

/// Probes for the device with the given id.
fn probe(dev: &mut Device) -> Result<Self::Data>;

/// Cleans any resources up that are associated with the device.
///
/// This is called when the driver is detached from the device.
fn remove(_data: &Self::Data) {}
}

/// A Virtio device.
///
/// # Invariants
///
/// The field `ptr` is non-null and valid for the lifetime of the object.
pub struct Device {
ptr: *mut bindings::virtio_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::virtio_device) -> Self {
// INVARIANT: The safety requirements of the function ensure the lifetime invariant.
Self { ptr }
}
}

// SAFETY: The device returned by `raw_device` is the raw virtio 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 virtio driver.
///
/// # Examples
///
/// ```
/// # use kernel::{virtio, define_virtio_id_table, module_virtio_driver};
/// #
/// struct MyDriver;
/// impl virtio::Driver for MyDriver {
/// // [...]
/// # fn probe(_dev: &mut virtio::Device) -> Result {
/// # Ok(())
/// # }
/// # define_virtio_id_table! {(), [
/// # ({ device: 0x00000001, vendor: 0xffffffff }, None),
/// # ]}
/// }
///
/// module_virtio_driver! {
/// type: MyDriver,
/// name: b"module_name",
/// author: b"Author name",
/// license: b"GPL",
/// }
/// ```
#[macro_export]
macro_rules! module_virtio_driver {
($($f:tt)*) => {
$crate::module_driver!(<T>, $crate::virtio::Adapter<T>, { $($f)* });
};
}

/// Defines the id table for virtio devices.
///
/// # Examples
///
/// ```
/// # use kernel::{virtio, define_virtio_id_table};
/// #
/// # struct Sample;
/// # impl kernel::virtio::Driver for Sample {
/// # fn probe(_dev: &mut virtio::Device) -> Result {
/// # Ok(())
/// # }
/// define_virtio_id_table! {(), [
/// ({ device: 0x00000001, vendor: 0xffffffff }, None),
/// ]}
/// # }
/// ```
#[macro_export]
macro_rules! define_virtio_id_table {
($data_type:ty, $($t:tt)*) => {
$crate::define_id_table!(ID_TABLE, $crate::virtio::DeviceId, $data_type, $($t)*);
};
}