diff options
Diffstat (limited to 'src/vhost_user/message.rs')
-rw-r--r-- | src/vhost_user/message.rs | 1230 |
1 files changed, 0 insertions, 1230 deletions
diff --git a/src/vhost_user/message.rs b/src/vhost_user/message.rs deleted file mode 100644 index fc33e1b..0000000 --- a/src/vhost_user/message.rs +++ /dev/null @@ -1,1230 +0,0 @@ -// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//! Define communication messages for the vhost-user protocol. -//! -//! For message definition, please refer to the [vhost-user spec](https://github.com/qemu/qemu/blob/f7526eece29cd2e36a63b6703508b24453095eb8/docs/interop/vhost-user.txt). - -#![allow(dead_code)] -#![allow(non_camel_case_types)] -#![allow(clippy::upper_case_acronyms)] - -use std::fmt::Debug; -use std::marker::PhantomData; - -use crate::VringConfigData; - -/// The vhost-user specification uses a field of u32 to store message length. -/// On the other hand, preallocated buffers are needed to receive messages from the Unix domain -/// socket. To preallocating a 4GB buffer for each vhost-user message is really just an overhead. -/// Among all defined vhost-user messages, only the VhostUserConfig and VhostUserMemory has variable -/// message size. For the VhostUserConfig, a maximum size of 4K is enough because the user -/// configuration space for virtio devices is (4K - 0x100) bytes at most. For the VhostUserMemory, -/// 4K should be enough too because it can support 255 memory regions at most. -pub const MAX_MSG_SIZE: usize = 0x1000; - -/// The VhostUserMemory message has variable message size and variable number of attached file -/// descriptors. Each user memory region entry in the message payload occupies 32 bytes, -/// so setting maximum number of attached file descriptors based on the maximum message size. -/// But rust only implements Default and AsMut traits for arrays with 0 - 32 entries, so further -/// reduce the maximum number... -// pub const MAX_ATTACHED_FD_ENTRIES: usize = (MAX_MSG_SIZE - 8) / 32; -pub const MAX_ATTACHED_FD_ENTRIES: usize = 32; - -/// Starting position (inclusion) of the device configuration space in virtio devices. -pub const VHOST_USER_CONFIG_OFFSET: u32 = 0x100; - -/// Ending position (exclusion) of the device configuration space in virtio devices. -pub const VHOST_USER_CONFIG_SIZE: u32 = 0x1000; - -/// Maximum number of vrings supported. -pub const VHOST_USER_MAX_VRINGS: u64 = 0x8000u64; - -pub(super) trait Req: - Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Into<u32> -{ - fn is_valid(&self) -> bool; -} - -/// Type of requests sending from masters to slaves. -#[repr(u32)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum MasterReq { - /// Null operation. - NOOP = 0, - /// Get from the underlying vhost implementation the features bit mask. - GET_FEATURES = 1, - /// Enable features in the underlying vhost implementation using a bit mask. - SET_FEATURES = 2, - /// Set the current Master as an owner of the session. - SET_OWNER = 3, - /// No longer used. - RESET_OWNER = 4, - /// Set the memory map regions on the slave so it can translate the vring addresses. - SET_MEM_TABLE = 5, - /// Set logging shared memory space. - SET_LOG_BASE = 6, - /// Set the logging file descriptor, which is passed as ancillary data. - SET_LOG_FD = 7, - /// Set the size of the queue. - SET_VRING_NUM = 8, - /// Set the addresses of the different aspects of the vring. - SET_VRING_ADDR = 9, - /// Set the base offset in the available vring. - SET_VRING_BASE = 10, - /// Get the available vring base offset. - GET_VRING_BASE = 11, - /// Set the event file descriptor for adding buffers to the vring. - SET_VRING_KICK = 12, - /// Set the event file descriptor to signal when buffers are used. - SET_VRING_CALL = 13, - /// Set the event file descriptor to signal when error occurs. - SET_VRING_ERR = 14, - /// Get the protocol feature bit mask from the underlying vhost implementation. - GET_PROTOCOL_FEATURES = 15, - /// Enable protocol features in the underlying vhost implementation. - SET_PROTOCOL_FEATURES = 16, - /// Query how many queues the backend supports. - GET_QUEUE_NUM = 17, - /// Signal slave to enable or disable corresponding vring. - SET_VRING_ENABLE = 18, - /// Ask vhost user backend to broadcast a fake RARP to notify the migration is terminated - /// for guest that does not support GUEST_ANNOUNCE. - SEND_RARP = 19, - /// Set host MTU value exposed to the guest. - NET_SET_MTU = 20, - /// Set the socket file descriptor for slave initiated requests. - SET_SLAVE_REQ_FD = 21, - /// Send IOTLB messages with struct vhost_iotlb_msg as payload. - IOTLB_MSG = 22, - /// Set the endianness of a VQ for legacy devices. - SET_VRING_ENDIAN = 23, - /// Fetch the contents of the virtio device configuration space. - GET_CONFIG = 24, - /// Change the contents of the virtio device configuration space. - SET_CONFIG = 25, - /// Create a session for crypto operation. - CREATE_CRYPTO_SESSION = 26, - /// Close a session for crypto operation. - CLOSE_CRYPTO_SESSION = 27, - /// Advise slave that a migration with postcopy enabled is underway. - POSTCOPY_ADVISE = 28, - /// Advise slave that a transition to postcopy mode has happened. - POSTCOPY_LISTEN = 29, - /// Advise that postcopy migration has now completed. - POSTCOPY_END = 30, - /// Get a shared buffer from slave. - GET_INFLIGHT_FD = 31, - /// Send the shared inflight buffer back to slave. - SET_INFLIGHT_FD = 32, - /// Sets the GPU protocol socket file descriptor. - GPU_SET_SOCKET = 33, - /// Ask the vhost user backend to disable all rings and reset all internal - /// device state to the initial state. - RESET_DEVICE = 34, - /// Indicate that a buffer was added to the vring instead of signalling it - /// using the vring’s kick file descriptor. - VRING_KICK = 35, - /// Return a u64 payload containing the maximum number of memory slots. - GET_MAX_MEM_SLOTS = 36, - /// Update the memory tables by adding the region described. - ADD_MEM_REG = 37, - /// Update the memory tables by removing the region described. - REM_MEM_REG = 38, - /// Notify the backend with updated device status as defined in the VIRTIO - /// specification. - SET_STATUS = 39, - /// Query the backend for its device status as defined in the VIRTIO - /// specification. - GET_STATUS = 40, - /// Upper bound of valid commands. - 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) - } -} - -/// Type of requests sending from slaves to masters. -#[repr(u32)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum SlaveReq { - /// Null operation. - NOOP = 0, - /// Send IOTLB messages with struct vhost_iotlb_msg as payload. - IOTLB_MSG = 1, - /// Notify that the virtio device's configuration space has changed. - CONFIG_CHANGE_MSG = 2, - /// Set host notifier for a specified queue. - VRING_HOST_NOTIFIER_MSG = 3, - /// Indicate that a buffer was used from the vring. - VRING_CALL = 4, - /// Indicate that an error occurred on the specific vring. - VRING_ERR = 5, - /// Virtio-fs draft: map file content into the window. - FS_MAP = 6, - /// Virtio-fs draft: unmap file content from the window. - FS_UNMAP = 7, - /// Virtio-fs draft: sync file content. - FS_SYNC = 8, - /// Virtio-fs draft: perform a read/write from an fd directly to GPA. - FS_IO = 9, - /// Upper bound of valid commands. - 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) - } -} - -/// Vhost message Validator. -pub trait VhostUserMsgValidator { - /// Validate message syntax only. - /// It doesn't validate message semantics such as protocol version number and dependency - /// on feature flags etc. - fn is_valid(&self) -> bool { - true - } -} - -// Bit mask for common message flags. -bitflags! { - /// Common message flags for vhost-user requests and replies. - pub struct VhostUserHeaderFlag: u32 { - /// Bits[0..2] is message version number. - const VERSION = 0x3; - /// Mark message as reply. - const REPLY = 0x4; - /// Sender anticipates a reply message from the peer. - const NEED_REPLY = 0x8; - /// All valid bits. - const ALL_FLAGS = 0xc; - /// All reserved bits. - const RESERVED_BITS = !0xf; - } -} - -/// Common message header for vhost-user requests and replies. -/// A vhost-user message consists of 3 header fields and an optional payload. All numbers are in the -/// machine native byte order. -#[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("Point") - .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> { - /// Create a new instance of `VhostUserMsgHeader`. - pub fn new(request: R, flags: u32, size: u32) -> Self { - // Default to protocol version 1 - let fl = (flags & VhostUserHeaderFlag::ALL_FLAGS.bits()) | 0x1; - VhostUserMsgHeader { - request: request.into(), - flags: fl, - size, - _r: PhantomData, - } - } - - /// Get message type. - pub fn get_code(&self) -> R { - // It's safe because R is marked as repr(u32). - unsafe { std::mem::transmute_copy::<u32, R>(&{ self.request }) } - } - - /// Set message type. - pub fn set_code(&mut self, request: R) { - self.request = request.into(); - } - - /// Get message version number. - pub fn get_version(&self) -> u32 { - self.flags & 0x3 - } - - /// Set message version number. - pub fn set_version(&mut self, ver: u32) { - self.flags &= !0x3; - self.flags |= ver & 0x3; - } - - /// Check whether it's a reply message. - pub fn is_reply(&self) -> bool { - (self.flags & VhostUserHeaderFlag::REPLY.bits()) != 0 - } - - /// Mark message as reply. - pub fn set_reply(&mut self, is_reply: bool) { - if is_reply { - self.flags |= VhostUserHeaderFlag::REPLY.bits(); - } else { - self.flags &= !VhostUserHeaderFlag::REPLY.bits(); - } - } - - /// Check whether reply for this message is requested. - pub fn is_need_reply(&self) -> bool { - (self.flags & VhostUserHeaderFlag::NEED_REPLY.bits()) != 0 - } - - /// Mark that reply for this message is needed. - 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(); - } - } - - /// Check whether it's the reply message for the request `req`. - pub fn is_reply_for(&self, req: &VhostUserMsgHeader<R>) -> bool { - self.is_reply() && !req.is_reply() && self.get_code() == req.get_code() - } - - /// Get message size. - pub fn get_size(&self) -> u32 { - self.size - } - - /// Set message 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, - } - } -} - -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 - } -} - -// Bit mask for transport specific flags in VirtIO feature set defined by vhost-user. -bitflags! { - /// Transport specific flags in VirtIO feature set defined by vhost-user. - pub struct VhostUserVirtioFeatures: u64 { - /// Feature flag for the protocol feature. - const PROTOCOL_FEATURES = 0x4000_0000; - } -} - -// Bit mask for vhost-user protocol feature flags. -bitflags! { - /// Vhost-user protocol feature flags. - pub struct VhostUserProtocolFeatures: u64 { - /// Support multiple queues. - const MQ = 0x0000_0001; - /// Support logging through shared memory fd. - const LOG_SHMFD = 0x0000_0002; - /// Support broadcasting fake RARP packet. - const RARP = 0x0000_0004; - /// Support sending reply messages for requests with NEED_REPLY flag set. - const REPLY_ACK = 0x0000_0008; - /// Support setting MTU for virtio-net devices. - const MTU = 0x0000_0010; - /// Allow the slave to send requests to the master by an optional communication channel. - const SLAVE_REQ = 0x0000_0020; - /// Support setting slave endian by SET_VRING_ENDIAN. - const CROSS_ENDIAN = 0x0000_0040; - /// Support crypto operations. - const CRYPTO_SESSION = 0x0000_0080; - /// Support sending userfault_fd from slaves to masters. - const PAGEFAULT = 0x0000_0100; - /// Support Virtio device configuration. - const CONFIG = 0x0000_0200; - /// Allow the slave to send fds (at most 8 descriptors in each message) to the master. - const SLAVE_SEND_FD = 0x0000_0400; - /// Allow the slave to register a host notifier. - const HOST_NOTIFIER = 0x0000_0800; - /// Support inflight shmfd. - const INFLIGHT_SHMFD = 0x0000_1000; - /// Support resetting the device. - const RESET_DEVICE = 0x0000_2000; - /// Support inband notifications. - const INBAND_NOTIFICATIONS = 0x0000_4000; - /// Support configuring memory slots. - const CONFIGURE_MEM_SLOTS = 0x0000_8000; - /// Support reporting status. - const STATUS = 0x0001_0000; - } -} - -/// A generic message to encapsulate a 64-bit value. -#[repr(packed)] -#[derive(Default)] -pub struct VhostUserU64 { - /// The encapsulated 64-bit common value. - pub value: u64, -} - -impl VhostUserU64 { - /// Create a new instance. - pub fn new(value: u64) -> Self { - VhostUserU64 { value } - } -} - -impl VhostUserMsgValidator for VhostUserU64 {} - -/// Memory region descriptor for the SET_MEM_TABLE request. -#[repr(packed)] -#[derive(Default)] -pub struct VhostUserMemory { - /// Number of memory regions in the payload. - pub num_regions: u32, - /// Padding for alignment. - pub padding1: u32, -} - -impl VhostUserMemory { - /// Create a new instance. - 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 - } -} - -/// Memory region descriptors as payload for the SET_MEM_TABLE request. -#[repr(packed)] -#[derive(Default, Clone, Copy)] -pub struct VhostUserMemoryRegion { - /// Guest physical address of the memory region. - pub guest_phys_addr: u64, - /// Size of the memory region. - pub memory_size: u64, - /// Virtual address in the current process. - pub user_addr: u64, - /// Offset where region starts in the mapped memory. - pub mmap_offset: u64, -} - -impl VhostUserMemoryRegion { - /// Create a new instance. - 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 - } -} - -/// Payload of the VhostUserMemory message. -pub type VhostUserMemoryPayload = Vec<VhostUserMemoryRegion>; - -/// Single memory region descriptor as payload for ADD_MEM_REG and REM_MEM_REG -/// requests. -#[repr(C)] -#[derive(Default, Clone, Copy)] -pub struct VhostUserSingleMemoryRegion { - /// Padding for correct alignment - padding: u64, - /// Guest physical address of the memory region. - pub guest_phys_addr: u64, - /// Size of the memory region. - pub memory_size: u64, - /// Virtual address in the current process. - pub user_addr: u64, - /// Offset where region starts in the mapped memory. - pub mmap_offset: u64, -} - -impl VhostUserSingleMemoryRegion { - /// Create a new instance. - 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 - } -} - -/// Vring state descriptor. -#[repr(packed)] -#[derive(Default)] -pub struct VhostUserVringState { - /// Vring index. - pub index: u32, - /// A common 32bit value to encapsulate vring state etc. - pub num: u32, -} - -impl VhostUserVringState { - /// Create a new instance. - pub fn new(index: u32, num: u32) -> Self { - VhostUserVringState { index, num } - } -} - -impl VhostUserMsgValidator for VhostUserVringState {} - -// Bit mask for vring address flags. -bitflags! { - /// Flags for vring address. - pub struct VhostUserVringAddrFlags: u32 { - /// Support log of vring operations. - /// Modifications to "used" vring should be logged. - const VHOST_VRING_F_LOG = 0x1; - } -} - -/// Vring address descriptor. -#[repr(packed)] -#[derive(Default)] -pub struct VhostUserVringAddr { - /// Vring index. - pub index: u32, - /// Vring flags defined by VhostUserVringAddrFlags. - pub flags: u32, - /// Ring address of the vring descriptor table. - pub descriptor: u64, - /// Ring address of the vring used ring. - pub used: u64, - /// Ring address of the vring available ring. - pub available: u64, - /// Guest address for logging. - pub log: u64, -} - -impl VhostUserVringAddr { - /// Create a new instance. - 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, - } - } - - /// Create a new instance from `VringConfigData`. - #[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 - } -} - -// Bit mask for the vhost-user device configuration message. -bitflags! { - /// Flags for the device configuration message. - pub struct VhostUserConfigFlags: u32 { - /// Vhost master messages used for writeable fields. - const WRITABLE = 0x1; - /// Vhost master messages used for live migration. - const LIVE_MIGRATION = 0x2; - } -} - -/// Message to read/write device configuration space. -#[repr(packed)] -#[derive(Default)] -pub struct VhostUserConfig { - /// Offset of virtio device's configuration space. - pub offset: u32, - /// Configuration space access size in bytes. - pub size: u32, - /// Flags for the device configuration operation. - pub flags: u32, -} - -impl VhostUserConfig { - /// Create a new instance. - pub fn new(offset: u32, size: u32, flags: VhostUserConfigFlags) -> Self { - VhostUserConfig { - offset, - size, - flags: flags.bits(), - } - } -} - -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 - } -} - -/// Payload for the VhostUserConfig message. -pub type VhostUserConfigPayload = Vec<u8>; - -/// Single memory region descriptor as payload for ADD_MEM_REG and REM_MEM_REG -/// requests. -#[repr(C)] -#[derive(Default, Clone)] -pub struct VhostUserInflight { - /// Size of the area to track inflight I/O. - pub mmap_size: u64, - /// Offset of this area from the start of the supplied file descriptor. - pub mmap_offset: u64, - /// Number of virtqueues. - pub num_queues: u16, - /// Size of virtqueues. - pub queue_size: u16, -} - -impl VhostUserInflight { - /// Create a new instance. - 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, - } - } -} - -impl VhostUserMsgValidator for VhostUserInflight { - fn is_valid(&self) -> bool { - if self.num_queues == 0 || self.queue_size == 0 { - return false; - } - true - } -} - -/* - * TODO: support dirty log, live migration and IOTLB operations. -#[repr(packed)] -pub struct VhostUserVringArea { - pub index: u32, - pub flags: u32, - pub size: u64, - pub offset: u64, -} - -#[repr(packed)] -pub struct VhostUserLog { - pub size: u64, - pub offset: u64, -} - -#[repr(packed)] -pub struct VhostUserIotlb { - pub iova: u64, - pub size: u64, - pub user_addr: u64, - pub permission: u8, - pub optype: u8, -} -*/ - -// Bit mask for flags in virtio-fs slave messages -bitflags! { - #[derive(Default)] - /// Flags for virtio-fs slave messages. - pub struct VhostUserFSSlaveMsgFlags: u64 { - /// Empty permission. - const EMPTY = 0x0; - /// Read permission. - const MAP_R = 0x1; - /// Write permission. - const MAP_W = 0x2; - } -} - -/// Max entries in one virtio-fs slave request. -pub const VHOST_USER_FS_SLAVE_ENTRIES: usize = 8; - -/// Slave request message to update the MMIO window. -#[repr(packed)] -#[derive(Default)] -pub struct VhostUserFSSlaveMsg { - /// File offset. - pub fd_offset: [u64; VHOST_USER_FS_SLAVE_ENTRIES], - /// Offset into the DAX window. - pub cache_offset: [u64; VHOST_USER_FS_SLAVE_ENTRIES], - /// Size of region to map. - pub len: [u64; VHOST_USER_FS_SLAVE_ENTRIES], - /// Flags for the mmap operation - 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 - } -} - -/// Inflight I/O descriptor state for split virtqueues -#[repr(packed)] -#[derive(Clone, Copy, Default)] -pub struct DescStateSplit { - /// Indicate whether this descriptor (only head) is inflight or not. - pub inflight: u8, - /// Padding - padding: [u8; 5], - /// List of last batch of used descriptors, only when batching is used for submitting - pub next: u16, - /// Preserve order of fetching available descriptors, only for head descriptor - pub counter: u64, -} - -impl DescStateSplit { - /// New instance of DescStateSplit struct - pub fn new() -> Self { - Self::default() - } -} - -/// Inflight I/O queue region for split virtqueues -#[repr(packed)] -pub struct QueueRegionSplit { - /// Features flags of this region - pub features: u64, - /// Version of this region - pub version: u16, - /// Number of DescStateSplit entries - pub desc_num: u16, - /// List to track last batch of used descriptors - pub last_batch_head: u16, - /// Idx value of used ring - pub used_idx: u16, - /// Pointer to an array of DescStateSplit entries - pub desc: u64, -} - -impl QueueRegionSplit { - /// New instance of QueueRegionSplit struct - 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, - } - } -} - -/// Inflight I/O descriptor state for packed virtqueues -#[repr(packed)] -#[derive(Clone, Copy, Default)] -pub struct DescStatePacked { - /// Indicate whether this descriptor (only head) is inflight or not. - pub inflight: u8, - /// Padding - padding: u8, - /// Link to next free entry - pub next: u16, - /// Link to last entry of descriptor list, only for head - pub last: u16, - /// Length of descriptor list, only for head - pub num: u16, - /// Preserve order of fetching avail descriptors, only for head - pub counter: u64, - /// Buffer ID - pub id: u16, - /// Descriptor flags - pub flags: u16, - /// Buffer length - pub len: u32, - /// Buffer address - pub addr: u64, -} - -impl DescStatePacked { - /// New instance of DescStatePacked struct - pub fn new() -> Self { - Self::default() - } -} - -/// Inflight I/O queue region for packed virtqueues -#[repr(packed)] -pub struct QueueRegionPacked { - /// Features flags of this region - pub features: u64, - /// version of this region - pub version: u16, - /// size of descriptor state array - pub desc_num: u16, - /// head of free DescStatePacked entry list - pub free_head: u16, - /// old head of free DescStatePacked entry list - pub old_free_head: u16, - /// used idx of descriptor ring - pub used_idx: u16, - /// old used idx of descriptor ring - pub old_used_idx: u16, - /// device ring wrap counter - pub used_wrap_counter: u8, - /// old device ring wrap counter - pub old_used_wrap_counter: u8, - /// Padding - padding: [u8; 7], - /// Pointer to array tracking state of each descriptor from descriptor ring - pub desc: u64, -} - -impl QueueRegionPacked { - /// New instance of QueueRegionPacked struct - 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_eq!(hdr.is_reply(), false); - hdr.set_reply(true); - assert_eq!(hdr.is_reply(), true); - hdr.set_reply(false); - - assert_eq!(hdr.is_need_reply(), false); - hdr.set_need_reply(true); - assert_eq!(hdr.is_need_reply(), true); - hdr.set_need_reply(false); - - assert_eq!(hdr.get_size(), 0x100); - hdr.set_size(0x200); - assert_eq!(hdr.get_size(), 0x200); - - assert_eq!(hdr.is_need_reply(), false); - assert_eq!(hdr.is_reply(), false); - assert_eq!(hdr.get_version(), 0x1); - - // Check message length - 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()); - - // Check version - hdr.set_version(0x0); - assert!(!hdr.is_valid()); - hdr.set_version(0x2); - assert!(!hdr.is_valid()); - hdr.set_version(0x1); - assert!(hdr.is_valid()); - - // Test Debug, Clone, PartiaEq trait - 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_eq!(state.is_valid(), true); - - let state = VhostUserVringState::default(); - let a = state.index; - assert_eq!(a, 0); - let a = state.num; - assert_eq!(a, 0); - assert_eq!(state.is_valid(), true); - } - - #[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_eq!(addr.is_valid(), true); - - addr.descriptor = 0x1001; - assert_eq!(addr.is_valid(), false); - addr.descriptor = 0x1000; - - addr.available = 0x3001; - assert_eq!(addr.is_valid(), false); - addr.available = 0x3000; - - addr.used = 0x2001; - assert_eq!(addr.is_valid(), false); - addr.used = 0x2000; - assert_eq!(addr.is_valid(), true); - } - - #[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_eq!(addr.is_valid(), true); - } - - #[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_eq!(fs_slave.is_valid(), true); - - fs_slave.fd_offset[0] = 0xffff_ffff_ffff_ffff; - fs_slave.len[0] = 0x1; - assert_eq!(fs_slave.is_valid(), false); - - assert_ne!( - VhostUserFSSlaveMsgFlags::MAP_R, - VhostUserFSSlaveMsgFlags::MAP_W - ); - assert_eq!(VhostUserFSSlaveMsgFlags::EMPTY.bits(), 0); - } -} |