use std::mem;
use std::os::unix::io::{AsRawFd, RawFd};
use libc::{c_void, ssize_t, write};
use vm_memory::{Address, GuestAddress, GuestAddressSpace, GuestMemoryBackend, GuestUsize};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
use super::{
Error, Result, VhostAccess, VhostBackend, VhostIotlbBackend, VhostIotlbMsg,
VhostIotlbMsgParser, VhostIotlbType, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo,
VringConfigData, VHOST_MAX_MEMORY_REGIONS,
};
pub mod vhost_binding;
use self::vhost_binding::*;
#[cfg(feature = "vhost-net")]
pub mod net;
#[cfg(feature = "vhost-vdpa")]
pub mod vdpa;
#[cfg(feature = "vhost-vsock")]
pub mod vsock;
pub trait PhysicalGuestAddressSpace: GuestAddressSpace<M: GuestMemoryBackend> {}
impl<AS: GuestAddressSpace> PhysicalGuestAddressSpace for AS where AS::M: GuestMemoryBackend {}
#[inline]
fn ioctl_result<T>(rc: i32, res: T) -> Result<T> {
if rc < 0 {
Err(Error::IoctlError(std::io::Error::last_os_error()))
} else {
Ok(res)
}
}
#[inline]
fn io_result<T>(rc: isize, res: T) -> Result<T> {
if rc < 0 {
Err(Error::IOError(std::io::Error::last_os_error()))
} else {
Ok(res)
}
}
pub trait VhostKernBackend: AsRawFd {
type AS: PhysicalGuestAddressSpace;
fn mem(&self) -> &Self::AS;
fn is_valid(&self, config_data: &VringConfigData) -> bool {
let queue_size = config_data.queue_size;
if queue_size > config_data.queue_max_size
|| queue_size == 0
|| (queue_size & (queue_size - 1)) != 0
{
return false;
}
let m = self.mem().memory();
let desc_table_size = 16 * u64::from(queue_size) as GuestUsize;
let avail_ring_size = 6 + 2 * u64::from(queue_size) as GuestUsize;
let used_ring_size = 6 + 8 * u64::from(queue_size) as GuestUsize;
if GuestAddress(config_data.desc_table_addr)
.checked_add(desc_table_size)
.is_none_or(|v| !m.address_in_range(v))
{
return false;
}
if GuestAddress(config_data.avail_ring_addr)
.checked_add(avail_ring_size)
.is_none_or(|v| !m.address_in_range(v))
{
return false;
}
if GuestAddress(config_data.used_ring_addr)
.checked_add(used_ring_size)
.is_none_or(|v| !m.address_in_range(v))
{
return false;
}
config_data.is_log_addr_valid()
}
}
impl<T: VhostKernBackend> VhostBackend for T {
fn get_features(&self) -> Result<u64> {
let mut avail_features: u64 = 0;
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_GET_FEATURES(), &mut avail_features) };
ioctl_result(ret, avail_features)
}
fn set_features(&self, features: u64) -> Result<()> {
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_FEATURES(), &features) };
ioctl_result(ret, ())
}
fn set_owner(&self) -> Result<()> {
let ret = unsafe { ioctl(self, VHOST_SET_OWNER()) };
ioctl_result(ret, ())
}
fn reset_owner(&self) -> Result<()> {
let ret = unsafe { ioctl(self, VHOST_RESET_OWNER()) };
ioctl_result(ret, ())
}
fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> {
if regions.is_empty() || regions.len() > VHOST_MAX_MEMORY_REGIONS {
return Err(Error::InvalidGuestMemory);
}
let mut vhost_memory = VhostMemory::new(regions.len() as u16);
for (index, region) in regions.iter().enumerate() {
vhost_memory.set_region(
index as u32,
&vhost_memory_region {
guest_phys_addr: region.guest_phys_addr,
memory_size: region.memory_size,
userspace_addr: region.userspace_addr,
flags_padding: 0u64,
},
)?;
}
let ret = unsafe { ioctl_with_ptr(self, VHOST_SET_MEM_TABLE(), vhost_memory.as_ptr()) };
ioctl_result(ret, ())
}
fn set_log_base(&self, base: u64, region: Option<VhostUserDirtyLogRegion>) -> Result<()> {
if region.is_some() {
return Err(Error::LogAddress);
}
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_LOG_BASE(), &base) };
ioctl_result(ret, ())
}
fn set_log_fd(&self, fd: RawFd) -> Result<()> {
let val: i32 = fd;
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_LOG_FD(), &val) };
ioctl_result(ret, ())
}
fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
let vring_state = vhost_vring_state {
index: queue_index as u32,
num: u32::from(num),
};
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_NUM(), &vring_state) };
ioctl_result(ret, ())
}
fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> {
if !self.is_valid(config_data) {
return Err(Error::InvalidQueue);
}
let vring_addr = config_data.to_vhost_vring_addr(queue_index, self.mem())?;
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) };
ioctl_result(ret, ())
}
fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> {
let vring_state = vhost_vring_state {
index: queue_index as u32,
num: u32::from(base),
};
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_BASE(), &vring_state) };
ioctl_result(ret, ())
}
fn get_vring_base(&self, queue_index: usize) -> Result<u32> {
let mut vring_state = vhost_vring_state {
index: queue_index as u32,
num: 0,
};
let ret = unsafe { ioctl_with_mut_ref(self, VHOST_GET_VRING_BASE(), &mut vring_state) };
ioctl_result(ret, vring_state.num)
}
fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
let vring_file = vhost_vring_file {
index: queue_index as u32,
fd: fd.as_raw_fd(),
};
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_CALL(), &vring_file) };
ioctl_result(ret, ())
}
fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
let vring_file = vhost_vring_file {
index: queue_index as u32,
fd: fd.as_raw_fd(),
};
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_KICK(), &vring_file) };
ioctl_result(ret, ())
}
fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> {
let vring_file = vhost_vring_file {
index: queue_index as u32,
fd: fd.as_raw_fd(),
};
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ERR(), &vring_file) };
ioctl_result(ret, ())
}
}
pub trait VhostKernFeatures: Sized + AsRawFd {
fn get_backend_features_acked(&self) -> u64;
fn set_backend_features_acked(&mut self, features: u64);
fn get_backend_features(&self) -> Result<u64> {
let mut avail_features: u64 = 0;
let ret =
unsafe { ioctl_with_mut_ref(self, VHOST_GET_BACKEND_FEATURES(), &mut avail_features) };
ioctl_result(ret, avail_features)
}
fn set_backend_features(&mut self, features: u64) -> Result<()> {
let ret = unsafe { ioctl_with_ref(self, VHOST_SET_BACKEND_FEATURES(), &features) };
if ret >= 0 {
self.set_backend_features_acked(features);
}
ioctl_result(ret, ())
}
}
impl<I: VhostKernBackend + VhostKernFeatures> VhostIotlbBackend for I {
fn send_iotlb_msg(&self, msg: &VhostIotlbMsg) -> Result<()> {
let ret: ssize_t;
if self.get_backend_features_acked() & (1 << VHOST_BACKEND_F_IOTLB_MSG_V2) != 0 {
let mut msg_v2 = vhost_msg_v2 {
type_: VHOST_IOTLB_MSG_V2,
..Default::default()
};
msg_v2.__bindgen_anon_1.iotlb.iova = msg.iova;
msg_v2.__bindgen_anon_1.iotlb.size = msg.size;
msg_v2.__bindgen_anon_1.iotlb.uaddr = msg.userspace_addr;
msg_v2.__bindgen_anon_1.iotlb.perm = msg.perm as u8;
msg_v2.__bindgen_anon_1.iotlb.type_ = msg.msg_type as u8;
ret = unsafe {
write(
self.as_raw_fd(),
&msg_v2 as *const vhost_msg_v2 as *const c_void,
mem::size_of::<vhost_msg_v2>(),
)
};
} else {
let mut msg_v1 = vhost_msg {
type_: VHOST_IOTLB_MSG,
..Default::default()
};
msg_v1.__bindgen_anon_1.iotlb.iova = msg.iova;
msg_v1.__bindgen_anon_1.iotlb.size = msg.size;
msg_v1.__bindgen_anon_1.iotlb.uaddr = msg.userspace_addr;
msg_v1.__bindgen_anon_1.iotlb.perm = msg.perm as u8;
msg_v1.__bindgen_anon_1.iotlb.type_ = msg.msg_type as u8;
ret = unsafe {
write(
self.as_raw_fd(),
&msg_v1 as *const vhost_msg as *const c_void,
mem::size_of::<vhost_msg>(),
)
};
}
io_result(ret, ())
}
}
impl VhostIotlbMsgParser for vhost_msg {
fn parse(&self, msg: &mut VhostIotlbMsg) -> Result<()> {
if self.type_ != VHOST_IOTLB_MSG {
return Err(Error::InvalidIotlbMsg);
}
unsafe {
if self.__bindgen_anon_1.iotlb.type_ == 0 {
return Err(Error::InvalidIotlbMsg);
}
msg.iova = self.__bindgen_anon_1.iotlb.iova;
msg.size = self.__bindgen_anon_1.iotlb.size;
msg.userspace_addr = self.__bindgen_anon_1.iotlb.uaddr;
msg.perm = mem::transmute::<u8, VhostAccess>(self.__bindgen_anon_1.iotlb.perm);
msg.msg_type = mem::transmute::<u8, VhostIotlbType>(self.__bindgen_anon_1.iotlb.type_);
}
Ok(())
}
}
impl VhostIotlbMsgParser for vhost_msg_v2 {
fn parse(&self, msg: &mut VhostIotlbMsg) -> Result<()> {
if self.type_ != VHOST_IOTLB_MSG_V2 {
return Err(Error::InvalidIotlbMsg);
}
unsafe {
if self.__bindgen_anon_1.iotlb.type_ == 0 {
return Err(Error::InvalidIotlbMsg);
}
msg.iova = self.__bindgen_anon_1.iotlb.iova;
msg.size = self.__bindgen_anon_1.iotlb.size;
msg.userspace_addr = self.__bindgen_anon_1.iotlb.uaddr;
msg.perm = mem::transmute::<u8, VhostAccess>(self.__bindgen_anon_1.iotlb.perm);
msg.msg_type = mem::transmute::<u8, VhostIotlbType>(self.__bindgen_anon_1.iotlb.type_);
}
Ok(())
}
}
impl VringConfigData {
pub fn to_vhost_vring_addr<AS: PhysicalGuestAddressSpace>(
&self,
queue_index: usize,
mem: &AS,
) -> Result<vhost_vring_addr> {
let desc_addr = mem
.memory()
.get_host_address(GuestAddress(self.desc_table_addr))
.map_err(|_| Error::DescriptorTableAddress)?;
let avail_addr = mem
.memory()
.get_host_address(GuestAddress(self.avail_ring_addr))
.map_err(|_| Error::AvailAddress)?;
let used_addr = mem
.memory()
.get_host_address(GuestAddress(self.used_ring_addr))
.map_err(|_| Error::UsedAddress)?;
Ok(vhost_vring_addr {
index: queue_index as u32,
flags: self.flags,
desc_user_addr: desc_addr as u64,
used_user_addr: used_addr as u64,
avail_user_addr: avail_addr as u64,
log_guest_addr: self.get_log_addr(),
})
}
}