Skip to content
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
7 changes: 5 additions & 2 deletions alioth/src/hv/hv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,12 @@ pub trait MsiSender: Debug + Send + Sync + 'static {
fn create_irqfd(&self) -> Result<Self::IrqFd>;
}

pub trait NotifierRegistry: Debug + Send + Sync + 'static {
pub trait NotifierRegistry: Debug + Send + Sync + Clone + 'static {
fn register(&self, notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> Result<()>;
fn deregister(&self, notifier: &Notifier) -> Result<()>;
/// Undoes a [`NotifierRegistry::register()`] call. The parameters must
/// match the ones the notifier was registered with, since the same
/// notifier can be registered at multiple addresses.
fn deregister(&self, notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> Result<()>;
}

pub trait IrqFd: Debug + Send + Sync + AsFd + 'static {
Expand Down
10 changes: 6 additions & 4 deletions alioth/src/hv/hv_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,10 @@ pub struct RegisteredAddr {
pub data: Option<u64>,
}

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct TestNotifierRegistry {
pub registered: Arc<Mutex<Vec<RegisteredAddr>>>,
pub deregistered: Arc<Mutex<usize>>,
pub deregistered: Arc<Mutex<Vec<RegisteredAddr>>>,
}

impl super::NotifierRegistry for TestNotifierRegistry {
Expand All @@ -163,8 +163,10 @@ impl super::NotifierRegistry for TestNotifierRegistry {
Ok(())
}

fn deregister(&self, _notifier: &Notifier) -> Result<()> {
*self.deregistered.lock() += 1;
fn deregister(&self, _notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> Result<()> {
self.deregistered
.lock()
.push(RegisteredAddr { gpa, len, data });
Ok(())
}
}
10 changes: 8 additions & 2 deletions alioth/src/hv/hvf/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,17 @@ impl MsiSender for HvfMsiSender {
/// Hypervisor.framework has no `KVM_IOEVENTFD` equivalent, so this registry is
/// deliberately uninhabited: [`HvfVm::create_notifier_registry()`] always fails
/// and no value of this type can ever be constructed.
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub enum HvfNotifierRegistry {}

impl NotifierRegistry for HvfNotifierRegistry {
fn deregister(&self, _notifier: &Notifier) -> Result<()> {
fn deregister(
&self,
_notifier: &Notifier,
_gpa: u64,
_len: u8,
_data: Option<u64>,
) -> Result<()> {
match *self {}
}

Expand Down
27 changes: 14 additions & 13 deletions alioth/src/hv/kvm/vm/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ pub struct VmInner {
pub fd: OwnedFd,
pub vcpu_mmap_size: usize,
memfd: Option<OwnedFd>,
ioeventfds: Mutex<HashMap<i32, KvmIoEventFd>>,
msi_table: RwLock<HashMap<u32, KvmMsiEntryData>>,
next_msi_gsi: AtomicU32,
pin_map: AtomicU32,
Expand Down Expand Up @@ -406,13 +405,13 @@ impl MsiSender for KvmMsiSender {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct KvmIoeventFdRegistry {
vm: Arc<VmInner>,
}

impl NotifierRegistry for KvmIoeventFdRegistry {
fn register(&self, notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> Result<()> {
impl KvmIoeventFdRegistry {
fn request(notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> KvmIoEventFd {
let mut request = KvmIoEventFd {
addr: gpa,
len: len as u32,
Expand All @@ -423,18 +422,21 @@ impl NotifierRegistry for KvmIoeventFdRegistry {
request.datamatch = data;
request.flags |= KvmIoEventFdFlag::DATA_MATCH;
}
request
}
}

impl NotifierRegistry for KvmIoeventFdRegistry {
fn register(&self, notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> Result<()> {
let request = Self::request(notifier, gpa, len, data);
unsafe { kvm_ioeventfd(&self.vm.fd, &request) }.context(error::Notifier)?;
let mut fds = self.vm.ioeventfds.lock();
fds.insert(request.fd, request);
Ok(())
}

fn deregister(&self, notifier: &Notifier) -> Result<()> {
let mut fds = self.vm.ioeventfds.lock();
if let Some(mut request) = fds.remove(&notifier.as_fd().as_raw_fd()) {
request.flags |= KvmIoEventFdFlag::DEASSIGN;
unsafe { kvm_ioeventfd(&self.vm.fd, &request) }.context(error::Notifier)?;
}
fn deregister(&self, notifier: &Notifier, gpa: u64, len: u8, data: Option<u64>) -> Result<()> {
let mut request = Self::request(notifier, gpa, len, data);
request.flags |= KvmIoEventFdFlag::DEASSIGN;
unsafe { kvm_ioeventfd(&self.vm.fd, &request) }.context(error::Notifier)?;
Ok(())
}
}
Expand Down Expand Up @@ -465,7 +467,6 @@ impl KvmVm {
fd,
vcpu_mmap_size,
memfd,
ioeventfds: Mutex::new(HashMap::new()),
msi_table: RwLock::new(HashMap::new()),
next_msi_gsi: AtomicU32::new(0),
pin_map: AtomicU32::new(0),
Expand Down
8 changes: 4 additions & 4 deletions alioth/src/mem/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ pub struct MemRegionEntry {

pub trait MemRegionCallback: Debug + Send + Sync + Any + 'static {
fn mapped(&self, addr: u64) -> Result<()>;
fn unmapped(&self) -> Result<()> {
log::debug!("{} unmapped", type_name::<Self>());
fn unmapped(&self, addr: u64) -> Result<()> {
log::debug!("{} unmapped from {addr:#x}", type_name::<Self>());
Ok(())
}
}
Expand Down Expand Up @@ -410,7 +410,7 @@ impl Memory {
}
let region_callbacks = region.callbacks.lock();
for callback in region_callbacks.iter() {
callback.unmapped()?;
callback.unmapped(addr)?;
}
Ok(())
}
Expand Down Expand Up @@ -496,7 +496,7 @@ impl Memory {
}
let callbacks = region.callbacks.lock();
for callback in callbacks.iter() {
callback.unmapped()?;
callback.unmapped(port as u64)?;
}
Ok(())
}
Expand Down
28 changes: 28 additions & 0 deletions alioth/src/vfio/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ use std::fs::File;
use std::mem::size_of;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
use std::os::unix::fs::FileExt;
use std::sync::Arc;

use crate::errors::BoxTrace;
use crate::mem;
use crate::sync::notifier::Notifier;
use crate::sys::vfio::{
DeviceFeature, VfioDeviceFeature, VfioDeviceFeatureDmaBuf, VfioDeviceFeatureFlag,
VfioDeviceInfo, VfioIrqInfo, VfioIrqSet, VfioIrqSetData, VfioIrqSetFlag, VfioRegionDmaRange,
Expand All @@ -28,6 +30,22 @@ use crate::sys::vfio::{
};
use crate::vfio::Result;

/// A sub-region of a device region whose accesses the hypervisor can signal
/// with a notifier, instead of exiting to the VMM and forwarding them to the
/// device.
#[derive(Debug)]
pub struct RegionNotifier {
/// Offset of the sub-region within the region.
pub offset: u64,
/// Access size the notifier covers, 0 if any size matches.
pub size: u8,
/// Value the access must carry to signal the notifier, if any.
pub datamatch: Option<u64>,
/// The notifier, shared since a device may back several sub-regions with
/// the same file descriptor.
pub notifier: Arc<Notifier>,
}

pub trait Device: Debug + Send + Sync + 'static {
fn get_info(&self) -> Result<VfioDeviceInfo>;
fn get_region_info(&self, index: u32) -> Result<VfioRegionInfo>;
Expand All @@ -50,6 +68,16 @@ pub trait Device: Debug + Send + Sync + 'static {

fn get_dma_buf_fd(&self, index: u32, offset: u64, size: usize) -> Result<OwnedFd>;

/// Returns the sub-regions of region `index` that the VMM should watch
/// with a notifier rather than emulate.
///
/// Kernel VFIO devices have nothing to offer here: `VFIO_DEVICE_IOEVENTFD`
/// goes the other way round, it hands an eventfd to the kernel so that it
/// writes a fixed value to the device when the VMM signals it.
fn get_region_notifiers(&self, _index: u32) -> Result<Vec<RegionNotifier>> {
Ok(vec![])
}

// Helper methods for single-value read/write
fn read(&self, region: &VfioRegionInfo, offset: u64, size: u8) -> mem::Result<u64> {
let mut bytes = [0u8; 8];
Expand Down
38 changes: 37 additions & 1 deletion alioth/src/vfio/device_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,21 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use assert_matches::assert_matches;
use parking_lot::{Mutex, RwLock};

use crate::sync::notifier::Notifier;
use crate::sys::vfio::{
VfioDeviceInfo, VfioDeviceInfoFlag, VfioIrqInfo, VfioPciRegion, VfioRegionInfo,
VfioRegionInfoFlag,
};
use crate::vfio::Result;
use crate::vfio::device::Device;
use crate::vfio::device::{Device, RegionNotifier};

pub type RegionMap = RwLock<HashMap<u32, (VfioRegionInfo, Vec<u8>)>>;
pub type IrqMap = RwLock<HashMap<u32, VfioIrqInfo>>;
pub type MmapFdMap = RwLock<HashMap<u32, OwnedFd>>;
pub type IrqEventFdList = Mutex<Vec<(u32, u32, Vec<Option<RawFd>>)>>;
/// Sub-regions [`MockVfioDevice::get_region_notifiers()`] reports, as
/// `(offset, size, datamatch)`.
pub type NotifierMap = RwLock<HashMap<u32, Vec<(u64, u8, Option<u64>)>>>;

#[derive(Debug)]
pub struct MockVfioDevice {
Expand All @@ -42,6 +46,7 @@ pub struct MockVfioDevice {
pub resets: AtomicUsize,
pub irq_eventfds: IrqEventFdList,
pub disabled_irqs: Mutex<Vec<u32>>,
pub notifiers: NotifierMap,
}

impl Default for MockVfioDevice {
Expand All @@ -64,6 +69,7 @@ impl MockVfioDevice {
resets: AtomicUsize::new(0),
irq_eventfds: Mutex::new(Vec::new()),
disabled_irqs: Mutex::new(Vec::new()),
notifiers: RwLock::new(HashMap::new()),
};
for index in 0..=5 {
dev.add_bar(index, 0, VfioRegionInfoFlag::empty(), 0);
Expand Down Expand Up @@ -101,6 +107,14 @@ impl MockVfioDevice {
self.add_region(index, size, flags, offset, vec![0u8; size as usize])
}

pub fn add_notifier(&self, index: u32, offset: u64, size: u8, datamatch: Option<u64>) {
let mut notifiers = self.notifiers.write();
notifiers
.entry(index)
.or_default()
.push((offset, size, datamatch));
}

pub fn add_config(&self, config: Vec<u8>) -> VfioRegionInfo {
self.add_region(
VfioPciRegion::CONFIG.raw(),
Expand Down Expand Up @@ -198,6 +212,24 @@ impl Device for MockVfioDevice {
fn get_dma_buf_fd(&self, _index: u32, _offset: u64, _size: usize) -> Result<OwnedFd> {
Err(std::io::Error::from(std::io::ErrorKind::Unsupported).into())
}

fn get_region_notifiers(&self, index: u32) -> Result<Vec<RegionNotifier>> {
let notifiers = self.notifiers.read();
let Some(specs) = notifiers.get(&index) else {
return Ok(vec![]);
};
specs
.iter()
.map(|&(offset, size, datamatch)| {
Ok(RegionNotifier {
offset,
size,
datamatch,
notifier: Arc::new(Notifier::new()?),
})
})
.collect()
}
}

impl Device for Arc<MockVfioDevice> {
Expand Down Expand Up @@ -245,6 +277,10 @@ impl Device for Arc<MockVfioDevice> {
fn get_dma_buf_fd(&self, index: u32, offset: u64, size: usize) -> Result<OwnedFd> {
(**self).get_dma_buf_fd(index, offset, size)
}

fn get_region_notifiers(&self, index: u32) -> Result<Vec<RegionNotifier>> {
(**self).get_region_notifiers(index)
}
}

#[test]
Expand Down
Loading
Loading