#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(clippy::upper_case_acronyms)]
use std::fmt::Debug;
use std::marker::PhantomData;
use vm_memory::ByteValued;
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>
{
fn is_valid(&self) -> bool;
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum MasterReq {
NOOP = 0,
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_SLAVE_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,
MAX_CMD = 41,
}
impl From<MasterReq> for u32 {
fn from(req: MasterReq) -> u32 {
req as u32
}
}
impl Req for MasterReq {
fn is_valid(&self) -> bool {
(*self > MasterReq::NOOP) && (*self < MasterReq::MAX_CMD)
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SlaveReq {
NOOP = 0,
IOTLB_MSG = 1,
CONFIG_CHANGE_MSG = 2,
VRING_HOST_NOTIFIER_MSG = 3,
VRING_CALL = 4,
VRING_ERR = 5,
FS_MAP = 6,
FS_UNMAP = 7,
FS_SYNC = 8,
FS_IO = 9,
MAX_CMD = 10,
}
impl From<SlaveReq> for u32 {
fn from(req: SlaveReq) -> u32 {
req as u32
}
}
impl Req for SlaveReq {
fn is_valid(&self) -> bool {
(*self > SlaveReq::NOOP) && (*self < SlaveReq::MAX_CMD)
}
}
pub trait VhostUserMsgValidator {
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(packed)]
#[derive(Copy)]
pub(super) struct VhostUserMsgHeader<R: Req> {
request: u32,
flags: u32,
size: u32,
_r: PhantomData<R>,
}
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) -> R {
unsafe { std::mem::transmute_copy::<u32, R>(&{ self.request }) }
}
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 {
self.is_reply() && !req.is_reply() && self.get_code() == req.get_code()
}
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_valid() {
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! {
pub struct VhostUserVirtioFeatures: u64 {
const PROTOCOL_FEATURES = 0x4000_0000;
}
}
bitflags! {
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 SLAVE_REQ = 0x0000_0020;
const CROSS_ENDIAN = 0x0000_0040;
const CRYPTO_SESSION = 0x0000_0080;
const PAGEFAULT = 0x0000_0100;
const CONFIG = 0x0000_0200;
const SLAVE_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;
}
}
#[repr(packed)]
#[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(packed)]
#[derive(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,
}
}
}
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(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,
}
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,
}
}
}
impl VhostUserMsgValidator for VhostUserMemoryRegion {
fn is_valid(&self) -> bool {
if self.memory_size == 0
|| self.guest_phys_addr.checked_add(self.memory_size).is_none()
|| self.user_addr.checked_add(self.memory_size).is_none()
|| self.mmap_offset.checked_add(self.memory_size).is_none()
{
return false;
}
true
}
}
pub type VhostUserMemoryPayload = Vec<VhostUserMemoryRegion>;
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct VhostUserSingleMemoryRegion {
padding: u64,
pub guest_phys_addr: u64,
pub memory_size: u64,
pub user_addr: u64,
pub mmap_offset: u64,
}
impl VhostUserSingleMemoryRegion {
pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self {
VhostUserSingleMemoryRegion {
padding: 0,
guest_phys_addr,
memory_size,
user_addr,
mmap_offset,
}
}
}
impl VhostUserMsgValidator for VhostUserSingleMemoryRegion {
fn is_valid(&self) -> bool {
if self.memory_size == 0
|| self.guest_phys_addr.checked_add(self.memory_size).is_none()
|| self.user_addr.checked_add(self.memory_size).is_none()
|| self.mmap_offset.checked_add(self.memory_size).is_none()
{
return false;
}
true
}
}
#[repr(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(packed)]
#[derive(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,
}
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::identity_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,
}
}
}
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! {
pub struct VhostUserConfigFlags: u32 {
const WRITABLE = 0x1;
const LIVE_MIGRATION = 0x2;
}
}
#[repr(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(Default, Clone)]
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,
}
}
}
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
}
}
bitflags! {
#[derive(Default)]
pub struct VhostUserFSSlaveMsgFlags: u64 {
const EMPTY = 0x0;
const MAP_R = 0x1;
const MAP_W = 0x2;
}
}
pub const VHOST_USER_FS_SLAVE_ENTRIES: usize = 8;
#[repr(packed)]
#[derive(Default)]
pub struct VhostUserFSSlaveMsg {
pub fd_offset: [u64; VHOST_USER_FS_SLAVE_ENTRIES],
pub cache_offset: [u64; VHOST_USER_FS_SLAVE_ENTRIES],
pub len: [u64; VHOST_USER_FS_SLAVE_ENTRIES],
pub flags: [VhostUserFSSlaveMsgFlags; VHOST_USER_FS_SLAVE_ENTRIES],
}
impl VhostUserMsgValidator for VhostUserFSSlaveMsg {
fn is_valid(&self) -> bool {
for i in 0..VHOST_USER_FS_SLAVE_ENTRIES {
if ({ self.flags[i] }.bits() & !VhostUserFSSlaveMsgFlags::all().bits()) != 0
|| self.fd_offset[i].checked_add(self.len[i]).is_none()
|| self.cache_offset[i].checked_add(self.len[i]).is_none()
{
return false;
}
}
true
}
}
#[repr(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(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(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(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;
#[test]
fn check_master_request_code() {
let code = MasterReq::NOOP;
assert!(!code.is_valid());
let code = MasterReq::MAX_CMD;
assert!(!code.is_valid());
assert!(code > MasterReq::NOOP);
let code = MasterReq::GET_FEATURES;
assert!(code.is_valid());
assert_eq!(code, code.clone());
let code: MasterReq = unsafe { std::mem::transmute::<u32, MasterReq>(10000u32) };
assert!(!code.is_valid());
}
#[test]
fn check_slave_request_code() {
let code = SlaveReq::NOOP;
assert!(!code.is_valid());
let code = SlaveReq::MAX_CMD;
assert!(!code.is_valid());
assert!(code > SlaveReq::NOOP);
let code = SlaveReq::CONFIG_CHANGE_MSG;
assert!(code.is_valid());
assert_eq!(code, code.clone());
let code: SlaveReq = unsafe { std::mem::transmute::<u32, SlaveReq>(10000u32) };
assert!(!code.is_valid());
}
#[test]
fn msg_header_ops() {
let mut hdr = VhostUserMsgHeader::new(MasterReq::GET_FEATURES, 0, 0x100);
assert_eq!(hdr.get_code(), MasterReq::GET_FEATURES);
hdr.set_code(MasterReq::SET_FEATURES);
assert_eq!(hdr.get_code(), MasterReq::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<MasterReq>>()) 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(), hdr.get_code());
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 {
guest_phys_addr: 0,
memory_size: 0x1000,
user_addr: 0,
mmap_offset: 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 test_vhost_user_fs_slave() {
let mut fs_slave = VhostUserFSSlaveMsg::default();
assert!(fs_slave.is_valid());
fs_slave.fd_offset[0] = 0xffff_ffff_ffff_ffff;
fs_slave.len[0] = 0x1;
assert!(!fs_slave.is_valid());
assert_ne!(
VhostUserFSSlaveMsgFlags::MAP_R,
VhostUserFSSlaveMsgFlags::MAP_W
);
assert_eq!(VhostUserFSSlaveMsgFlags::EMPTY.bits(), 0);
}
}