use std::cell::RefCell;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::sync::RwLock;
use vm_memory::{bitmap::Bitmap, Address, GuestMemoryRegion, GuestRegionMmap};
use vmm_sys_util::eventfd::EventFd;
#[cfg(feature = "vhost-user")]
use super::vhost_user::message::{VhostUserMemoryRegion, VhostUserSingleMemoryRegion};
use super::{Error, Result};
pub const VHOST_MAX_MEMORY_REGIONS: usize = 255;
#[derive(Default, Clone, Copy)]
pub struct VringConfigData {
pub queue_max_size: u16,
pub queue_size: u16,
pub flags: u32,
pub desc_table_addr: u64,
pub used_ring_addr: u64,
pub avail_ring_addr: u64,
pub log_addr: Option<u64>,
}
impl VringConfigData {
pub fn is_log_addr_valid(&self) -> bool {
if self.flags & 0x1 != 0 && self.log_addr.is_none() {
return false;
}
true
}
pub fn get_log_addr(&self) -> u64 {
if self.flags & 0x1 != 0 && self.log_addr.is_some() {
self.log_addr.unwrap()
} else {
0
}
}
}
#[derive(Default, Clone, Copy)]
pub struct VhostUserMemoryRegionInfo {
pub guest_phys_addr: u64,
pub memory_size: u64,
pub userspace_addr: u64,
pub mmap_offset: u64,
pub mmap_handle: RawFd,
#[cfg(feature = "xen")]
pub xen_mmap_flags: u32,
#[cfg(feature = "xen")]
pub xen_mmap_data: u32,
}
impl VhostUserMemoryRegionInfo {
pub fn from_guest_region<B: Bitmap>(region: &GuestRegionMmap<B>) -> Result<Self> {
let file_offset = region
.file_offset()
.ok_or(Error::InvalidGuestMemoryRegion)?;
Ok(Self {
guest_phys_addr: region.start_addr().raw_value(),
memory_size: region.len(),
userspace_addr: region.as_ptr() as u64,
mmap_offset: file_offset.start(),
mmap_handle: file_offset.file().as_raw_fd(),
#[cfg(feature = "xen")]
xen_mmap_flags: region.xen_mmap_flags(),
#[cfg(feature = "xen")]
xen_mmap_data: region.xen_mmap_data(),
})
}
#[cfg(feature = "vhost-user")]
pub fn to_region(&self) -> VhostUserMemoryRegion {
#[cfg(not(feature = "xen"))]
return VhostUserMemoryRegion::new(
self.guest_phys_addr,
self.memory_size,
self.userspace_addr,
self.mmap_offset,
);
#[cfg(feature = "xen")]
VhostUserMemoryRegion::with_xen(
self.guest_phys_addr,
self.memory_size,
self.userspace_addr,
self.mmap_offset,
self.xen_mmap_flags,
self.xen_mmap_data,
)
}
#[cfg(feature = "vhost-user")]
pub fn to_single_region(&self) -> VhostUserSingleMemoryRegion {
VhostUserSingleMemoryRegion::new(
self.guest_phys_addr,
self.memory_size,
self.userspace_addr,
self.mmap_offset,
#[cfg(feature = "xen")]
self.xen_mmap_flags,
#[cfg(feature = "xen")]
self.xen_mmap_data,
)
}
}
#[derive(Default, Clone, Copy)]
pub struct VhostUserDirtyLogRegion {
pub mmap_size: u64,
pub mmap_offset: u64,
pub mmap_handle: RawFd,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum VhostAccess {
#[default]
No = 0,
ReadOnly = 1,
WriteOnly = 2,
ReadWrite = 3,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum VhostIotlbType {
#[default]
Empty = 0,
Miss = 1,
Update = 2,
Invalidate = 3,
AccessFail = 4,
BatchBegin = 5,
BatchEnd = 6,
}
#[derive(Default, Clone, Copy)]
pub struct VhostIotlbMsg {
pub iova: u64,
pub size: u64,
pub userspace_addr: u64,
pub perm: VhostAccess,
pub msg_type: VhostIotlbType,
}
pub trait VhostIotlbMsgParser {
fn parse(&self, msg: &mut VhostIotlbMsg) -> Result<()>;
}
pub trait VhostIotlbBackend: std::marker::Sized {
fn send_iotlb_msg(&self, msg: &VhostIotlbMsg) -> Result<()>;
}
pub trait VhostBackend: std::marker::Sized {
fn get_features(&self) -> Result<u64>;
fn set_features(&self, features: u64) -> Result<()>;
fn set_owner(&self) -> Result<()>;
fn reset_owner(&self) -> Result<()>;
fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>;
fn set_log_base(&self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()>;
fn set_log_fd(&self, fd: RawFd) -> Result<()>;
fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()>;
fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()>;
fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()>;
fn get_vring_base(&self, queue_index: usize) -> Result<u32>;
fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()>;
fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()>;
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()>;
}
pub trait VhostBackendMut: std::marker::Sized {
fn get_features(&mut self) -> Result<u64>;
fn set_features(&mut self, features: u64) -> Result<()>;
fn set_owner(&mut self) -> Result<()>;
fn reset_owner(&mut self) -> Result<()>;
fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>;
fn set_log_base(&mut self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()>;
fn set_log_fd(&mut self, fd: RawFd) -> Result<()>;
fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>;
fn set_vring_addr(&mut self, queue_index: usize, config_data: &VringConfigData) -> Result<()>;
fn set_vring_base(&mut self, queue_index: usize, base: u16) -> Result<()>;
fn get_vring_base(&mut self, queue_index: usize) -> Result<u32>;
fn set_vring_call(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;
fn set_vring_kick(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;
fn set_vring_err(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>;
}
impl<T: VhostBackendMut> VhostBackend for RwLock<T> {
fn get_features(&self) -> Result<u64> {
self.write().unwrap().get_features()
}
fn set_features(&self, features: u64) -> Result<()> {
self.write().unwrap().set_features(features)
}
fn set_owner(&self) -> Result<()> {
self.write().unwrap().set_owner()
}
fn reset_owner(&self) -> Result<()> {
self.write().unwrap().reset_owner()
}
fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
self.write().unwrap().set_mem_table(regions)
}
fn set_log_base(&self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()> {
self.write().unwrap().set_log_base(base, region)
}
fn set_log_fd(&self, fd: RawFd) -> Result<()> {
self.write().unwrap().set_log_fd(fd)
}
fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
self.write().unwrap().set_vring_num(queue_index, num)
}
fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
self.write()
.unwrap()
.set_vring_addr(queue_index, config_data)
}
fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
self.write().unwrap().set_vring_base(queue_index, base)
}
fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
self.write().unwrap().get_vring_base(queue_index)
}
fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.write().unwrap().set_vring_call(queue_index, fd)
}
fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.write().unwrap().set_vring_kick(queue_index, fd)
}
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.write().unwrap().set_vring_err(queue_index, fd)
}
}
impl<T: VhostBackendMut> VhostBackend for RefCell<T> {
fn get_features(&self) -> Result<u64> {
self.borrow_mut().get_features()
}
fn set_features(&self, features: u64) -> Result<()> {
self.borrow_mut().set_features(features)
}
fn set_owner(&self) -> Result<()> {
self.borrow_mut().set_owner()
}
fn reset_owner(&self) -> Result<()> {
self.borrow_mut().reset_owner()
}
fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
self.borrow_mut().set_mem_table(regions)
}
fn set_log_base(&self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()> {
self.borrow_mut().set_log_base(base, region)
}
fn set_log_fd(&self, fd: RawFd) -> Result<()> {
self.borrow_mut().set_log_fd(fd)
}
fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
self.borrow_mut().set_vring_num(queue_index, num)
}
fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
self.borrow_mut().set_vring_addr(queue_index, config_data)
}
fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
self.borrow_mut().set_vring_base(queue_index, base)
}
fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
self.borrow_mut().get_vring_base(queue_index)
}
fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.borrow_mut().set_vring_call(queue_index, fd)
}
fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.borrow_mut().set_vring_kick(queue_index, fd)
}
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
self.borrow_mut().set_vring_err(queue_index, fd)
}
}
#[cfg(any(test, feature = "test-utils"))]
impl VhostUserMemoryRegionInfo {
pub fn new(
guest_phys_addr: u64,
memory_size: u64,
userspace_addr: u64,
mmap_offset: u64,
mmap_handle: RawFd,
) -> Self {
Self {
guest_phys_addr,
memory_size,
userspace_addr,
mmap_offset,
mmap_handle,
#[cfg(feature = "xen")]
xen_mmap_flags: vm_memory::MmapXenFlags::UNIX.bits(),
#[cfg(feature = "xen")]
xen_mmap_data: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockBackend {}
impl VhostBackendMut for MockBackend {
fn get_features(&mut self) -> Result<u64> {
Ok(0x1)
}
fn set_features(&mut self, features: u64) -> Result<()> {
assert_eq!(features, 0x1);
Ok(())
}
fn set_owner(&mut self) -> Result<()> {
Ok(())
}
fn reset_owner(&mut self) -> Result<()> {
Ok(())
}
fn set_mem_table(&mut self, _regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
Ok(())
}
fn set_log_base(
&mut self,
base: u64,
region: Option<VhostUserDirtyLogRegion>,
) -> Result<()> {
assert_eq!(base, 0x100);
let region = region.unwrap();
assert_eq!(region.mmap_size, 0x1000);
assert_eq!(region.mmap_offset, 0x10);
assert_eq!(region.mmap_handle, 100);
Ok(())
}
fn set_log_fd(&mut self, fd: RawFd) -> Result<()> {
assert_eq!(fd, 100);
Ok(())
}
fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()> {
assert_eq!(queue_index, 1);
assert_eq!(num, 256);
Ok(())
}
fn set_vring_addr(
&mut self,
queue_index: usize,
_config_data: &VringConfigData,
) -> Result<()> {
assert_eq!(queue_index, 1);
Ok(())
}
fn set_vring_base(&mut self, queue_index: usize, base: u16) -> Result<()> {
assert_eq!(queue_index, 1);
assert_eq!(base, 2);
Ok(())
}
fn get_vring_base(&mut self, queue_index: usize) -> Result<u32> {
assert_eq!(queue_index, 1);
Ok(2)
}
fn set_vring_call(&mut self, queue_index: usize, _fd: &EventFd) -> Result<()> {
assert_eq!(queue_index, 1);
Ok(())
}
fn set_vring_kick(&mut self, queue_index: usize, _fd: &EventFd) -> Result<()> {
assert_eq!(queue_index, 1);
Ok(())
}
fn set_vring_err(&mut self, queue_index: usize, _fd: &EventFd) -> Result<()> {
assert_eq!(queue_index, 1);
Ok(())
}
}
#[test]
fn test_vring_backend_mut() {
let b = RwLock::new(MockBackend {});
assert_eq!(b.get_features().unwrap(), 0x1);
b.set_features(0x1).unwrap();
b.set_owner().unwrap();
b.reset_owner().unwrap();
b.set_mem_table(&[]).unwrap();
b.set_log_base(
0x100,
Some(VhostUserDirtyLogRegion {
mmap_size: 0x1000,
mmap_offset: 0x10,
mmap_handle: 100,
}),
)
.unwrap();
b.set_log_fd(100).unwrap();
b.set_vring_num(1, 256).unwrap();
let config = VringConfigData {
queue_max_size: 0x1000,
queue_size: 0x2000,
flags: 0x0,
desc_table_addr: 0x4000,
used_ring_addr: 0x5000,
avail_ring_addr: 0x6000,
log_addr: None,
};
b.set_vring_addr(1, &config).unwrap();
b.set_vring_base(1, 2).unwrap();
assert_eq!(b.get_vring_base(1).unwrap(), 2);
let eventfd = EventFd::new(0).unwrap();
b.set_vring_call(1, &eventfd).unwrap();
b.set_vring_kick(1, &eventfd).unwrap();
b.set_vring_err(1, &eventfd).unwrap();
}
#[test]
fn test_vring_config_data() {
let mut config = VringConfigData {
queue_max_size: 0x1000,
queue_size: 0x2000,
flags: 0x0,
desc_table_addr: 0x4000,
used_ring_addr: 0x5000,
avail_ring_addr: 0x6000,
log_addr: None,
};
assert!(config.is_log_addr_valid());
assert_eq!(config.get_log_addr(), 0);
config.flags = 0x1;
assert!(!config.is_log_addr_valid());
assert_eq!(config.get_log_addr(), 0);
config.log_addr = Some(0x7000);
assert!(config.is_log_addr_valid());
assert_eq!(config.get_log_addr(), 0x7000);
config.flags = 0x0;
assert!(config.is_log_addr_valid());
assert_eq!(config.get_log_addr(), 0);
}
}