Слияние кода завершено, страница обновится автоматически
// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//! # Virtio
//!
//! This mod is used for virtio device.
//!
//! ## Design
//!
//! This module offers support for:
//! 1. Some Spec specified const variable used by virtio device.
//! 2. Virtio Device trait
//!
//! ## Platform Support
//!
//! - `x86_64`
//! - `aarch64`
pub mod device;
pub mod error;
pub mod vhost;
mod queue;
mod transport;
pub use device::balloon::*;
pub use device::block::{Block, BlockState, VirtioBlkConfig};
#[cfg(feature = "virtio_gpu")]
pub use device::gpu::*;
pub use device::net::*;
pub use device::rng::{Rng, RngState};
pub use device::scsi_cntlr as ScsiCntlr;
pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState};
pub use error::VirtioError;
pub use queue::*;
pub use transport::virtio_mmio::{VirtioMmioDevice, VirtioMmioState};
pub use transport::virtio_pci::VirtioPciDevice;
pub use vhost::kernel as VhostKern;
pub use vhost::user as VhostUser;
use std::cmp;
use std::io::Write;
use std::os::unix::prelude::RawFd;
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU8, Ordering};
use std::sync::{Arc, Mutex};
use anyhow::{anyhow, bail, Context, Result};
use log::{error, warn};
use vmm_sys_util::eventfd::EventFd;
use address_space::AddressSpace;
use machine_manager::config::ConfigCheck;
use migration_derive::ByteCode;
use util::aio::{mem_to_buf, Iovec};
use util::num_ops::{read_u32, write_u32};
use util::AsAny;
/// Check if the bit of features is configured.
pub fn virtio_has_feature(feature: u64, fbit: u32) -> bool {
feature & (1 << fbit) != 0
}
/// Identifier of different virtio device, refer to Virtio Spec.
pub const VIRTIO_TYPE_NET: u32 = 1;
pub const VIRTIO_TYPE_BLOCK: u32 = 2;
pub const VIRTIO_TYPE_CONSOLE: u32 = 3;
pub const VIRTIO_TYPE_RNG: u32 = 4;
pub const VIRTIO_TYPE_BALLOON: u32 = 5;
pub const VIRTIO_TYPE_SCSI: u32 = 8;
pub const VIRTIO_TYPE_GPU: u32 = 16;
pub const VIRTIO_TYPE_VSOCK: u32 = 19;
pub const VIRTIO_TYPE_FS: u32 = 26;
// The Status of Virtio Device.
const CONFIG_STATUS_ACKNOWLEDGE: u32 = 0x01;
const CONFIG_STATUS_DRIVER: u32 = 0x02;
const CONFIG_STATUS_DRIVER_OK: u32 = 0x04;
const CONFIG_STATUS_FEATURES_OK: u32 = 0x08;
const CONFIG_STATUS_NEEDS_RESET: u32 = 0x40;
const CONFIG_STATUS_FAILED: u32 = 0x80;
/// Feature Bits, refer to Virtio Spec.
/// Negotiating this feature indicates that the driver can use descriptors
/// with the VIRTQ_DESC_F_INDIRECT flag set.
pub const VIRTIO_F_RING_INDIRECT_DESC: u32 = 28;
/// This feature enables the used_event and the avail_event fields.
pub const VIRTIO_F_RING_EVENT_IDX: u32 = 29;
/// Indicates compliance with Virtio Spec.
pub const VIRTIO_F_VERSION_1: u32 = 32;
/// This feature indicates that the device can be used on a platform
/// where device access to data in memory is limited and/or translated.
pub const VIRTIO_F_ACCESS_PLATFORM: u32 = 33;
/// This feature indicates support for the packed virtqueue layout.
pub const VIRTIO_F_RING_PACKED: u32 = 34;
/// Device handles packets with partial checksum.
pub const VIRTIO_NET_F_CSUM: u32 = 0;
/// Driver handles packets with partial checksum.
pub const VIRTIO_NET_F_GUEST_CSUM: u32 = 1;
/// Device has given MAC address.
pub const VIRTIO_NET_F_MAC: u32 = 5;
/// Driver can receive TSOv4.
pub const VIRTIO_NET_F_GUEST_TSO4: u32 = 7;
/// Driver can receive TSOv6.
pub const VIRTIO_NET_F_GUEST_TSO6: u32 = 8;
/// Driver can receive TSO with ECN.
pub const VIRTIO_NET_F_GUEST_ECN: u32 = 9;
/// Driver can receive UFO.
pub const VIRTIO_NET_F_GUEST_UFO: u32 = 10;
/// Device can receive TSOv4.
pub const VIRTIO_NET_F_HOST_TSO4: u32 = 11;
/// Device can receive TSOv6.
pub const VIRTIO_NET_F_HOST_TSO6: u32 = 12;
/// Device can receive UFO.
pub const VIRTIO_NET_F_HOST_UFO: u32 = 14;
/// Device can merge receive buffers.
pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15;
/// Control channel is available.
pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17;
/// Control channel RX mode support.
pub const VIRTIO_NET_F_CTRL_RX: u32 = 18;
/// Control channel VLAN filtering.
pub const VIRTIO_NET_F_CTRL_VLAN: u32 = 19;
/// Extra RX mode control support.
pub const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20;
/// Device supports multi queue with automatic receive steering.
pub const VIRTIO_NET_F_MQ: u32 = 22;
/// Set Mac Address through control channel.
pub const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23;
/// Configuration cols and rows are valid.
pub const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
/// Device has support for multiple ports.
/// max_nr_ports is valid and control virtqueues will be used.
pub const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 1;
/// Device has support for emergency write.
/// Configuration field emerg_wr is valid.
pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 2;
/// Maximum size of any single segment is in size_max.
pub const VIRTIO_BLK_F_SIZE_MAX: u32 = 1;
/// Maximum number of segments in a request is in seg_max.
pub const VIRTIO_BLK_F_SEG_MAX: u32 = 2;
/// Legacy geometry available.
pub const VIRTIO_BLK_F_GEOMETRY: u32 = 4;
/// Device is read-only.
pub const VIRTIO_BLK_F_RO: u32 = 5;
/// Block size of disk is available.
pub const VIRTIO_BLK_F_BLK_SIZE: u32 = 6;
/// Cache flush command support.
pub const VIRTIO_BLK_F_FLUSH: u32 = 9;
/// Topology information is available.
pub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10;
/// DISCARD is supported.
pub const VIRTIO_BLK_F_DISCARD: u32 = 13;
/// WRITE ZEROES is supported.
pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14;
/// Unmap flags for write zeroes command.
pub const VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP: u32 = 1;
/// GPU EDID feature is supported.
pub const VIRTIO_GPU_F_EDID: u32 = 1;
/// TODO: need to change to 5 or bigger
pub const VIRTIO_GPU_F_MONOCHROME: u32 = 4;
/// The device sets control ok status to driver.
pub const VIRTIO_NET_OK: u8 = 0;
/// The device sets control err status to driver.
pub const VIRTIO_NET_ERR: u8 = 1;
/// Driver can send control commands.
pub const VIRTIO_NET_CTRL_RX: u8 = 0;
/// Control commands for promiscuous mode.
pub const VIRTIO_NET_CTRL_RX_PROMISC: u8 = 0;
/// Control commands for all-multicast receive.
pub const VIRTIO_NET_CTRL_RX_ALLMULTI: u8 = 1;
/// Control commands for all-unicast receive.
pub const VIRTIO_NET_CTRL_RX_ALLUNI: u8 = 2;
/// Control commands for suppressing multicast receive.
pub const VIRTIO_NET_CTRL_RX_NOMULTI: u8 = 3;
/// Control commands for suppressing unicast receive.
pub const VIRTIO_NET_CTRL_RX_NOUNI: u8 = 4;
/// Control commands for suppressing broadcast receive.
pub const VIRTIO_NET_CTRL_RX_NOBCAST: u8 = 5;
/// The driver can send control commands for MAC address filtering.
pub const VIRTIO_NET_CTRL_MAC: u8 = 1;
/// The driver sets the unicast/multicast address table.
pub const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0;
/// The driver sets the default MAC address which rx filtering accepts.
pub const VIRTIO_NET_CTRL_MAC_ADDR_SET: u8 = 1;
/// The driver can send control commands for vlan filtering.
pub const VIRTIO_NET_CTRL_VLAN: u8 = 2;
/// The driver adds a vlan id to the vlan filtering table.
pub const VIRTIO_NET_CTRL_VLAN_ADD: u8 = 0;
/// The driver adds a vlan id from the vlan filtering table.
pub const VIRTIO_NET_CTRL_VLAN_DEL: u8 = 1;
/// Driver configure the class before enabling virtqueue.
pub const VIRTIO_NET_CTRL_MQ: u8 = 4;
/// Driver configure the command before enabling virtqueue.
pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0;
/// The minimum pairs of multiple queue.
pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: u16 = 1;
/// The maximum pairs of multiple queue.
pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000;
/// Support more than one virtqueue.
pub const VIRTIO_BLK_F_MQ: u32 = 12;
/// A single request can include both device-readable and device-writable data buffers.
pub const VIRTIO_SCSI_F_INOUT: u32 = 0;
/// The host SHOULD enable reporting of hot-plug and hot-unplug events for LUNs and targets on the
/// SCSI bus. The guest SHOULD handle hot-plug and hot-unplug events.
pub const VIRTIO_SCSI_F_HOTPLUG: u32 = 1;
/// The host will report changes to LUN parameters via a VIRTIO_SCSI_T_PARAM_CHANGE event.
/// The guest SHOULD handle them.
pub const VIRTIO_SCSI_F_CHANGE: u32 = 2;
/// The extended fields for T10 protection information (DIF/DIX) are included in the SCSI request
/// header.
pub const VIRTIO_SCSI_F_T10_PI: u32 = 3;
/// The IO type of virtio block, refer to Virtio Spec.
/// Read.
pub const VIRTIO_BLK_T_IN: u32 = 0;
/// Write.
pub const VIRTIO_BLK_T_OUT: u32 = 1;
/// Flush.
pub const VIRTIO_BLK_T_FLUSH: u32 = 4;
/// Device id
pub const VIRTIO_BLK_T_GET_ID: u32 = 8;
/// Discard command.
pub const VIRTIO_BLK_T_DISCARD: u32 = 11;
/// Write zeroes command.
pub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13;
/// Device id length
pub const VIRTIO_BLK_ID_BYTES: u32 = 20;
/// Success
pub const VIRTIO_BLK_S_OK: u8 = 0;
/// IO Error.
pub const VIRTIO_BLK_S_IOERR: u8 = 1;
/// Unsupported.
pub const VIRTIO_BLK_S_UNSUPP: u8 = 2;
/// The Type of virtio gpu, refer to Virtio Spec.
/// 2D commands:
/// Retrieve the current output configuration.
pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
/// Create a 2D resource on the host.
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101;
/// Destroy a resource on the host.
pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102;
/// Set the scanout parameters for a single output.
pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103;
/// Flush a scanout resource.
pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104;
/// Transfer from guest memory to host resource.
pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105;
/// Assign backing pages to a resource.
pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106;
/// Detach backing pages from a resource.
pub const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107;
/// Retrieve the EDID data for a given scanout.
pub const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x010a;
/// update cursor
pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300;
/// move cursor
pub const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301;
/// Success for cmd without data back.
pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
/// Success for VIRTIO_GPU_CMD_GET_DISPLAY_INFO.
pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
/// Success for VIRTIO_GPU_CMD_GET_EDID.
pub const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104;
/// unspecificated
pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
/// out of host memory
pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
/// invalid id of scanout
pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
/// invalid id of 2D resource
pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
/// invalid parameter
pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
/// Flags in virtio gpu cmd which means need a fence.
pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0;
/// Interrupt status: Used Buffer Notification
pub const VIRTIO_MMIO_INT_VRING: u32 = 0x01;
/// Interrupt status: Configuration Change Notification
pub const VIRTIO_MMIO_INT_CONFIG: u32 = 0x02;
/// The offset between notify reg's address and base MMIO address
/// Guest OS uses notify reg to notify the VMM.
pub const NOTIFY_REG_OFFSET: u32 = 0x50;
/// Packet header, refer to Virtio Spec.
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct VirtioNetHdr {
pub flags: u8,
pub gso_type: u8,
pub hdr_len: u16,
pub gso_size: u16,
pub csum_start: u16,
pub csum_offset: u16,
pub num_buffers: u16,
}
#[derive(Debug)]
pub enum VirtioInterruptType {
Config,
Vring,
}
pub type VirtioInterrupt =
Box<dyn Fn(&VirtioInterruptType, Option<&Queue>, bool) -> Result<()> + Send + Sync>;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum VirtioDeviceQuirk {
VirtioGpuEnableBar0,
VirtioDeviceQuirkMax,
}
#[derive(Default)]
pub struct VirtioBase {
/// Device type
device_type: u32,
/// Bit mask of features supported by the backend.
device_features: u64,
/// Bit mask of features negotiated by the backend and the frontend.
driver_features: u64,
/// Device (host) feature-setting selector.
hfeatures_sel: u32,
/// Driver (guest) feature-setting selector.
gfeatures_sel: u32,
/// Interrupt status.
interrupt_status: Arc<AtomicU32>,
/// Device status.
device_status: Arc<AtomicU32>,
/// If this device is activated or not.
device_activated: Arc<AtomicBool>,
/// Configuration atomicity value.
config_generation: Arc<AtomicU8>,
/// The MSI-X vector for config change notification.
config_vector: Arc<AtomicU16>,
/// The type of queue, split-vring or packed-vring.
queue_type: u16,
/// The number of device queues.
queue_num: usize,
/// The max size of each queue.
queue_size_max: u16,
/// Queue selector.
queue_select: u16,
/// The configuration of queues.
queues_config: Vec<QueueConfig>,
/// Virtio queues.
queues: Vec<Arc<Mutex<Queue>>>,
/// Eventfd for device deactivate.
deactivate_evts: Vec<RawFd>,
/// Device is broken or not.
broken: Arc<AtomicBool>,
}
#[derive(Copy, Clone, ByteCode)]
struct VirtioBaseState {
device_activated: bool,
hfeatures_sel: u32,
gfeatures_sel: u32,
interrupt_status: u32,
device_status: u32,
config_generation: u8,
queue_select: u16,
config_vector: u16,
queues_config: [QueueConfig; 32],
/// The number of activated queues.
queue_num: usize,
queue_type: u16,
}
impl VirtioBase {
fn new(device_type: u32, queue_num: usize, queue_size_max: u16) -> Self {
Self {
device_type,
config_vector: Arc::new(AtomicU16::new(INVALID_VECTOR_NUM)),
queue_num,
queue_size_max,
queue_type: QUEUE_TYPE_SPLIT_VRING,
queues_config: vec![QueueConfig::new(queue_size_max); queue_num],
..Default::default()
}
}
fn reset(&mut self) {
// device_type, device_features, queue_num and queue_size_max
// is not mutable, thus no need to reset.
self.driver_features = 0;
self.hfeatures_sel = 0;
self.gfeatures_sel = 0;
self.interrupt_status.store(0, Ordering::SeqCst);
self.device_status.store(0, Ordering::SeqCst);
self.device_activated.store(false, Ordering::SeqCst);
self.config_generation.store(0, Ordering::SeqCst);
self.config_vector
.store(INVALID_VECTOR_NUM, Ordering::SeqCst);
self.queue_type = QUEUE_TYPE_SPLIT_VRING;
self.queue_select = 0;
self.queues_config.iter_mut().for_each(|q| q.reset());
self.queues.clear();
self.broken.store(false, Ordering::SeqCst);
}
fn get_state(&self) -> VirtioBaseState {
let mut state = VirtioBaseState {
device_activated: self.device_activated.load(Ordering::Acquire),
hfeatures_sel: self.hfeatures_sel,
gfeatures_sel: self.gfeatures_sel,
interrupt_status: self.interrupt_status.load(Ordering::Acquire),
device_status: self.device_status.load(Ordering::Acquire),
config_generation: self.config_generation.load(Ordering::Acquire),
queue_select: self.queue_select,
config_vector: self.config_vector.load(Ordering::Acquire),
queues_config: [QueueConfig::default(); 32],
queue_num: 0,
queue_type: self.queue_type,
};
for (index, queue) in self.queues_config.iter().enumerate() {
state.queues_config[index] = *queue;
}
for (index, queue) in self.queues.iter().enumerate() {
state.queues_config[index] = queue.lock().unwrap().vring.get_queue_config();
state.queue_num += 1;
}
state
}
fn set_state(
&mut self,
state: &VirtioBaseState,
mem_space: Arc<AddressSpace>,
interrupt_cb: Arc<VirtioInterrupt>,
) {
self.device_activated
.store(state.device_activated, Ordering::SeqCst);
self.hfeatures_sel = state.hfeatures_sel;
self.gfeatures_sel = state.gfeatures_sel;
self.interrupt_status
.store(state.interrupt_status, Ordering::SeqCst);
self.device_status
.store(state.device_status, Ordering::SeqCst);
self.config_generation
.store(state.config_generation, Ordering::SeqCst);
self.queue_select = state.queue_select;
self.config_vector
.store(state.config_vector, Ordering::SeqCst);
self.queues_config = state.queues_config[..self.queue_num].to_vec();
self.queue_type = state.queue_type;
if state.queue_num == 0 {
return;
}
let mut queues = Vec::with_capacity(self.queue_num);
for queue_config in self.queues_config.iter_mut().take(state.queue_num) {
if queue_config.ready {
queue_config.set_addr_cache(
mem_space.clone(),
interrupt_cb.clone(),
self.driver_features,
&self.broken,
);
}
queues.push(Arc::new(Mutex::new(
Queue::new(*queue_config, self.queue_type).unwrap(),
)));
}
self.queues = queues;
}
}
/// The trait for virtio device operations.
pub trait VirtioDevice: Send + AsAny {
/// Get base property of virtio device.
fn virtio_base(&self) -> &VirtioBase;
/// Get mutable base property virtio device.
fn virtio_base_mut(&mut self) -> &mut VirtioBase;
/// Realize low level device.
fn realize(&mut self) -> Result<()>;
/// Unrealize low level device.
fn unrealize(&mut self) -> Result<()> {
bail!("Unrealize of the virtio device is not implemented");
}
/// Get the virtio device type, refer to Virtio Spec.
fn device_type(&self) -> u32 {
self.virtio_base().device_type
}
/// Get the virtio device customized modification.
fn device_quirk(&self) -> Option<VirtioDeviceQuirk> {
None
}
/// Get the count of virtio device queues.
fn queue_num(&self) -> usize {
self.virtio_base().queue_num
}
/// Get the queue size of virtio device.
fn queue_size_max(&self) -> u16 {
self.virtio_base().queue_size_max
}
/// Init device configure space and features.
fn init_config_features(&mut self) -> Result<()>;
/// Get device features from host.
fn device_features(&self, features_select: u32) -> u32 {
read_u32(self.virtio_base().device_features, features_select)
}
/// Set driver features by guest.
fn set_driver_features(&mut self, page: u32, value: u32) {
let mut v = value;
let unsupported_features = value & !self.device_features(page);
if unsupported_features != 0 {
warn!(
"Receive acknowledge request with unknown feature: {:x}",
write_u32(value, page)
);
v &= !unsupported_features;
}
let features = if page == 0 {
(self.driver_features(1) as u64) << 32 | (v as u64)
} else {
(v as u64) << 32 | (self.driver_features(0) as u64)
};
self.virtio_base_mut().driver_features = features;
}
/// Get driver features by guest.
fn driver_features(&self, features_select: u32) -> u32 {
read_u32(self.virtio_base().driver_features, features_select)
}
/// Get host feature selector.
fn hfeatures_sel(&self) -> u32 {
self.virtio_base().hfeatures_sel
}
/// Set host feature selector.
fn set_hfeatures_sel(&mut self, val: u32) {
self.virtio_base_mut().hfeatures_sel = val;
}
/// Get guest feature selector.
fn gfeatures_sel(&self) -> u32 {
self.virtio_base().gfeatures_sel
}
/// Set guest feature selector.
fn set_gfeatures_sel(&mut self, val: u32) {
self.virtio_base_mut().gfeatures_sel = val;
}
/// Check whether virtio device status is as expected.
fn check_device_status(&self, set: u32, clr: u32) -> bool {
self.device_status() & (set | clr) == set
}
/// Get the status of virtio device.
fn device_status(&self) -> u32 {
self.virtio_base().device_status.load(Ordering::Acquire)
}
/// Set the status of virtio device.
fn set_device_status(&mut self, val: u32) {
self.virtio_base_mut()
.device_status
.store(val, Ordering::SeqCst)
}
/// Check device is activated or not.
fn device_activated(&self) -> bool {
self.virtio_base().device_activated.load(Ordering::Acquire)
}
/// Set device activate status.
fn set_device_activated(&mut self, val: bool) {
self.virtio_base_mut()
.device_activated
.store(val, Ordering::SeqCst)
}
/// Get config generation.
fn config_generation(&self) -> u8 {
self.virtio_base().config_generation.load(Ordering::Acquire)
}
/// Set config generation.
fn set_config_generation(&mut self, val: u8) {
self.virtio_base_mut()
.config_generation
.store(val, Ordering::SeqCst);
}
/// Get msix vector of config change interrupt.
fn config_vector(&self) -> u16 {
self.virtio_base().config_vector.load(Ordering::Acquire)
}
/// Set msix vector of config change interrupt.
fn set_config_vector(&mut self, val: u16) {
self.virtio_base_mut()
.config_vector
.store(val, Ordering::SeqCst);
}
/// Get virtqueue type.
fn queue_type(&self) -> u16 {
self.virtio_base().queue_type
}
/// Set virtqueue type.
fn set_queue_type(&mut self, val: u16) {
self.virtio_base_mut().queue_type = val;
}
/// Get virtqueue selector.
fn queue_select(&self) -> u16 {
self.virtio_base().queue_select
}
/// Set virtqueue selector.
fn set_queue_select(&mut self, val: u16) {
self.virtio_base_mut().queue_select = val;
}
/// Get virtqueue config.
fn queue_config(&self) -> Result<&QueueConfig> {
let queues_config = &self.virtio_base().queues_config;
let queue_select = self.virtio_base().queue_select;
queues_config
.get(queue_select as usize)
.with_context(|| "queue_select overflows")
}
/// Get mutable virtqueue config.
fn queue_config_mut(&mut self, need_check: bool) -> Result<&mut QueueConfig> {
if need_check
&& !self.check_device_status(
CONFIG_STATUS_FEATURES_OK,
CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FAILED,
)
{
return Err(anyhow!(VirtioError::DevStatErr(self.device_status())));
}
let queue_select = self.virtio_base().queue_select;
let queues_config = &mut self.virtio_base_mut().queues_config;
return queues_config
.get_mut(queue_select as usize)
.with_context(|| "queue_select overflows");
}
/// Get ISR register.
fn interrupt_status(&self) -> u32 {
self.virtio_base().interrupt_status.load(Ordering::Acquire)
}
/// Set ISR register.
fn set_interrupt_status(&mut self, val: u32) {
self.virtio_base_mut()
.interrupt_status
.store(val, Ordering::SeqCst)
}
/// Read data of config from guest.
fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()>;
/// Write data to config from guest.
fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()>;
/// Activate the virtio device, this function is called by vcpu thread when frontend
/// virtio driver is ready and write `DRIVER_OK` to backend.
///
/// # Arguments
///
/// * `mem_space` - System mem.
/// * `interrupt_cb` - The callback used to send interrupt to guest.
/// * `queues` - The virtio queues.
/// * `queue_evts` - The notifier events from guest.
fn activate(
&mut self,
mem_space: Arc<AddressSpace>,
interrupt_cb: Arc<VirtioInterrupt>,
queue_evts: Vec<Arc<EventFd>>,
) -> Result<()>;
/// Deactivate virtio device, this function remove event fd
/// of device out of the event loop.
fn deactivate(&mut self) -> Result<()> {
bail!(
"Reset this device is not supported, virtio dev type is {}",
self.device_type()
);
}
/// Reset virtio device, used to do some special reset action for
/// different device.
fn reset(&mut self) -> Result<()> {
Ok(())
}
/// Update the low level config of MMIO device,
/// for example: update the images file fd of virtio block device.
///
/// # Arguments
///
/// * `_file_path` - The related backend file path.
fn update_config(&mut self, _dev_config: Option<Arc<dyn ConfigCheck>>) -> Result<()> {
bail!("Unsupported to update configuration")
}
/// Set guest notifiers for notifying the guest.
///
/// # Arguments
///
/// * `_queue_evts` - The notifier events from host.
fn set_guest_notifiers(&mut self, _queue_evts: &[Arc<EventFd>]) -> Result<()> {
Ok(())
}
/// Get whether the virtio device has a control queue,
/// devices with a control queue should override this function.
fn has_control_queue(&self) -> bool {
false
}
}
/// Check boundary for config space rw.
fn check_config_space_rw(config: &[u8], offset: u64, data: &[u8]) -> Result<()> {
let config_len = config.len() as u64;
let data_len = data.len() as u64;
offset
.checked_add(data_len)
.filter(|&end| end <= config_len)
.with_context(|| VirtioError::DevConfigOverflow(offset, data_len, config_len))?;
Ok(())
}
/// Default implementation for config space read.
fn read_config_default(config: &[u8], offset: u64, mut data: &mut [u8]) -> Result<()> {
check_config_space_rw(config, offset, data)?;
let read_end = offset as usize + data.len();
data.write_all(&config[offset as usize..read_end])?;
Ok(())
}
/// The function used to inject interrupt to guest when encounter an virtio error.
pub fn report_virtio_error(
interrupt_cb: Arc<VirtioInterrupt>,
features: u64,
broken: &Arc<AtomicBool>,
) {
if virtio_has_feature(features, VIRTIO_F_VERSION_1) {
interrupt_cb(&VirtioInterruptType::Config, None, true).unwrap_or_else(|e| {
error!(
"Failed to trigger interrupt for virtio error, error is {:?}",
e
)
});
}
// The device should not work when meeting virtio error.
broken.store(true, Ordering::SeqCst);
}
/// Read iovec to buf and return the read number of bytes.
pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) -> Result<usize> {
let mut start: usize = 0;
let mut end: usize = 0;
for iov in iovec {
let mut addr_map = Vec::new();
mem_space.get_address_map(iov.addr, iov.len as u64, &mut addr_map)?;
for addr in addr_map.into_iter() {
end = cmp::min(start + addr.iov_len as usize, buf.len());
mem_to_buf(&mut buf[start..end], addr.iov_base)?;
if end >= buf.len() {
return Ok(end);
}
start = end;
}
}
Ok(end)
}
/// Discard "size" bytes of the front of iovec.
pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> {
for (index, iov) in iovec.iter_mut().enumerate() {
if iov.len as u64 > size {
iov.addr.0 += size;
iov.len -= size as u32;
return Some(&mut iovec[index..]);
}
size -= iov.len as u64;
}
None
}
/// Discard "size" bytes of the back of iovec.
pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> {
let len = iovec.len();
for (index, iov) in iovec.iter_mut().rev().enumerate() {
if iov.len as u64 > size {
iov.len -= size as u32;
return Some(&mut iovec[..(len - index)]);
}
size -= iov.len as u64;
}
None
}
/// Convert GPA buffer iovec to HVA buffer iovec.
/// If don't need the entire iovec, use iov_discard_front/iov_discard_back firstly.
fn gpa_hva_iovec_map(
gpa_elemiovec: &[ElemIovec],
mem_space: &AddressSpace,
) -> Result<(u64, Vec<Iovec>)> {
let mut iov_size = 0;
let mut hva_iovec = Vec::with_capacity(gpa_elemiovec.len());
for elem in gpa_elemiovec.iter() {
mem_space.get_address_map(elem.addr, elem.len as u64, &mut hva_iovec)?;
iov_size += elem.len as u64;
}
Ok((iov_size, hva_iovec))
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарий ( 0 )