#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(clippy::upper_case_acronyms)]
use std::fmt::Debug;
use std::fs::File;
use std::io;
use std::marker::PhantomData;
use std::ops::Deref;
use uuid::Uuid;
use vm_memory::{mmap::NewBitmap, ByteValued, FileOffset, MmapRegion};
#[cfg(feature = "xen")]
use vm_memory::{GuestAddress, MmapRange, MmapXenFlags};
use super::{enum_value, Error, Result};
use crate::VringConfigData;
pub const MAX_MSG_SIZE: usize = 0x1000;
pub const MAX_ATTACHED_FD_ENTRIES: usize = 32;
pub const VHOST_USER_CONFIG_OFFSET: u32 = 0x100;
pub const VHOST_USER_CONFIG_SIZE: u32 = 0x1000;
pub const VHOST_USER_MAX_VRINGS: u64 = 0x8000u64;
pub(super) trait Req:
Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Send + Sync + Into<u32> + TryFrom<u32>
{
}
pub(super) trait MsgHeader: ByteValued + Copy + Default + VhostUserMsgValidator {
type Request: Req;
const MAX_MSG_SIZE: usize;
}
enum_value! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FrontendReq: u32 {
GET_FEATURES = 1,
SET_FEATURES = 2,
SET_OWNER = 3,
RESET_OWNER = 4,
SET_MEM_TABLE = 5,
SET_LOG_BASE = 6,
SET_LOG_FD = 7,
SET_VRING_NUM = 8,
SET_VRING_ADDR = 9,
SET_VRING_BASE = 10,
GET_VRING_BASE = 11,
SET_VRING_KICK = 12,
SET_VRING_CALL = 13,
SET_VRING_ERR = 14,
GET_PROTOCOL_FEATURES = 15,
SET_PROTOCOL_FEATURES = 16,
GET_QUEUE_NUM = 17,
SET_VRING_ENABLE = 18,
SEND_RARP = 19,
NET_SET_MTU = 20,
SET_BACKEND_REQ_FD = 21,
IOTLB_MSG = 22,
SET_VRING_ENDIAN = 23,
GET_CONFIG = 24,
SET_CONFIG = 25,
CREATE_CRYPTO_SESSION = 26,
CLOSE_CRYPTO_SESSION = 27,
POSTCOPY_ADVISE = 28,
POSTCOPY_LISTEN = 29,
POSTCOPY_END = 30,
GET_INFLIGHT_FD = 31,
SET_INFLIGHT_FD = 32,
GPU_SET_SOCKET = 33,
RESET_DEVICE = 34,
VRING_KICK = 35,
GET_MAX_MEM_SLOTS = 36,
ADD_MEM_REG = 37,
REM_MEM_REG = 38,
SET_STATUS = 39,
GET_STATUS = 40,
GET_SHARED_OBJECT = 41,
SET_DEVICE_STATE_FD = 42,
CHECK_DEVICE_STATE = 43,
GET_SHMEM_CONFIG = 44,
}
}
impl Req for FrontendReq {}
enum_value! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum BackendReq: u32 {
IOTLB_MSG = 1,
CONFIG_CHANGE_MSG = 2,
VRING_HOST_NOTIFIER_MSG = 3,
VRING_CALL = 4,
VRING_ERR = 5,
SHARED_OBJECT_ADD = 6,
SHARED_OBJECT_REMOVE = 7,
SHARED_OBJECT_LOOKUP = 8,
SHMEM_MAP = 9,
SHMEM_UNMAP = 10,
}
}
impl Req for BackendReq {}
pub trait VhostUserMsgValidator: ByteValued {
fn is_valid(&self) -> bool {
true
}
}
bitflags! {
pub struct VhostUserHeaderFlag: u32 {
const VERSION = 0x3;
const REPLY = 0x4;
const NEED_REPLY = 0x8;
const ALL_FLAGS = 0xc;
const RESERVED_BITS = !0xf;
}
}
#[repr(C, packed)]
#[derive(Copy)]
pub(super) struct VhostUserMsgHeader<R: Req> {
request: u32,
flags: u32,
size: u32,
_r: PhantomData<R>,
}
impl<R: Req> MsgHeader for VhostUserMsgHeader<R> {
type Request = R;
const MAX_MSG_SIZE: usize = MAX_MSG_SIZE;
}
impl<R: Req> Debug for VhostUserMsgHeader<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VhostUserMsgHeader")
.field("request", &{ self.request })
.field("flags", &{ self.flags })
.field("size", &{ self.size })
.finish()
}
}
impl<R: Req> Clone for VhostUserMsgHeader<R> {
fn clone(&self) -> VhostUserMsgHeader<R> {
*self
}
}
impl<R: Req> PartialEq for VhostUserMsgHeader<R> {
fn eq(&self, other: &Self) -> bool {
self.request == other.request && self.flags == other.flags && self.size == other.size
}
}
impl<R: Req> VhostUserMsgHeader<R> {
pub fn new(request: R, flags: u32, size: u32) -> Self {
let fl = (flags & VhostUserHeaderFlag::ALL_FLAGS.bits()) | 0x1;
VhostUserMsgHeader {
request: request.into(),
flags: fl,
size,
_r: PhantomData,
}
}
pub fn get_code(&self) -> Result<R> {
R::try_from(self.request).map_err(|_| Error::InvalidMessage)
}
pub fn set_code(&mut self, request: R) {
self.request = request.into();
}
pub fn get_version(&self) -> u32 {
self.flags & 0x3
}
pub fn set_version(&mut self, ver: u32) {
self.flags &= !0x3;
self.flags |= ver & 0x3;
}
pub fn is_reply(&self) -> bool {
(self.flags & VhostUserHeaderFlag::REPLY.bits()) != 0
}
pub fn set_reply(&mut self, is_reply: bool) {
if is_reply {
self.flags |= VhostUserHeaderFlag::REPLY.bits();
} else {
self.flags &= !VhostUserHeaderFlag::REPLY.bits();
}
}
pub fn is_need_reply(&self) -> bool {
(self.flags & VhostUserHeaderFlag::NEED_REPLY.bits()) != 0
}
pub fn set_need_reply(&mut self, need_reply: bool) {
if need_reply {
self.flags |= VhostUserHeaderFlag::NEED_REPLY.bits();
} else {
self.flags &= !VhostUserHeaderFlag::NEED_REPLY.bits();
}
}
pub fn is_reply_for(&self, req: &VhostUserMsgHeader<R>) -> bool {
if let (Ok(code1), Ok(code2)) = (self.get_code(), req.get_code()) {
self.is_reply() && !req.is_reply() && code1 == code2
} else {
false
}
}
pub fn get_size(&self) -> u32 {
self.size
}
pub fn set_size(&mut self, size: u32) {
self.size = size;
}
}
impl<R: Req> Default for VhostUserMsgHeader<R> {
fn default() -> Self {
VhostUserMsgHeader {
request: 0,
flags: 0x1,
size: 0,
_r: PhantomData,
}
}
}
unsafe impl<R: Req> ByteValued for VhostUserMsgHeader<R> {}
impl<T: Req> VhostUserMsgValidator for VhostUserMsgHeader<T> {
#[allow(clippy::if_same_then_else)]
fn is_valid(&self) -> bool {
if self.get_code().is_err() {
return false;
} else if self.size as usize > MAX_MSG_SIZE {
return false;
} else if self.get_version() != 0x1 {
return false;
} else if (self.flags & VhostUserHeaderFlag::RESERVED_BITS.bits()) != 0 {
return false;
}
true
}
}
bitflags! {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct VhostUserVirtioFeatures: u64 {
const LOG_ALL = 0x400_0000;
const PROTOCOL_FEATURES = 0x4000_0000;
}
}
bitflags! {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct VhostUserProtocolFeatures: u64 {
const MQ = 0x0000_0001;
const LOG_SHMFD = 0x0000_0002;
const RARP = 0x0000_0004;
const REPLY_ACK = 0x0000_0008;
const MTU = 0x0000_0010;
const BACKEND_REQ = 0x0000_0020;
const CROSS_ENDIAN = 0x0000_0040;
const CRYPTO_SESSION = 0x0000_0080;
const PAGEFAULT = 0x0000_0100;
const CONFIG = 0x0000_0200;
const BACKEND_SEND_FD = 0x0000_0400;
const HOST_NOTIFIER = 0x0000_0800;
const INFLIGHT_SHMFD = 0x0000_1000;
const RESET_DEVICE = 0x0000_2000;
const INBAND_NOTIFICATIONS = 0x0000_4000;
const CONFIGURE_MEM_SLOTS = 0x0000_8000;
const STATUS = 0x0001_0000;
const XEN_MMAP = 0x0002_0000;
const SHARED_OBJECT = 0x0004_0000;
const DEVICE_STATE = 0x0008_0000;
const GET_VRING_BASE_INFLIGHT = 0x0010_0000;
const SHMEM = 0x0020_0000;
}
}
#[derive(Copy, Clone, Default)]
pub struct VhostUserEmpty;
unsafe impl ByteValued for VhostUserEmpty {}
impl VhostUserMsgValidator for VhostUserEmpty {}
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserU64 {
pub value: u64,
}
impl VhostUserU64 {
pub fn new(value: u64) -> Self {
VhostUserU64 { value }
}
}
unsafe impl ByteValued for VhostUserU64 {}
impl VhostUserMsgValidator for VhostUserU64 {}
#[repr(C, packed)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserMemory {
pub num_regions: u32,
pub padding1: u32,
}
impl VhostUserMemory {
pub fn new(cnt: u32) -> Self {
VhostUserMemory {
num_regions: cnt,
padding1: 0,
}
}
}
unsafe impl ByteValued for VhostUserMemory {}
impl VhostUserMsgValidator for VhostUserMemory {
#[allow(clippy::if_same_then_else)]
fn is_valid(&self) -> bool {
if self.padding1 != 0 {
return false;
} else if self.num_regions == 0 || self.num_regions > MAX_ATTACHED_FD_ENTRIES as u32 {
return false;
}
true
}
}
#[repr(C, packed)]
#[derive(Default, Clone, Copy)]
pub struct VhostUserMemoryRegion {
pub guest_phys_addr: u64,
pub memory_size: u64,
pub user_addr: u64,
pub mmap_offset: u64,
#[cfg(feature = "xen")]
pub xen_mmap_flags: u32,
#[cfg(feature = "xen")]
pub xen_mmap_data: u32,
}
impl VhostUserMemoryRegion {
fn is_valid_common(&self) -> bool {
self.memory_size != 0
&& self.guest_phys_addr.checked_add(self.memory_size).is_some()
&& self.user_addr.checked_add(self.memory_size).is_some()
&& self.mmap_offset.checked_add(self.memory_size).is_some()
}
}
#[cfg(not(feature = "xen"))]
impl VhostUserMemoryRegion {
pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
VhostUserMemoryRegion {
guest_phys_addr,
memory_size,
user_addr,
mmap_offset,
}
}
pub fn mmap_region<B: NewBitmap>(&self, file: File) -> Result<MmapRegion<B>> {
MmapRegion::<B>::from_file(
FileOffset::new(file, self.mmap_offset),
self.memory_size as usize,
)
.map_err(|e| Error::ReqHandlerError(io::Error::other(e)))
}
fn is_valid(&self) -> bool {
self.is_valid_common()
}
}
#[cfg(feature = "xen")]
impl VhostUserMemoryRegion {
pub fn with_xen(
guest_phys_addr: u64,
memory_size: u64,
user_addr: u64,
mmap_offset: u64,
xen_mmap_flags: u32,
xen_mmap_data: u32,
) -> Self {
VhostUserMemoryRegion {
guest_phys_addr,
memory_size,
user_addr,
mmap_offset,
xen_mmap_flags,
xen_mmap_data,
}
}
pub fn mmap_region<B: NewBitmap>(&self, file: File) -> Result<MmapRegion<B>> {
let range = MmapRange::new(
self.memory_size as usize,
Some(FileOffset::new(file, self.mmap_offset)),
GuestAddress(self.guest_phys_addr),
self.xen_mmap_flags,
self.xen_mmap_data,
);
MmapRegion::<B>::from_range(range).map_err(|e| Error::ReqHandlerError(io::Error::other(e)))
}
fn is_valid(&self) -> bool {
if !self.is_valid_common() {
false
} else {
match MmapXenFlags::from_bits(self.xen_mmap_flags) {
Some(flags) => flags.is_valid(),
None => false,
}
}
}
}
unsafe impl ByteValued for VhostUserMemoryRegion {}
impl VhostUserMsgValidator for VhostUserMemoryRegion {
fn is_valid(&self) -> bool {
self.is_valid()
}
}
pub type VhostUserMemoryPayload = Vec<VhostUserMemoryRegion>;
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct VhostUserSingleMemoryRegion {
padding: u64,
region: VhostUserMemoryRegion,
}
impl Deref for VhostUserSingleMemoryRegion {
type Target = VhostUserMemoryRegion;
fn deref(&self) -> &VhostUserMemoryRegion {
&self.region
}
}
#[cfg(not(feature = "xen"))]
impl VhostUserSingleMemoryRegion {
pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
VhostUserSingleMemoryRegion {
padding: 0,
region: VhostUserMemoryRegion::new(
guest_phys_addr,
memory_size,
user_addr,
mmap_offset,
),
}
}
}
#[cfg(feature = "xen")]
impl VhostUserSingleMemoryRegion {
pub fn new(
guest_phys_addr: u64,
memory_size: u64,
user_addr: u64,
mmap_offset: u64,
xen_mmap_flags: u32,
xen_mmap_data: u32,
) -> Self {
VhostUserSingleMemoryRegion {
padding: 0,
region: VhostUserMemoryRegion::with_xen(
guest_phys_addr,
memory_size,
user_addr,
mmap_offset,
xen_mmap_flags,
xen_mmap_data,
),
}
}
}
unsafe impl ByteValued for VhostUserSingleMemoryRegion {}
impl VhostUserMsgValidator for VhostUserSingleMemoryRegion {}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct VhostUserShMemConfig {
pub nregions: u32,
padding: u32,
pub memory_sizes: [u64; 256],
}
impl Default for VhostUserShMemConfig {
fn default() -> Self {
Self {
nregions: 0,
padding: 0,
memory_sizes: [0; 256],
}
}
}
impl VhostUserShMemConfig {
pub fn new(nregions: u32, memory: &[u64]) -> Self {
let memory_sizes: [u64; 256] = std::array::from_fn(|i| *memory.get(i).unwrap_or(&0));
Self {
nregions,
padding: 0,
memory_sizes,
}
}
}
unsafe impl ByteValued for VhostUserShMemConfig {}
impl VhostUserMsgValidator for VhostUserShMemConfig {}
#[repr(C, packed)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserVringState {
pub index: u32,
pub num: u32,
}
impl VhostUserVringState {
pub fn new(index: u32, num: u32) -> Self {
VhostUserVringState { index, num }
}
}
unsafe impl ByteValued for VhostUserVringState {}
impl VhostUserMsgValidator for VhostUserVringState {}
bitflags! {
pub struct VhostUserVringAddrFlags: u32 {
const VHOST_VRING_F_LOG = 0x1;
}
}
#[repr(C, packed)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserVringAddr {
pub index: u32,
pub flags: u32,
pub descriptor: u64,
pub used: u64,
pub available: u64,
pub log: u64,
}
impl VhostUserVringAddr {
pub fn new(
index: u32,
flags: VhostUserVringAddrFlags,
descriptor: u64,
used: u64,
available: u64,
log: u64,
) -> Self {
VhostUserVringAddr {
index,
flags: flags.bits(),
descriptor,
used,
available,
log,
}
}
#[allow(clippy::useless_conversion)]
pub fn from_config_data(index: u32, config_data: &VringConfigData) -> Self {
let log_addr = config_data.log_addr.unwrap_or(0);
VhostUserVringAddr {
index,
flags: config_data.flags,
descriptor: config_data.desc_table_addr,
used: config_data.used_ring_addr,
available: config_data.avail_ring_addr,
log: log_addr,
}
}
}
unsafe impl ByteValued for VhostUserVringAddr {}
impl VhostUserMsgValidator for VhostUserVringAddr {
#[allow(clippy::if_same_then_else)]
fn is_valid(&self) -> bool {
if (self.flags & !VhostUserVringAddrFlags::all().bits()) != 0 {
return false;
} else if self.descriptor & 0xf != 0 {
return false;
} else if self.available & 0x1 != 0 {
return false;
} else if self.used & 0x3 != 0 {
return false;
}
true
}
}
bitflags! {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct VhostUserConfigFlags: u32 {
const WRITABLE = 0x1;
const LIVE_MIGRATION = 0x2;
}
}
#[repr(C, packed)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserConfig {
pub offset: u32,
pub size: u32,
pub flags: u32,
}
impl VhostUserConfig {
pub fn new(offset: u32, size: u32, flags: VhostUserConfigFlags) -> Self {
VhostUserConfig {
offset,
size,
flags: flags.bits(),
}
}
}
unsafe impl ByteValued for VhostUserConfig {}
impl VhostUserMsgValidator for VhostUserConfig {
#[allow(clippy::if_same_then_else)]
fn is_valid(&self) -> bool {
let end_addr = match self.size.checked_add(self.offset) {
Some(addr) => addr,
None => return false,
};
if (self.flags & !VhostUserConfigFlags::all().bits()) != 0 {
return false;
} else if self.size == 0 || end_addr > VHOST_USER_CONFIG_SIZE {
return false;
}
true
}
}
pub type VhostUserConfigPayload = Vec<u8>;
#[repr(C)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserInflight {
pub mmap_size: u64,
pub mmap_offset: u64,
pub num_queues: u16,
pub queue_size: u16,
}
impl VhostUserInflight {
pub fn new(mmap_size: u64, mmap_offset: u64, num_queues: u16, queue_size: u16) -> Self {
VhostUserInflight {
mmap_size,
mmap_offset,
num_queues,
queue_size,
}
}
}
unsafe impl ByteValued for VhostUserInflight {}
impl VhostUserMsgValidator for VhostUserInflight {
fn is_valid(&self) -> bool {
if self.num_queues == 0 || self.queue_size == 0 {
return false;
}
true
}
}
#[repr(C)]
#[derive(Copy, Clone, Default)]
pub struct VhostUserLog {
pub mmap_size: u64,
pub mmap_offset: u64,
}
impl VhostUserLog {
pub fn new(mmap_size: u64, mmap_offset: u64) -> Self {
VhostUserLog {
mmap_size,
mmap_offset,
}
}
}
unsafe impl ByteValued for VhostUserLog {}
impl VhostUserMsgValidator for VhostUserLog {
fn is_valid(&self) -> bool {
if self.mmap_size == 0 || self.mmap_offset.checked_add(self.mmap_size).is_none() {
return false;
}
true
}
}
enum_value! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum VhostTransferStateDirection: u32 {
SAVE = 0,
LOAD = 1,
}
}
enum_value! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum VhostTransferStatePhase: u32 {
STOPPED = 0,
}
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct VhostUserSharedMsg {
pub uuid: Uuid,
}
unsafe impl ByteValued for VhostUserSharedMsg {}
impl VhostUserMsgValidator for VhostUserSharedMsg {
fn is_valid(&self) -> bool {
!(self.uuid.is_nil() || self.uuid.is_max())
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Default)]
pub struct VhostUserTransferDeviceState {
pub direction: u32,
pub phase: u32,
}
unsafe impl ByteValued for VhostUserTransferDeviceState {}
impl VhostUserTransferDeviceState {
pub fn new(direction: VhostTransferStateDirection, phase: VhostTransferStatePhase) -> Self {
VhostUserTransferDeviceState {
direction: direction as u32,
phase: phase as u32,
}
}
}
impl VhostUserMsgValidator for VhostUserTransferDeviceState {
fn is_valid(&self) -> bool {
VhostTransferStateDirection::try_from(self.direction).is_ok()
&& VhostTransferStatePhase::try_from(self.phase).is_ok()
}
}
bitflags! {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub struct VhostUserMMapFlags: u64 {
const WRITABLE = 1 << 0;
}
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone, Default)]
pub struct VhostUserMMap {
pub shmid: u8,
pub padding: [u8; 7],
pub fd_offset: u64,
pub shm_offset: u64,
pub len: u64,
pub flags: u64,
}
unsafe impl ByteValued for VhostUserMMap {}
impl VhostUserMsgValidator for VhostUserMMap {
fn is_valid(&self) -> bool {
self.fd_offset.checked_add(self.len).is_some()
&& self.shm_offset.checked_add(self.len).is_some()
&& self.len != 0
&& VhostUserMMapFlags::from_bits(self.flags).is_some()
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Default)]
pub struct DescStateSplit {
pub inflight: u8,
padding: [u8; 5],
pub next: u16,
pub counter: u64,
}
impl DescStateSplit {
pub fn new() -> Self {
Self::default()
}
}
#[repr(C, packed)]
pub struct QueueRegionSplit {
pub features: u64,
pub version: u16,
pub desc_num: u16,
pub last_batch_head: u16,
pub used_idx: u16,
pub desc: u64,
}
impl QueueRegionSplit {
pub fn new(features: u64, queue_size: u16) -> Self {
QueueRegionSplit {
features,
version: 1,
desc_num: queue_size,
last_batch_head: 0,
used_idx: 0,
desc: 0,
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Default)]
pub struct DescStatePacked {
pub inflight: u8,
padding: u8,
pub next: u16,
pub last: u16,
pub num: u16,
pub counter: u64,
pub id: u16,
pub flags: u16,
pub len: u32,
pub addr: u64,
}
impl DescStatePacked {
pub fn new() -> Self {
Self::default()
}
}
#[repr(C, packed)]
pub struct QueueRegionPacked {
pub features: u64,
pub version: u16,
pub desc_num: u16,
pub free_head: u16,
pub old_free_head: u16,
pub used_idx: u16,
pub old_used_idx: u16,
pub used_wrap_counter: u8,
pub old_used_wrap_counter: u8,
padding: [u8; 7],
pub desc: u64,
}
impl QueueRegionPacked {
pub fn new(features: u64, queue_size: u16) -> Self {
QueueRegionPacked {
features,
version: 1,
desc_num: queue_size,
free_head: 0,
old_free_head: 0,
used_idx: 0,
old_used_idx: 0,
used_wrap_counter: 0,
old_used_wrap_counter: 0,
padding: [0; 7],
desc: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
#[cfg(feature = "xen")]
impl VhostUserMemoryRegion {
fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
Self::with_xen(
guest_phys_addr,
memory_size,
user_addr,
mmap_offset,
MmapXenFlags::FOREIGN.bits(),
0,
)
}
}
#[test]
fn check_transfer_state_direction_code() {
let load_code: u32 = VhostTransferStateDirection::LOAD.into();
assert!(VhostTransferStateDirection::try_from(load_code).is_ok());
assert_eq!(load_code, load_code.clone());
let save_code: u32 = VhostTransferStateDirection::SAVE.into();
assert!(VhostTransferStateDirection::try_from(save_code).is_ok());
assert_eq!(save_code, save_code.clone());
assert!(VhostTransferStateDirection::try_from(3).is_err());
}
#[test]
fn check_transfer_state_phase_code() {
let code: u32 = VhostTransferStatePhase::STOPPED.into();
assert!(VhostTransferStatePhase::try_from(code).is_ok());
assert_eq!(code, code.clone());
assert!(VhostTransferStatePhase::try_from(1).is_err());
}
#[test]
fn check_frontend_request_code() {
let code: u32 = FrontendReq::GET_FEATURES.into();
assert!(FrontendReq::try_from(code).is_ok());
assert_eq!(code, code.clone());
assert!(FrontendReq::try_from(10000).is_err());
}
#[test]
fn check_backend_request_code() {
let code: u32 = BackendReq::CONFIG_CHANGE_MSG.into();
assert!(BackendReq::try_from(code).is_ok());
assert_eq!(code, code.clone());
assert!(BackendReq::try_from(10000).is_err());
}
#[test]
fn msg_header_ops() {
let mut hdr = VhostUserMsgHeader::new(FrontendReq::GET_FEATURES, 0, 0x100);
assert_eq!(hdr.get_code().unwrap(), FrontendReq::GET_FEATURES);
hdr.set_code(FrontendReq::SET_FEATURES);
assert_eq!(hdr.get_code().unwrap(), FrontendReq::SET_FEATURES);
assert_eq!(hdr.get_version(), 0x1);
assert!(!hdr.is_reply());
hdr.set_reply(true);
assert!(hdr.is_reply());
hdr.set_reply(false);
assert!(!hdr.is_need_reply());
hdr.set_need_reply(true);
assert!(hdr.is_need_reply());
hdr.set_need_reply(false);
assert_eq!(hdr.get_size(), 0x100);
hdr.set_size(0x200);
assert_eq!(hdr.get_size(), 0x200);
assert!(!hdr.is_need_reply());
assert!(!hdr.is_reply());
assert_eq!(hdr.get_version(), 0x1);
assert!(hdr.is_valid());
hdr.set_size(0x2000);
assert!(!hdr.is_valid());
hdr.set_size(0x100);
assert_eq!(hdr.get_size(), 0x100);
assert!(hdr.is_valid());
hdr.set_size((MAX_MSG_SIZE - mem::size_of::<VhostUserMsgHeader<FrontendReq>>()) as u32);
assert!(hdr.is_valid());
hdr.set_size(0x0);
assert!(hdr.is_valid());
hdr.set_version(0x0);
assert!(!hdr.is_valid());
hdr.set_version(0x2);
assert!(!hdr.is_valid());
hdr.set_version(0x1);
assert!(hdr.is_valid());
assert_eq!(hdr, hdr.clone());
assert_eq!(hdr.clone().get_code().unwrap(), hdr.get_code().unwrap());
assert_eq!(format!("{:?}", hdr.clone()), format!("{:?}", hdr));
}
#[test]
fn test_vhost_user_message_u64() {
let val = VhostUserU64::default();
let val1 = VhostUserU64::new(0);
let a = val.value;
let b = val1.value;
assert_eq!(a, b);
let a = VhostUserU64::new(1).value;
assert_eq!(a, 1);
}
#[test]
fn check_user_memory() {
let mut msg = VhostUserMemory::new(1);
assert!(msg.is_valid());
msg.num_regions = MAX_ATTACHED_FD_ENTRIES as u32;
assert!(msg.is_valid());
msg.num_regions += 1;
assert!(!msg.is_valid());
msg.num_regions = 0xFFFFFFFF;
assert!(!msg.is_valid());
msg.num_regions = MAX_ATTACHED_FD_ENTRIES as u32;
msg.padding1 = 1;
assert!(!msg.is_valid());
}
#[test]
fn check_user_memory_region() {
let mut msg = VhostUserMemoryRegion::new(0, 0x1000, 0, 0);
assert!(msg.is_valid());
msg.guest_phys_addr = 0xFFFFFFFFFFFFEFFF;
assert!(msg.is_valid());
msg.guest_phys_addr = 0xFFFFFFFFFFFFF000;
assert!(!msg.is_valid());
msg.guest_phys_addr = 0xFFFFFFFFFFFF0000;
msg.memory_size = 0;
assert!(!msg.is_valid());
let a = msg.guest_phys_addr;
let b = msg.guest_phys_addr;
assert_eq!(a, b);
let msg = VhostUserMemoryRegion::default();
let a = msg.guest_phys_addr;
assert_eq!(a, 0);
let a = msg.memory_size;
assert_eq!(a, 0);
let a = msg.user_addr;
assert_eq!(a, 0);
let a = msg.mmap_offset;
assert_eq!(a, 0);
}
#[test]
fn test_vhost_user_state() {
let state = VhostUserVringState::new(5, 8);
let a = state.index;
assert_eq!(a, 5);
let a = state.num;
assert_eq!(a, 8);
assert!(state.is_valid());
let state = VhostUserVringState::default();
let a = state.index;
assert_eq!(a, 0);
let a = state.num;
assert_eq!(a, 0);
assert!(state.is_valid());
}
#[test]
fn test_vhost_user_addr() {
let mut addr = VhostUserVringAddr::new(
2,
VhostUserVringAddrFlags::VHOST_VRING_F_LOG,
0x1000,
0x2000,
0x3000,
0x4000,
);
let a = addr.index;
assert_eq!(a, 2);
let a = addr.flags;
assert_eq!(a, VhostUserVringAddrFlags::VHOST_VRING_F_LOG.bits());
let a = addr.descriptor;
assert_eq!(a, 0x1000);
let a = addr.used;
assert_eq!(a, 0x2000);
let a = addr.available;
assert_eq!(a, 0x3000);
let a = addr.log;
assert_eq!(a, 0x4000);
assert!(addr.is_valid());
addr.descriptor = 0x1001;
assert!(!addr.is_valid());
addr.descriptor = 0x1000;
addr.available = 0x3001;
assert!(!addr.is_valid());
addr.available = 0x3000;
addr.used = 0x2001;
assert!(!addr.is_valid());
addr.used = 0x2000;
assert!(addr.is_valid());
}
#[test]
fn test_vhost_user_state_from_config() {
let config = VringConfigData {
queue_max_size: 256,
queue_size: 128,
flags: VhostUserVringAddrFlags::VHOST_VRING_F_LOG.bits(),
desc_table_addr: 0x1000,
used_ring_addr: 0x2000,
avail_ring_addr: 0x3000,
log_addr: Some(0x4000),
};
let addr = VhostUserVringAddr::from_config_data(2, &config);
let a = addr.index;
assert_eq!(a, 2);
let a = addr.flags;
assert_eq!(a, VhostUserVringAddrFlags::VHOST_VRING_F_LOG.bits());
let a = addr.descriptor;
assert_eq!(a, 0x1000);
let a = addr.used;
assert_eq!(a, 0x2000);
let a = addr.available;
assert_eq!(a, 0x3000);
let a = addr.log;
assert_eq!(a, 0x4000);
assert!(addr.is_valid());
}
#[test]
fn check_user_vring_addr() {
let mut msg =
VhostUserVringAddr::new(0, VhostUserVringAddrFlags::all(), 0x0, 0x0, 0x0, 0x0);
assert!(msg.is_valid());
msg.descriptor = 1;
assert!(!msg.is_valid());
msg.descriptor = 0;
msg.available = 1;
assert!(!msg.is_valid());
msg.available = 0;
msg.used = 1;
assert!(!msg.is_valid());
msg.used = 0;
msg.flags |= 0x80000000;
assert!(!msg.is_valid());
msg.flags &= !0x80000000;
}
#[test]
fn check_user_config_msg() {
let mut msg =
VhostUserConfig::new(0, VHOST_USER_CONFIG_SIZE, VhostUserConfigFlags::WRITABLE);
assert!(msg.is_valid());
msg.size = 0;
assert!(!msg.is_valid());
msg.size = 1;
assert!(msg.is_valid());
msg.offset = u32::MAX;
assert!(!msg.is_valid());
msg.offset = VHOST_USER_CONFIG_SIZE;
assert!(!msg.is_valid());
msg.offset = VHOST_USER_CONFIG_SIZE - 1;
assert!(msg.is_valid());
msg.size = 2;
assert!(!msg.is_valid());
msg.size = 1;
msg.flags |= VhostUserConfigFlags::LIVE_MIGRATION.bits();
assert!(msg.is_valid());
msg.flags |= 0x4;
assert!(!msg.is_valid());
}
#[test]
fn check_user_mmap_msg() {
let mut msg = VhostUserMMap {
shmid: 0,
padding: [0; 7],
fd_offset: 0,
shm_offset: 0x1000,
len: 0x2000,
flags: VhostUserMMapFlags::WRITABLE.bits(),
};
assert!(msg.is_valid());
msg.flags = 0;
assert!(msg.is_valid());
msg.flags = 0x2; assert!(!msg.is_valid());
msg.flags = VhostUserMMapFlags::WRITABLE.bits();
msg.len = 0;
assert!(!msg.is_valid());
msg.len = 0x2000;
msg.fd_offset = u64::MAX - msg.len + 1;
assert!(!msg.is_valid());
msg.fd_offset = 0;
msg.shm_offset = u64::MAX - msg.len + 1;
assert!(!msg.is_valid());
}
}