use core::mem;
use num_enum::{FromPrimitive, IntoPrimitive};
use pci_types::capability::PciCapabilityAddress;
use pci_types::ConfigRegionAccess;
use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess};
use volatile::VolatilePtr;
use volatile_macro::VolatileFieldAccess;
pub use crate::driver_notifications::NotificationData;
use crate::volatile::WideVolatilePtr;
use crate::{le16, le32, le64, DeviceConfigSpace, DeviceStatus};
#[doc(alias = "virtio_pci_cap")]
#[cfg_attr(
feature = "zerocopy",
derive(
zerocopy_derive::KnownLayout,
zerocopy_derive::Immutable,
zerocopy_derive::FromBytes,
)
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Cap {
pub cap_vndr: u8,
pub cap_next: u8,
pub cap_len: u8,
pub cfg_type: u8,
pub bar: u8,
pub id: u8,
pub padding: [u8; 2],
pub offset: le32,
pub length: le32,
}
impl Cap {
pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option<Self> {
let data = unsafe { access.read(addr.address, addr.offset) };
let [cap_vndr, _cap_next, cap_len, _cfg_type] = data.to_le_bytes();
if cap_vndr != 0x09 {
return None;
}
if cap_len < 16 {
return None;
}
let data = [
data,
unsafe { access.read(addr.address, addr.offset + 4) },
unsafe { access.read(addr.address, addr.offset + 8) },
unsafe { access.read(addr.address, addr.offset + 12) },
];
let data: [u32; 4] = data.map(u32::from_le);
let this = unsafe { mem::transmute::<[u32; 4], Self>(data) };
Some(this)
}
}
#[doc(alias = "virtio_pci_cap64")]
#[cfg_attr(
feature = "zerocopy",
derive(
zerocopy_derive::KnownLayout,
zerocopy_derive::Immutable,
zerocopy_derive::FromBytes,
)
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Cap64 {
pub cap: Cap,
pub offset_hi: le32,
pub length_hi: le32,
}
#[doc(alias = "virtio_pci_notify_cap")]
#[cfg_attr(
feature = "zerocopy",
derive(
zerocopy_derive::KnownLayout,
zerocopy_derive::Immutable,
zerocopy_derive::FromBytes,
)
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct NotifyCap {
pub cap: Cap,
pub notify_off_multiplier: le32,
}
#[doc(alias = "virtio_pci_cfg_cap")]
#[cfg_attr(
feature = "zerocopy",
derive(
zerocopy_derive::KnownLayout,
zerocopy_derive::Immutable,
zerocopy_derive::FromBytes,
)
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct CfgCap {
pub cap: Cap,
pub pci_cfg_data: [u8; 4],
}
#[derive(Clone, Copy, Debug)]
pub struct CapData {
pub cfg_type: CapCfgType,
pub bar: u8,
pub id: u8,
pub offset: le64,
pub length: le64,
pub notify_off_multiplier: Option<le32>,
}
impl CapData {
pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option<Self> {
let cap = Cap::read(addr, &access)?;
let cfg_type = CapCfgType::from(cap.cfg_type);
let (offset, length) = match cfg_type {
CapCfgType::SharedMemory => {
if cap.cap_len < 24 {
return None;
}
let offset_hi = unsafe { access.read(addr.address, addr.offset + 16) };
let offset_hi = le32::from_ne(offset_hi);
let offset = le64::from([cap.offset, offset_hi]);
let length_hi = unsafe { access.read(addr.address, addr.offset + 20) };
let length_hi = le32::from_ne(length_hi);
let length = le64::from([cap.length, length_hi]);
(offset, length)
}
_ => (le64::from(cap.offset), le64::from(cap.length)),
};
let notify_off_multiplier = match cfg_type {
CapCfgType::Notify => {
if cap.cap_len < 20 {
return None;
}
let notify_off_multiplier = unsafe { access.read(addr.address, addr.offset + 16) };
let notify_off_multiplier = le32::from_ne(notify_off_multiplier);
Some(notify_off_multiplier)
}
_ => None,
};
Some(Self {
cfg_type,
bar: cap.bar,
id: cap.id,
offset,
length,
notify_off_multiplier,
})
}
}
#[doc(alias = "VIRTIO_PCI_CAP")]
#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum CapCfgType {
#[doc(alias = "VIRTIO_PCI_CAP_COMMON_CFG")]
Common = 1,
#[doc(alias = "VIRTIO_PCI_CAP_NOTIFY_CFG")]
Notify = 2,
#[doc(alias = "VIRTIO_PCI_CAP_ISR_CFG")]
Isr = 3,
#[doc(alias = "VIRTIO_PCI_CAP_DEVICE_CFG")]
Device = 4,
#[doc(alias = "VIRTIO_PCI_CAP_PCI_CFG")]
Pci = 5,
#[doc(alias = "VIRTIO_PCI_CAP_SHARED_MEMORY_CFG")]
SharedMemory = 8,
#[doc(alias = "VIRTIO_PCI_CAP_VENDOR_CFG")]
Vendor = 9,
#[num_enum(catch_all)]
Unknown(u8),
}
#[doc(alias = "virtio_pci_common_cfg")]
#[cfg_attr(
feature = "zerocopy",
derive(
zerocopy_derive::KnownLayout,
zerocopy_derive::Immutable,
zerocopy_derive::FromBytes,
)
)]
#[derive(VolatileFieldAccess)]
#[repr(C)]
pub struct CommonCfg {
device_feature_select: le32,
#[access(ReadOnly)]
device_feature: le32,
driver_feature_select: le32,
driver_feature: le32,
config_msix_vector: le16,
#[access(ReadOnly)]
num_queues: le16,
device_status: DeviceStatus,
#[access(ReadOnly)]
config_generation: u8,
queue_select: le16,
queue_size: le16,
queue_msix_vector: le16,
queue_enable: le16,
#[access(ReadOnly)]
queue_notify_off: le16,
queue_desc_low: le32,
queue_desc_high: le32,
queue_driver_low: le32,
queue_driver_high: le32,
queue_device_low: le32,
queue_device_high: le32,
#[access(ReadOnly)]
queue_notify_data: le16,
queue_reset: le16,
}
impl_wide_field_access! {
pub trait CommonCfgVolatileWideFieldAccess<'a, A>: CommonCfg {
#[access(ReadWrite)]
queue_desc: queue_desc_low, queue_desc_high;
#[access(ReadWrite)]
queue_driver: queue_driver_low, queue_driver_high;
#[access(ReadWrite)]
queue_device: queue_device_low, queue_device_high;
}
}
impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, CommonCfg, A>
where
A: RestrictAccess<ReadOnly>,
A::Restricted: Readable,
{
fn read_config_with<F, T>(self, f: F) -> T
where
F: FnMut() -> T,
{
let mut f = f;
loop {
let before = self.config_generation().read();
let read = f();
let after = self.config_generation().read();
if after == before {
break read;
}
}
}
}
virtio_bitflags! {
pub struct IsrStatus: u8 {
const QUEUE_INTERRUPT = 1 << 0;
const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1;
}
}