Skip to content
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

feat: add virtio-def crate for virtio definitions #1149

Merged
merged 9 commits into from
Apr 30, 2024
Prev Previous commit
Next Next commit
refactor(virtio/pci): migrate CommonCfg to virtio-def
Signed-off-by: Martin Kröning <martin.kroening@eonerc.rwth-aachen.de>
  • Loading branch information
mkroening committed Apr 30, 2024
commit 84daffc6f1c6f62b8fb78a1c671ea3bb39d82eab
132 changes: 74 additions & 58 deletions src/drivers/virtio/transport/pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use core::ptr::NonNull;
use core::sync::atomic::{fence, Ordering};
use core::{mem, ptr};

use virtio_def::pci::{CommonCfg, CommonCfgVolatileFieldAccess};
use virtio_def::DeviceStatus;
use volatile::{map_field, VolatileRef};
use volatile::VolatileRef;

#[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))]
use crate::arch::kernel::interrupts::*;
Expand Down Expand Up @@ -221,7 +222,7 @@ impl PciCap {
}

/// Returns a reference to the actual structure inside the PCI devices memory space.
fn map_common_cfg(&self) -> Option<VolatileRef<'static, ComCfgRaw>> {
fn map_common_cfg(&self) -> Option<VolatileRef<'static, CommonCfg>> {
if self.bar.length < u64::from(self.length + self.offset) {
error!("Common config of with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
mkroening marked this conversation as resolved.
Show resolved Hide resolved
self.id,
Expand All @@ -231,15 +232,15 @@ impl PciCap {
return None;
}

// Using "as u32" is safe here as ComCfgRaw has a defined size smaller 2^31-1
// Using "as u32" is safe here as CommonCfg has a defined size smaller 2^31-1
mkroening marked this conversation as resolved.
Show resolved Hide resolved
// Drivers MAY do this check. See Virtio specification v1.1. - 4.1.4.1
if self.length < MemLen::from(mem::size_of::<ComCfgRaw>() * 8) {
if self.length < MemLen::from(mem::size_of::<CommonCfg>() * 8) {
error!("Common config of with id {}, does not represent actual structure specified by the standard!", self.id);
return None;
}

let virt_addr_raw = self.bar.mem_addr + self.offset;
let ptr = NonNull::new(ptr::with_exposed_provenance_mut::<ComCfgRaw>(
let ptr = NonNull::new(ptr::with_exposed_provenance_mut::<CommonCfg>(
virt_addr_raw.into(),
))
.unwrap();
Expand Down Expand Up @@ -410,36 +411,38 @@ impl UniCapsColl {
}
}

/// Wraps a [ComCfgRaw] in order to preserve
/// Wraps a [`CommonCfg`] in order to preserve
/// the original structure.
///
/// Provides a safe API for Raw structure and allows interaction with the device via
/// the structure.
pub struct ComCfg {
/// References the raw structure in PCI memory space. Is static as
/// long as the device is present, which is mandatory in order to let this code work.
com_cfg: VolatileRef<'static, ComCfgRaw>,
com_cfg: VolatileRef<'static, CommonCfg>,
/// Preferences of the device for this config. From 1 (highest) to 2^7-1 (lowest)
rank: u8,
}

// Private interface of ComCfg
impl ComCfg {
fn new(raw: VolatileRef<'static, ComCfgRaw>, rank: u8) -> Self {
fn new(raw: VolatileRef<'static, CommonCfg>, rank: u8) -> Self {
ComCfg { com_cfg: raw, rank }
}
}

pub struct VqCfgHandler<'a> {
vq_index: u16,
raw: VolatileRef<'a, ComCfgRaw>,
raw: VolatileRef<'a, CommonCfg>,
}

impl<'a> VqCfgHandler<'a> {
// TODO: Create type for queue selected invariant to get rid of `self.select_queue()` everywhere.
fn select_queue(&mut self) {
let raw = self.raw.as_mut_ptr();
map_field!(raw.queue_select).write(self.vq_index);
self.raw
.as_mut_ptr()
.queue_select()
.write(self.vq_index.into());
}

/// Sets the size of a given virtqueue. In case the provided size exceeds the maximum allowed
Expand All @@ -448,44 +451,47 @@ impl<'a> VqCfgHandler<'a> {
/// Returns the set size in form of a `u16`.
pub fn set_vq_size(&mut self, size: u16) -> u16 {
self.select_queue();
let raw = self.raw.as_mut_ptr();
let queue_size = map_field!(raw.queue_size);
let queue_size = self.raw.as_mut_ptr().queue_size();

if queue_size.read() >= size {
queue_size.write(size);
if queue_size.read().get() >= size {
queue_size.write(size.into());
}

queue_size.read()
queue_size.read().get()
}

pub fn set_ring_addr(&mut self, addr: PhysAddr) {
self.select_queue();
let raw = self.raw.as_mut_ptr();
map_field!(raw.queue_desc).write(addr.as_u64());
self.raw
.as_mut_ptr()
.queue_desc()
.write(addr.as_u64().into());
}

pub fn set_drv_ctrl_addr(&mut self, addr: PhysAddr) {
self.select_queue();
let raw = self.raw.as_mut_ptr();
map_field!(raw.queue_driver).write(addr.as_u64());
self.raw
.as_mut_ptr()
.queue_driver()
.write(addr.as_u64().into());
}

pub fn set_dev_ctrl_addr(&mut self, addr: PhysAddr) {
self.select_queue();
let raw = self.raw.as_mut_ptr();
map_field!(raw.queue_device).write(addr.as_u64());
self.raw
.as_mut_ptr()
.queue_device()
.write(addr.as_u64().into());
}

pub fn notif_off(&mut self) -> u16 {
self.select_queue();
let raw = self.raw.as_mut_ptr();
map_field!(raw.queue_notify_off).read()
self.raw.as_mut_ptr().queue_notify_off().read().get()
}

pub fn enable_queue(&mut self) {
self.select_queue();
let raw = self.raw.as_mut_ptr();
map_field!(raw.queue_enable).write(1);
self.raw.as_mut_ptr().queue_enable().write(1.into());
}
}

Expand All @@ -496,11 +502,9 @@ impl ComCfg {
///
/// INFO: The queue size is automatically bounded by constant `src::config:VIRTIO_MAX_QUEUE_SIZE`.
pub fn select_vq(&mut self, index: u16) -> Option<VqCfgHandler<'_>> {
let com_cfg = self.com_cfg.as_mut_ptr();

map_field!(com_cfg.queue_select).write(index);
self.com_cfg.as_mut_ptr().queue_select().write(index.into());

if map_field!(com_cfg.queue_size).read() == 0 {
if self.com_cfg.as_mut_ptr().queue_size().read().get() == 0 {
None
} else {
Some(VqCfgHandler {
Expand All @@ -512,49 +516,58 @@ impl ComCfg {

/// Returns the device status field.
pub fn dev_status(&self) -> u8 {
let com_cfg = self.com_cfg.as_ptr();
map_field!(com_cfg.device_status).read().bits()
self.com_cfg.as_ptr().device_status().read().bits()
}

/// Resets the device status field to zero.
pub fn reset_dev(&mut self) {
memory_barrier();
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).write(DeviceStatus::empty());
self.com_cfg
.as_mut_ptr()
.device_status()
.write(DeviceStatus::empty());
}

/// Sets the device status field to FAILED.
/// A driver MUST NOT initialize and use the device any further after this.
/// A driver MAY use the device again after a proper reset of the device.
pub fn set_failed(&mut self) {
memory_barrier();
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).write(DeviceStatus::FAILED);
self.com_cfg
.as_mut_ptr()
.device_status()
.write(DeviceStatus::FAILED);
}

/// Sets the ACKNOWLEDGE bit in the device status field. This indicates, the
/// OS has notived the device
pub fn ack_dev(&mut self) {
memory_barrier();
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | DeviceStatus::ACKNOWLEDGE);
self.com_cfg
.as_mut_ptr()
.device_status()
.update(|s| s | DeviceStatus::ACKNOWLEDGE);
}

/// Sets the DRIVER bit in the device status field. This indicates, the OS
/// know how to run this device.
pub fn set_drv(&mut self) {
memory_barrier();
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | DeviceStatus::DRIVER);
self.com_cfg
.as_mut_ptr()
.device_status()
.update(|s| s | DeviceStatus::DRIVER);
}

/// Sets the FEATURES_OK bit in the device status field.
///
/// Drivers MUST NOT accept new features after this step.
pub fn features_ok(&mut self) {
memory_barrier();
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | DeviceStatus::FEATURES_OK);
self.com_cfg
.as_mut_ptr()
.device_status()
.update(|s| s | DeviceStatus::FEATURES_OK);
}

/// In order to correctly check feature negotiaten, this function
Expand All @@ -565,8 +578,9 @@ impl ComCfg {
/// otherwise, the device does not support our subset of features and the device is unusable.
pub fn check_features(&self) -> bool {
memory_barrier();
let com_cfg = self.com_cfg.as_ptr();
map_field!(com_cfg.device_status)
self.com_cfg
.as_ptr()
.device_status()
.read()
.contains(DeviceStatus::FEATURES_OK)
}
Expand All @@ -576,61 +590,63 @@ impl ComCfg {
/// After this call, the device is "live"!
pub fn drv_ok(&mut self) {
memory_barrier();
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | DeviceStatus::DRIVER_OK);
self.com_cfg
.as_mut_ptr()
.device_status()
.update(|s| s | DeviceStatus::DRIVER_OK);
}

/// Returns the features offered by the device. Coded in a 64bit value.
pub fn dev_features(&mut self) -> u64 {
let com_cfg = self.com_cfg.as_mut_ptr();
let device_feature_select = map_field!(com_cfg.device_feature_select);
let device_feature = map_field!(com_cfg.device_feature);
let device_feature_select = com_cfg.device_feature_select();
let device_feature = com_cfg.device_feature();

// Indicate device to show high 32 bits in device_feature field.
// See Virtio specification v1.1. - 4.1.4.3
memory_barrier();
device_feature_select.write(1);
device_feature_select.write(1.into());
memory_barrier();

// read high 32 bits of device features
let mut dev_feat = u64::from(device_feature.read()) << 32;
let mut dev_feat = u64::from(device_feature.read().get()) << 32;

// Indicate device to show low 32 bits in device_feature field.
// See Virtio specification v1.1. - 4.1.4.3
device_feature_select.write(0);
device_feature_select.write(0.into());
memory_barrier();

// read low 32 bits of device features
dev_feat |= u64::from(device_feature.read());
dev_feat |= u64::from(device_feature.read().get());

dev_feat
}

/// Write selected features into driver_select field.
pub fn set_drv_features(&mut self, feats: u64) {
let com_cfg = self.com_cfg.as_mut_ptr();
let driver_feature_select = map_field!(com_cfg.driver_feature_select);
let driver_feature = map_field!(com_cfg.driver_feature);
let driver_feature_select = com_cfg.driver_feature_select();
let driver_feature = com_cfg.driver_feature();

let high: u32 = (feats >> 32) as u32;
let low: u32 = feats as u32;

// Indicate to device that driver_features field shows low 32 bits.
// See Virtio specification v1.1. - 4.1.4.3
memory_barrier();
driver_feature_select.write(0);
driver_feature_select.write(0.into());
memory_barrier();

// write low 32 bits of device features
driver_feature.write(low);
driver_feature.write(low.into());

// Indicate to device that driver_features field shows high 32 bits.
// See Virtio specification v1.1. - 4.1.4.3
driver_feature_select.write(1);
driver_feature_select.write(1.into());
memory_barrier();

// write high 32 bits of device features
driver_feature.write(high);
driver_feature.write(high.into());
}
}

Expand Down