#[cfg(test)]
pub mod fake;
pub mod mmio;
pub mod pci;
mod some;
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
use crate::{PAGE_SIZE, PhysAddr, Result};
use bitflags::{Flags, bitflags};
use core::{
fmt::{self, Debug, Formatter},
ops::BitAnd,
};
use log::debug;
pub use some::SomeTransport;
use thiserror::Error;
use zerocopy::{FromBytes, Immutable, IntoBytes};
pub trait Transport {
fn device_type(&self) -> DeviceType;
fn read_device_features(&mut self) -> u64;
fn write_driver_features(&mut self, driver_features: u64);
fn max_queue_size(&mut self, queue: u16) -> u32;
fn notify(&mut self, queue: u16);
fn get_status(&self) -> DeviceStatus;
fn set_status(&mut self, status: DeviceStatus);
fn set_guest_page_size(&mut self, guest_page_size: u32);
fn requires_legacy_layout(&self) -> bool;
fn queue_set(
&mut self,
queue: u16,
size: u32,
descriptors: PhysAddr,
driver_area: PhysAddr,
device_area: PhysAddr,
);
fn queue_unset(&mut self, queue: u16);
fn queue_used(&mut self, queue: u16) -> bool;
fn ack_interrupt(&mut self) -> InterruptStatus;
fn begin_init<F: Flags<Bits = u64> + BitAnd<Output = F> + Debug>(
&mut self,
supported_features: F,
) -> F {
self.set_status(DeviceStatus::empty());
self.set_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER);
let device_feature_bits = self.read_device_features();
let device_features = F::from_bits_truncate(device_feature_bits);
debug!("Device features: {:?}", device_features);
let negotiated_features = device_features & supported_features;
if cfg!(debug_assertions) {
use crate::device::common::Feature;
if device_feature_bits & Feature::VERSION_1.bits() > 0 {
debug_assert!(
negotiated_features.bits() & Feature::VERSION_1.bits() > 0,
"Driver must accept VIRTIO_F_VERSION_1 in supported features because it is offered by the device."
);
}
}
self.write_driver_features(negotiated_features.bits());
self.set_status(
DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK,
);
self.set_guest_page_size(PAGE_SIZE as u32);
negotiated_features
}
fn finish_init(&mut self) {
self.set_status(
DeviceStatus::ACKNOWLEDGE
| DeviceStatus::DRIVER
| DeviceStatus::FEATURES_OK
| DeviceStatus::DRIVER_OK,
);
}
fn read_config_generation(&self) -> u32;
fn read_config_space<T: FromBytes + IntoBytes>(&self, offset: usize) -> Result<T>;
fn write_config_space<T: IntoBytes + Immutable>(
&mut self,
offset: usize,
value: T,
) -> Result<()>;
fn read_consistent<T>(&self, f: impl Fn() -> Result<T>) -> Result<T> {
loop {
let before = self.read_config_generation();
let result = f();
let after = self.read_config_generation();
if before == after {
break result;
}
}
}
}
#[derive(Copy, Clone, Default, Eq, FromBytes, Immutable, IntoBytes, PartialEq)]
pub struct DeviceStatus(u32);
impl Debug for DeviceStatus {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "DeviceStatus(")?;
bitflags::parser::to_writer(self, &mut *f)?;
write!(f, ")")?;
Ok(())
}
}
bitflags! {
impl DeviceStatus: u32 {
const ACKNOWLEDGE = 1;
const DRIVER = 2;
const FAILED = 128;
const FEATURES_OK = 8;
const DRIVER_OK = 4;
const DEVICE_NEEDS_RESET = 64;
}
}
#[derive(Copy, Clone, Default, Eq, FromBytes, PartialEq)]
pub struct InterruptStatus(u32);
bitflags! {
impl InterruptStatus: u32 {
const QUEUE_INTERRUPT = 1 << 0;
const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1;
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[allow(missing_docs)]
pub enum DeviceType {
Network = 1,
Block = 2,
Console = 3,
EntropySource = 4,
MemoryBallooning = 5,
IoMemory = 6,
Rpmsg = 7,
ScsiHost = 8,
_9P = 9,
Mac80211 = 10,
RprocSerial = 11,
VirtioCAIF = 12,
MemoryBalloon = 13,
GPU = 16,
Timer = 17,
Input = 18,
Socket = 19,
Crypto = 20,
SignalDistributionModule = 21,
Pstore = 22,
IOMMU = 23,
Memory = 24,
Sound = 25,
}
#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
pub enum DeviceTypeError {
#[error("Invalid or unknown virtio device type {0}")]
InvalidDeviceType(u32),
}
impl TryFrom<u32> for DeviceType {
type Error = DeviceTypeError;
fn try_from(virtio_device_id: u32) -> core::result::Result<Self, Self::Error> {
match virtio_device_id {
1 => Ok(DeviceType::Network),
2 => Ok(DeviceType::Block),
3 => Ok(DeviceType::Console),
4 => Ok(DeviceType::EntropySource),
5 => Ok(DeviceType::MemoryBalloon),
6 => Ok(DeviceType::IoMemory),
7 => Ok(DeviceType::Rpmsg),
8 => Ok(DeviceType::ScsiHost),
9 => Ok(DeviceType::_9P),
10 => Ok(DeviceType::Mac80211),
11 => Ok(DeviceType::RprocSerial),
12 => Ok(DeviceType::VirtioCAIF),
13 => Ok(DeviceType::MemoryBalloon),
16 => Ok(DeviceType::GPU),
17 => Ok(DeviceType::Timer),
18 => Ok(DeviceType::Input),
19 => Ok(DeviceType::Socket),
20 => Ok(DeviceType::Crypto),
21 => Ok(DeviceType::SignalDistributionModule),
22 => Ok(DeviceType::Pstore),
23 => Ok(DeviceType::IOMMU),
24 => Ok(DeviceType::Memory),
25 => Ok(DeviceType::Sound),
_ => Err(DeviceTypeError::InvalidDeviceType(virtio_device_id)),
}
}
}
impl TryFrom<u16> for DeviceType {
type Error = DeviceTypeError;
fn try_from(virtio_device_id: u16) -> core::result::Result<Self, Self::Error> {
u32::from(virtio_device_id).try_into()
}
}
impl TryFrom<u8> for DeviceType {
type Error = DeviceTypeError;
fn try_from(virtio_device_id: u8) -> core::result::Result<Self, Self::Error> {
u32::from(virtio_device_id).try_into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn debug_device_status() {
let status = DeviceStatus::from_bits_retain(0x23);
assert_eq!(
format!("{:?}", status),
"DeviceStatus(ACKNOWLEDGE | DRIVER | 0x20)"
);
}
}