use core::mem::size_of;
use snafu::prelude::*;
use heterob::{P16,P5,P6,P1, endianness::{FromLeBytes, LeBytesInto}, bit_numbering::Lsb};
use crate::header::BaseAddresses;
use super::ExtendedCapabilityDataError;
#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
pub enum SingleRootIoVirtualizationError {
#[snafu(display("can't read Element Self Description (4 bytes) from slice"))]
ElementSelfDescription,
#[snafu(display("can't read even one entry (4 bytes) from Link Entries"))]
LinkEntries,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SingleRootIoVirtualization {
pub sriov_capabilities: SriovCapabilities,
pub sriov_control: SriovControl,
pub sriov_status: SriovStatus,
pub initial_vfs: u16,
pub total_vfs: u16,
pub num_vfs: u16,
pub function_dependency_link: u8,
pub first_vf_offset: u16,
pub vf_stride: u16,
pub vf_device_id: u16,
pub page_sizes: PageSizes,
pub base_addresses: BaseAddresses<6>,
pub vf_migration_state_array_offset: u32,
}
impl SingleRootIoVirtualization {
pub const BYTES: usize = 0x40 - super::ECH_BYTES;
}
impl From<[u8; SingleRootIoVirtualization::BYTES]> for SingleRootIoVirtualization {
fn from(bytes: [u8; Self::BYTES]) -> Self {
let P16((
sriov_capabilities, sriov_control, sriov_status, initial_vfs, total_vfs, num_vfs,
function_dependency_link, rsvdp_0, first_vf_offset, vf_stride, rsvdp_1, vf_device_id,
supported_page_sizes, system_page_sizes, base_addresses, vf_migration_state_array_offset,
)) = bytes.le_bytes_into();
let _: (u8, u16, [u8; size_of::<u32>() * 6]) = (rsvdp_0, rsvdp_1, base_addresses);
Self {
sriov_capabilities,
sriov_control,
sriov_status,
initial_vfs,
total_vfs,
num_vfs,
function_dependency_link,
first_vf_offset,
vf_stride,
vf_device_id,
page_sizes: PageSizes {
supported: supported_page_sizes,
system: system_page_sizes
},
base_addresses: BaseAddresses::new(base_addresses.le_bytes_into()),
vf_migration_state_array_offset,
}
}
}
impl<'a> TryFrom<&'a [u8]> for SingleRootIoVirtualization {
type Error = ExtendedCapabilityDataError;
fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
slice.get(..Self::BYTES)
.and_then(|slice| <[u8; Self::BYTES]>::try_from(slice).ok())
.ok_or(ExtendedCapabilityDataError {
name: "Single Root I/O Virtualization",
size: Self::BYTES
})
.map(Self::from)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SriovCapabilities {
pub vf_migration_capable: bool,
pub ari_capable_hierarchy_preserved: bool,
pub vf_10bit_tag_requester_supported: bool,
pub vf_migration_interrupt_message_number: u16,
}
impl From<u32> for SriovCapabilities {
fn from(dword: u32) -> Self {
let Lsb((
vf_migration_capable,
ari_capable_hierarchy_preserved,
vf_10bit_tag_requester_supported,
(),
vf_migration_interrupt_message_number,
)) = P5::<_, 1, 1, 1, 18, 11>(dword).into();
Self {
vf_migration_capable,
ari_capable_hierarchy_preserved,
vf_10bit_tag_requester_supported,
vf_migration_interrupt_message_number,
}
}
}
impl FromLeBytes<4> for SriovCapabilities {
fn from_le_bytes(bytes: [u8;4]) -> Self { u32::from_le_bytes(bytes).into() }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SriovControl {
pub vf_enable: bool,
pub vf_migration_enable: bool,
pub vf_migration_interrupt_enable: bool,
pub vf_mse: bool,
pub ari_capable_hierarchy: bool,
pub vf_10bit_tag_requester_enable: bool,
}
impl From<u16> for SriovControl {
fn from(word: u16) -> Self {
let Lsb((
vf_enable,
vf_migration_enable,
vf_migration_interrupt_enable,
vf_mse,
ari_capable_hierarchy,
vf_10bit_tag_requester_enable,
)) = P6::<_, 1, 1, 1, 1, 1, 1>(word).into();
Self {
vf_enable,
vf_migration_enable,
vf_migration_interrupt_enable,
vf_mse,
ari_capable_hierarchy,
vf_10bit_tag_requester_enable,
}
}
}
impl FromLeBytes<2> for SriovControl {
fn from_le_bytes(bytes: [u8;2]) -> Self { u16::from_le_bytes(bytes).into() }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SriovStatus {
pub vf_migration_status: bool,
}
impl From<u16> for SriovStatus {
fn from(word: u16) -> Self {
let Lsb((
vf_migration_status,
)) = P1::<_, 1>(word).into();
Self {
vf_migration_status,
}
}
}
impl FromLeBytes<2> for SriovStatus {
fn from_le_bytes(bytes: [u8;2]) -> Self { u16::from_le_bytes(bytes).into() }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PageSizes {
pub supported: u32,
pub system: u32,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::header::{BaseAddress, BaseAddressType};
use pretty_assertions::assert_eq;
const DATA: [u8; 60] = [
0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,
0x00,0x00,0x00,0x00,0x80,0x01,0x04,0x00,0x00,0x00,0x20,0x15,0x53,0x05,0x00,0x00,
0x01,0x00,0x00,0x00,0x0c,0x00,0x18,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0c,0x00,0x1a,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
];
#[test]
fn parse_full_struct() {
let result: SingleRootIoVirtualization = DATA.into();
let sample = SingleRootIoVirtualization {
sriov_capabilities: SriovCapabilities {
vf_migration_capable: false,
ari_capable_hierarchy_preserved: true,
vf_10bit_tag_requester_supported: false,
vf_migration_interrupt_message_number: 0,
},
sriov_control: SriovControl {
vf_enable: false,
vf_migration_enable: false,
vf_migration_interrupt_enable: false,
vf_mse: false,
ari_capable_hierarchy: false,
vf_10bit_tag_requester_enable: false,
},
sriov_status: SriovStatus {
vf_migration_status: false
},
initial_vfs: 8,
total_vfs: 8,
num_vfs: 0,
function_dependency_link: 0,
first_vf_offset: 384,
vf_stride: 4,
vf_device_id: 0x1520,
page_sizes: PageSizes {
supported: 0x00000553,
system: 0x00000001,
},
base_addresses: BaseAddresses::new([
BaseAddress {
region: 0,
base_address_type: BaseAddressType::MemorySpace64 {
prefetchable: true, base_address: 0xa0180000
},
},
BaseAddress {
region: 3,
base_address_type: BaseAddressType::MemorySpace64 {
prefetchable: true, base_address: 0xa01a0000
},
},
].iter().collect::<[u32; 6]>()),
vf_migration_state_array_offset: 0,
};
assert_eq!(sample, result);
}
}