Skip to content

Commit eb4ac60

Browse files
committed
fix(virtio/pci): put common config into volatile reference
Signed-off-by: Martin Kröning <martin.kroening@eonerc.rwth-aachen.de>
1 parent 79087bc commit eb4ac60

File tree

3 files changed

+87
-43
lines changed

3 files changed

+87
-43
lines changed

Cargo.lock

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ build-time = "0.1.3"
100100
async-trait = "0.1.79"
101101
async-lock = { version = "3.3.0", default-features = false }
102102
simple-shell = { version = "0.0.1", optional = true }
103+
volatile = "0.5.3"
103104

104105
[dependencies.smoltcp]
105106
version = "0.11"

src/drivers/virtio/transport/pci.rs

Lines changed: 77 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
#![allow(dead_code)]
55

66
use alloc::vec::Vec;
7+
use core::ptr::NonNull;
78
use core::sync::atomic::{fence, Ordering};
89
use core::{mem, ptr};
910

11+
use volatile::{map_field, VolatileRef};
12+
1013
#[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))]
1114
use crate::arch::kernel::interrupts::*;
1215
use crate::arch::memory_barrier;
@@ -385,61 +388,77 @@ impl UniCapsColl {
385388
pub struct ComCfg {
386389
/// References the raw structure in PCI memory space. Is static as
387390
/// long as the device is present, which is mandatory in order to let this code work.
388-
com_cfg: &'static mut ComCfgRaw,
391+
com_cfg: VolatileRef<'static, ComCfgRaw>,
389392
/// Preferences of the device for this config. From 1 (highest) to 2^7-1 (lowest)
390393
rank: u8,
391394
}
392395

393396
// Private interface of ComCfg
394397
impl ComCfg {
395-
fn new(raw: &'static mut ComCfgRaw, rank: u8) -> Self {
398+
fn new(raw: VolatileRef<'static, ComCfgRaw>, rank: u8) -> Self {
396399
ComCfg { com_cfg: raw, rank }
397400
}
398401
}
399402

403+
// TODO: Create type for queue selected invariant to get rid of `self.select_queue()` everywhere.
400404
pub struct VqCfgHandler<'a> {
401405
vq_index: u16,
402-
raw: &'a mut ComCfgRaw,
406+
raw: VolatileRef<'a, ComCfgRaw>,
403407
}
404408

405409
impl<'a> VqCfgHandler<'a> {
410+
fn select_queue(&mut self) {
411+
let raw = self.raw.as_mut_ptr();
412+
map_field!(raw.queue_select).write(self.vq_index);
413+
}
414+
406415
/// Sets the size of a given virtqueue. In case the provided size exceeds the maximum allowed
407416
/// size, the size is set to this maximum instead. Else size is set to the provided value.
408417
///
409418
/// Returns the set size in form of a `u16`.
410419
pub fn set_vq_size(&mut self, size: u16) -> u16 {
411-
self.raw.queue_select = self.vq_index;
420+
self.select_queue();
421+
422+
let raw = self.raw.as_mut_ptr();
423+
let queue_size = map_field!(raw.queue_size);
424+
425+
let old = queue_size.read();
412426

413-
if self.raw.queue_size >= size {
414-
self.raw.queue_size = size;
427+
if old > size {
428+
queue_size.write(size);
415429
}
416430

417-
self.raw.queue_size
431+
old.min(size)
418432
}
419433

420434
pub fn set_ring_addr(&mut self, addr: PhysAddr) {
421-
self.raw.queue_select = self.vq_index;
422-
self.raw.queue_desc = addr.as_u64();
435+
self.select_queue();
436+
let raw = self.raw.as_mut_ptr();
437+
map_field!(raw.queue_desc).write(addr.as_u64());
423438
}
424439

425440
pub fn set_drv_ctrl_addr(&mut self, addr: PhysAddr) {
426-
self.raw.queue_select = self.vq_index;
427-
self.raw.queue_driver = addr.as_u64();
441+
self.select_queue();
442+
let raw = self.raw.as_mut_ptr();
443+
map_field!(raw.queue_driver).write(addr.as_u64());
428444
}
429445

430446
pub fn set_dev_ctrl_addr(&mut self, addr: PhysAddr) {
431-
self.raw.queue_select = self.vq_index;
432-
self.raw.queue_device = addr.as_u64();
447+
self.select_queue();
448+
let raw = self.raw.as_mut_ptr();
449+
map_field!(raw.queue_device).write(addr.as_u64());
433450
}
434451

435452
pub fn notif_off(&mut self) -> u16 {
436-
self.raw.queue_select = self.vq_index;
437-
self.raw.queue_notify_off
453+
self.select_queue();
454+
let raw = self.raw.as_mut_ptr();
455+
map_field!(raw.queue_notify_off).read()
438456
}
439457

440458
pub fn enable_queue(&mut self) {
441-
self.raw.queue_select = self.vq_index;
442-
self.raw.queue_enable = 1;
459+
self.select_queue();
460+
let raw = self.raw.as_mut_ptr();
461+
map_field!(raw.queue_enable).write(1);
443462
}
444463
}
445464

@@ -450,57 +469,65 @@ impl ComCfg {
450469
///
451470
/// INFO: The queue size is automatically bounded by constant `src::config:VIRTIO_MAX_QUEUE_SIZE`.
452471
pub fn select_vq(&mut self, index: u16) -> Option<VqCfgHandler<'_>> {
453-
self.com_cfg.queue_select = index;
472+
let com_cfg = self.com_cfg.as_mut_ptr();
454473

455-
if self.com_cfg.queue_size == 0 {
474+
map_field!(com_cfg.queue_select).write(index);
475+
476+
if map_field!(com_cfg.queue_size).read() == 0 {
456477
None
457478
} else {
458479
Some(VqCfgHandler {
459480
vq_index: index,
460-
raw: self.com_cfg,
481+
raw: self.com_cfg.borrow_mut(),
461482
})
462483
}
463484
}
464485

465486
/// Returns the device status field.
466487
pub fn dev_status(&self) -> u8 {
467-
self.com_cfg.device_status
488+
let com_cfg = self.com_cfg.as_ptr();
489+
map_field!(com_cfg.device_status).read()
468490
}
469491

470492
/// Resets the device status field to zero.
471493
pub fn reset_dev(&mut self) {
472494
memory_barrier();
473-
self.com_cfg.device_status = 0;
495+
let com_cfg = self.com_cfg.as_mut_ptr();
496+
map_field!(com_cfg.device_status).write(0);
474497
}
475498

476499
/// Sets the device status field to FAILED.
477500
/// A driver MUST NOT initialize and use the device any further after this.
478501
/// A driver MAY use the device again after a proper reset of the device.
479502
pub fn set_failed(&mut self) {
480503
memory_barrier();
481-
self.com_cfg.device_status = u8::from(device::Status::FAILED);
504+
let com_cfg = self.com_cfg.as_mut_ptr();
505+
map_field!(com_cfg.device_status).write(u8::from(device::Status::FAILED));
482506
}
483507

484508
/// Sets the ACKNOWLEDGE bit in the device status field. This indicates, the
485509
/// OS has notived the device
486510
pub fn ack_dev(&mut self) {
487511
memory_barrier();
488-
self.com_cfg.device_status |= u8::from(device::Status::ACKNOWLEDGE);
512+
let com_cfg = self.com_cfg.as_mut_ptr();
513+
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::ACKNOWLEDGE));
489514
}
490515

491516
/// Sets the DRIVER bit in the device status field. This indicates, the OS
492517
/// know how to run this device.
493518
pub fn set_drv(&mut self) {
494519
memory_barrier();
495-
self.com_cfg.device_status |= u8::from(device::Status::DRIVER);
520+
let com_cfg = self.com_cfg.as_mut_ptr();
521+
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::DRIVER));
496522
}
497523

