Skip to content

Commit

Permalink
refactor(virtio/pci): migrate CommonCfg to virtio-def
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Kröning <[email protected]>
  • Loading branch information
mkroening committed Apr 26, 2024
1 parent 1b84cd7 commit 343a3a5
Showing 1 changed file with 74 additions and 58 deletions.
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}!",
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
// 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

0 comments on commit 343a3a5

Please sign in to comment.