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

fix(virtio/pci): put common config into volatile reference #1145

Merged
merged 2 commits into from
Apr 23, 2024
Merged
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
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ build-time = "0.1.3"
async-trait = "0.1.79"
async-lock = { version = "3.3.0", default-features = false }
simple-shell = { version = "0.0.1", optional = true }
volatile = "0.5.3"

[dependencies.smoltcp]
version = "0.11"
Expand Down
120 changes: 77 additions & 43 deletions src/drivers/virtio/transport/pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
#![allow(dead_code)]

use alloc::vec::Vec;
use core::ptr::NonNull;
use core::sync::atomic::{fence, Ordering};
use core::{mem, ptr};

use volatile::{map_field, VolatileRef};

#[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))]
use crate::arch::kernel::interrupts::*;
use crate::arch::memory_barrier;
Expand Down Expand Up @@ -385,61 +388,74 @@ impl UniCapsColl {
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: &'static mut ComCfgRaw,
com_cfg: VolatileRef<'static, ComCfgRaw>,
/// 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: &'static mut ComCfgRaw, rank: u8) -> Self {
fn new(raw: VolatileRef<'static, ComCfgRaw>, rank: u8) -> Self {
ComCfg { com_cfg: raw, rank }
}
}

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

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);
}

/// Sets the size of a given virtqueue. In case the provided size exceeds the maximum allowed
/// size, the size is set to this maximum instead. Else size is set to the provided value.
///
/// Returns the set size in form of a `u16`.
pub fn set_vq_size(&mut self, size: u16) -> u16 {
self.raw.queue_select = self.vq_index;
self.select_queue();
let raw = self.raw.as_mut_ptr();
let queue_size = map_field!(raw.queue_size);

if self.raw.queue_size >= size {
self.raw.queue_size = size;
if queue_size.read() >= size {
queue_size.write(size);
}

self.raw.queue_size
queue_size.read()
}

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

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

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

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

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

Expand All @@ -450,57 +466,65 @@ 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<'_>> {
self.com_cfg.queue_select = index;
let com_cfg = self.com_cfg.as_mut_ptr();

map_field!(com_cfg.queue_select).write(index);

if self.com_cfg.queue_size == 0 {
if map_field!(com_cfg.queue_size).read() == 0 {
None
} else {
Some(VqCfgHandler {
vq_index: index,
raw: self.com_cfg,
raw: self.com_cfg.borrow_mut(),
})
}
}

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

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

/// 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();
self.com_cfg.device_status = u8::from(device::Status::FAILED);
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).write(u8::from(device::Status::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();
self.com_cfg.device_status |= u8::from(device::Status::ACKNOWLEDGE);
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::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();
self.com_cfg.device_status |= u8::from(device::Status::DRIVER);
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::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();
self.com_cfg.device_status |= u8::from(device::Status::FEATURES_OK);
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::FEATURES_OK));
}

/// In order to correctly check feature negotiaten, this function
Expand All @@ -511,7 +535,8 @@ 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();
self.com_cfg.device_status & u8::from(device::Status::FEATURES_OK)
let com_cfg = self.com_cfg.as_ptr();
map_field!(com_cfg.device_status).read() & u8::from(device::Status::FEATURES_OK)
== u8::from(device::Status::FEATURES_OK)
}

Expand All @@ -520,54 +545,61 @@ impl ComCfg {
/// After this call, the device is "live"!
pub fn drv_ok(&mut self) {
memory_barrier();
self.com_cfg.device_status |= u8::from(device::Status::DRIVER_OK);
let com_cfg = self.com_cfg.as_mut_ptr();
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::DRIVER_OK));
}

/// Returns the features offered by the device. Coded in a 64bit value.
pub fn dev_features(&mut self) -> u64 {
let device_feature_select = ptr::from_mut(&mut self.com_cfg.device_feature_select);
let device_feature = ptr::from_mut(&mut self.com_cfg.device_feature);
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);

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

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

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

// read low 32 bits of device features
dev_feat |= u64::from(unsafe { device_feature.read_volatile() });
dev_feat |= u64::from(device_feature.read());

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 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();
self.com_cfg.driver_feature_select = 0;
driver_feature_select.write(0);
memory_barrier();

// write low 32 bits of device features
self.com_cfg.driver_feature = low;
driver_feature.write(low);

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

// write high 32 bits of device features
self.com_cfg.driver_feature = high;
driver_feature.write(high);
}
}

Expand Down Expand Up @@ -601,9 +633,8 @@ struct ComCfgRaw {
// Common configuration raw does NOT provide a PUBLIC
// interface.
impl ComCfgRaw {
/// Returns a boxed [ComCfgRaw] structure. The box points to the actual structure inside the
/// PCI devices memory space.
fn map(cap: &PciCap) -> Option<&'static mut ComCfgRaw> {
/// Returns a reference to the actual structure inside the PCI devices memory space.
fn map(cap: &PciCap) -> Option<VolatileRef<'static, ComCfgRaw>> {
if cap.bar.length < u64::from(cap.length + cap.offset) {
error!("Common config of with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
cap.id,
Expand All @@ -621,10 +652,13 @@ impl ComCfgRaw {
}

let virt_addr_raw = cap.bar.mem_addr + cap.offset;
let ptr = NonNull::new(ptr::with_exposed_provenance_mut::<ComCfgRaw>(
virt_addr_raw.into(),
))
.unwrap();

// Create mutable reference to the PCI structure in PCI memory
let com_cfg_raw: &mut ComCfgRaw =
unsafe { &mut *(ptr::with_exposed_provenance_mut(virt_addr_raw.into())) };
let com_cfg_raw = unsafe { VolatileRef::new(ptr) };

Some(com_cfg_raw)
}
Expand Down