498524
/// Sets the FEATURES_OK bit in the device status field.
499525
///
500526
/// Drivers MUST NOT accept new features after this step.
501527
pub fn features_ok(&mut self) {
502528
memory_barrier();
503-
self.com_cfg.device_status |= u8::from(device::Status::FEATURES_OK);
529+
let com_cfg = self.com_cfg.as_mut_ptr();
530+
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::FEATURES_OK));
504531
}
505532

506533
/// In order to correctly check feature negotiaten, this function
@@ -511,7 +538,8 @@ impl ComCfg {
511538
/// otherwise, the device does not support our subset of features and the device is unusable.
512539
pub fn check_features(&self) -> bool {
513540
memory_barrier();
514-
self.com_cfg.device_status & u8::from(device::Status::FEATURES_OK)
541+
let com_cfg = self.com_cfg.as_ptr();
542+
map_field!(com_cfg.device_status).read() & u8::from(device::Status::FEATURES_OK)
515543
== u8::from(device::Status::FEATURES_OK)
516544
}
517545

@@ -520,54 +548,59 @@ impl ComCfg {
520548
/// After this call, the device is "live"!
521549
pub fn drv_ok(&mut self) {
522550
memory_barrier();
523-
self.com_cfg.device_status |= u8::from(device::Status::DRIVER_OK);
551+
let com_cfg = self.com_cfg.as_mut_ptr();
552+
map_field!(com_cfg.device_status).update(|s| s | u8::from(device::Status::DRIVER_OK));
524553
}
525554

526555
/// Returns the features offered by the device. Coded in a 64bit value.
527556
pub fn dev_features(&mut self) -> u64 {
528-
let device_feature_select = ptr::from_mut(&mut self.com_cfg.device_feature_select);
529-
let device_feature = ptr::from_mut(&mut self.com_cfg.device_feature);
557+
let com_cfg = self.com_cfg.as_mut_ptr();
558+
let device_feature_select = map_field!(com_cfg.device_feature_select);
559+
let device_feature = map_field!(com_cfg.device_feature_select);
560+
530561
// Indicate device to show high 32 bits in device_feature field.
531562
// See Virtio specification v1.1. - 4.1.4.3
532563
memory_barrier();
533-
unsafe { device_feature_select.write_volatile(1) };
564+
device_feature_select.write(1);
534565
memory_barrier();
535566

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

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

544575
// read low 32 bits of device features
545-
dev_feat |= u64::from(unsafe { device_feature.read_volatile() });
576+
dev_feat |= u64::from(device_feature.read());
546577

547578
dev_feat
548579
}
549580

550581
/// Write selected features into driver_select field.
551582
pub fn set_drv_features(&mut self, feats: u64) {
583+
let com_cfg = self.com_cfg.as_mut_ptr();
584+
552585
let high: u32 = (feats >> 32) as u32;
553586
let low: u32 = feats as u32;
554587

555588
// Indicate to device that driver_features field shows low 32 bits.
556589
// See Virtio specification v1.1. - 4.1.4.3
557590
memory_barrier();
558-
self.com_cfg.driver_feature_select = 0;
591+
map_field!(com_cfg.driver_feature_select).write(0);
559592
memory_barrier();
560593

561594
// write low 32 bits of device features
562-
self.com_cfg.driver_feature = low;
595+
map_field!(com_cfg.driver_feature).write(low);
563596

564597
// Indicate to device that driver_features field shows high 32 bits.
565598
// See Virtio specification v1.1. - 4.1.4.3
566-
self.com_cfg.driver_feature_select = 1;
599+
map_field!(com_cfg.driver_feature_select).write(1);
567600
memory_barrier();
568601

569602
// write high 32 bits of device features
570-
self.com_cfg.driver_feature = high;
603+
map_field!(com_cfg.driver_feature).write(high);
571604
}
572605
}
573606

@@ -603,7 +636,7 @@ struct ComCfgRaw {
603636
impl ComCfgRaw {
604637
/// Returns a boxed [ComCfgRaw] structure. The box points to the actual structure inside the
605638
/// PCI devices memory space.
606-
fn map(cap: &PciCap) -> Option<&'static mut ComCfgRaw> {
639+
fn map(cap: &PciCap) -> Option<VolatileRef<'static, ComCfgRaw>> {
607640
if cap.bar.length < u64::from(cap.length + cap.offset) {
608641
error!("Common config of with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
609642
cap.id,
@@ -621,10 +654,13 @@ impl ComCfgRaw {
621654
}
622655

623656
let virt_addr_raw = cap.bar.mem_addr + cap.offset;
657+
let ptr = NonNull::new(ptr::from_exposed_addr_mut::<ComCfgRaw>(
658+
virt_addr_raw.into(),
659+
))
660+
.unwrap();
624661

625662
// Create mutable reference to the PCI structure in PCI memory
626-
let com_cfg_raw: &mut ComCfgRaw =
627-
unsafe { &mut *(ptr::from_exposed_addr_mut(virt_addr_raw.into())) };
663+
let com_cfg_raw = unsafe { VolatileRef::new(ptr) };
628664

629665
Some(com_cfg_raw)
630666
}

0 commit comments

Comments
 (0)