#![allow(dead_code)]
use alloc::vec::Vec;
use core::intrinsics::unaligned_volatile_store;
use core::mem;
use core::result::Result;
use core::sync::atomic::{fence, Ordering};
#[cfg(all(not(feature = "rtl8139"), feature = "tcp"))]
use crate::arch::kernel::interrupts::*;
use crate::arch::memory_barrier;
use crate::arch::mm::PhysAddr;
use crate::arch::pci::PciConfigRegion;
use crate::drivers::error::DriverError;
#[cfg(feature = "fs")]
use crate::drivers::fs::virtio_fs::VirtioFsDriver;
#[cfg(all(not(feature = "rtl8139"), feature = "tcp"))]
use crate::drivers::net::network_irqhandler;
#[cfg(all(not(feature = "rtl8139"), feature = "tcp"))]
use crate::drivers::net::virtio_net::VirtioNetDriver;
use crate::drivers::pci::error::PciError;
use crate::drivers::pci::{DeviceHeader, Masks, PciDevice};
use crate::drivers::virtio::device;
use crate::drivers::virtio::env::memory::{MemLen, MemOff, VirtMemAddr};
use crate::drivers::virtio::error::VirtioError;
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u16)]
pub enum DevId {
INVALID = 0x0,
VIRTIO_TRANS_DEV_ID_NET = 0x1000,
VIRTIO_TRANS_DEV_ID_BLK = 0x1001,
VIRTIO_TRANS_DEV_ID_MEM_BALL = 0x1002,
VIRTIO_TRANS_DEV_ID_CONS = 0x1003,
VIRTIO_TRANS_DEV_ID_SCSI = 0x1004,
VIRTIO_TRANS_DEV_ID_ENTROPY = 0x1005,
VIRTIO_TRANS_DEV_ID_9P = 0x1009,
VIRTIO_DEV_ID_NET = 0x1041,
VIRTIO_DEV_ID_FS = 0x105A,
}
impl From<DevId> for u16 {
fn from(val: DevId) -> u16 {
match val {
DevId::VIRTIO_TRANS_DEV_ID_NET => 0x1000,
DevId::VIRTIO_TRANS_DEV_ID_BLK => 0x1001,
DevId::VIRTIO_TRANS_DEV_ID_MEM_BALL => 0x1002,
DevId::VIRTIO_TRANS_DEV_ID_CONS => 0x1003,
DevId::VIRTIO_TRANS_DEV_ID_SCSI => 0x1004,
DevId::VIRTIO_TRANS_DEV_ID_ENTROPY => 0x1005,
DevId::VIRTIO_TRANS_DEV_ID_9P => 0x1009,
DevId::VIRTIO_DEV_ID_NET => 0x1041,
DevId::VIRTIO_DEV_ID_FS => 0x105A,
DevId::INVALID => 0x0,
}
}
}
impl From<u16> for DevId {
fn from(val: u16) -> Self {
match val {
0x1000 => DevId::VIRTIO_TRANS_DEV_ID_NET,
0x1001 => DevId::VIRTIO_TRANS_DEV_ID_BLK,
0x1002 => DevId::VIRTIO_TRANS_DEV_ID_MEM_BALL,
0x1003 => DevId::VIRTIO_TRANS_DEV_ID_CONS,
0x1004 => DevId::VIRTIO_TRANS_DEV_ID_SCSI,
0x1005 => DevId::VIRTIO_TRANS_DEV_ID_ENTROPY,
0x1009 => DevId::VIRTIO_TRANS_DEV_ID_9P,
0x1041 => DevId::VIRTIO_DEV_ID_NET,
0x105A => DevId::VIRTIO_DEV_ID_FS,
_ => DevId::INVALID,
}
}
}
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum CfgType {
INVALID = 0,
VIRTIO_PCI_CAP_COMMON_CFG = 1,
VIRTIO_PCI_CAP_NOTIFY_CFG = 2,
VIRTIO_PCI_CAP_ISR_CFG = 3,
VIRTIO_PCI_CAP_DEVICE_CFG = 4,
VIRTIO_PCI_CAP_PCI_CFG = 5,
VIRTIO_PCI_CAP_SHARED_MEMORY_CFG = 8,
}
impl From<CfgType> for u8 {
fn from(val: CfgType) -> u8 {
match val {
CfgType::INVALID => 0,
CfgType::VIRTIO_PCI_CAP_COMMON_CFG => 1,
CfgType::VIRTIO_PCI_CAP_NOTIFY_CFG => 2,
CfgType::VIRTIO_PCI_CAP_ISR_CFG => 3,
CfgType::VIRTIO_PCI_CAP_DEVICE_CFG => 4,
CfgType::VIRTIO_PCI_CAP_PCI_CFG => 5,
CfgType::VIRTIO_PCI_CAP_SHARED_MEMORY_CFG => 8,
}
}
}
impl From<u8> for CfgType {
fn from(val: u8) -> Self {
match val {
1 => CfgType::VIRTIO_PCI_CAP_COMMON_CFG,
2 => CfgType::VIRTIO_PCI_CAP_NOTIFY_CFG,
3 => CfgType::VIRTIO_PCI_CAP_ISR_CFG,
4 => CfgType::VIRTIO_PCI_CAP_DEVICE_CFG,
5 => CfgType::VIRTIO_PCI_CAP_PCI_CFG,
8 => CfgType::VIRTIO_PCI_CAP_SHARED_MEMORY_CFG,
_ => CfgType::INVALID,
}
}
}
#[derive(Clone)]
pub struct Origin {
cfg_ptr: u32, dev_id: u16,
cap_struct: PciCapRaw,
}
pub fn map_dev_cfg<T>(cap: &PciCap) -> Option<&'static mut T> {
if cap.cfg_type != CfgType::VIRTIO_PCI_CAP_DEVICE_CFG {
error!("Capability of device config has wrong id. Mapping not possible...");
return None;
};
if cap.bar_len() < u64::from(cap.len() + cap.offset()) {
error!(
"Device config of device {:x}, does not fit into memory specified by bar!",
cap.dev_id(),
);
return None;
}
if cap.len() < MemLen::from(mem::size_of::<T>()) {
error!("Device specific config from device {:x}, does not represent actual structure specified by the standard!", cap.dev_id());
return None;
}
let virt_addr_raw: VirtMemAddr = cap.bar_addr() + cap.offset();
let dev_cfg: &'static mut T = unsafe { &mut *(usize::from(virt_addr_raw) as *mut T) };
Some(dev_cfg)
}
#[derive(Clone)]
pub struct PciCap {
cfg_type: CfgType,
bar: PciBar,
id: u8,
offset: MemOff,
length: MemLen,
device: PciDevice<PciConfigRegion>,
origin: Origin,
}
impl PciCap {
pub fn offset(&self) -> MemOff {
self.offset
}
pub fn len(&self) -> MemLen {
self.length
}
pub fn bar_len(&self) -> u64 {
self.bar.length
}
pub fn bar_addr(&self) -> VirtMemAddr {
self.bar.mem_addr
}
pub fn dev_id(&self) -> u16 {
self.origin.dev_id
}
}
#[derive(Clone)]
#[repr(C)]
struct PciCapRaw {
cap_vndr: u8,
cap_next: u8,
cap_len: u8,
cfg_type: u8,
bar_index: u8,
id: u8,
padding: [u8; 2],
offset: u32,
length: u32,
}
impl Eq for PciCapRaw {}
impl PartialEq for PciCapRaw {
fn eq(&self, other: &Self) -> bool {
self.cap_vndr == other.cap_vndr
&& self.cap_next == other.cap_next
&& self.cap_len == other.cap_len
&& self.cfg_type == other.cfg_type
&& self.bar_index == other.bar_index
&& self.id == other.id
&& self.offset == other.offset
&& self.length == other.length
}
}
pub struct UniCapsColl {
com_cfg_list: Vec<ComCfg>,
notif_cfg_list: Vec<NotifCfg>,
isr_stat_list: Vec<IsrStatus>,
pci_cfg_acc_list: Vec<PciCfgAlt>,
sh_mem_cfg_list: Vec<ShMemCfg>,
dev_cfg_list: Vec<PciCap>,
}
impl UniCapsColl {
fn new() -> Self {
UniCapsColl {
com_cfg_list: Vec::new(),
notif_cfg_list: Vec::new(),
isr_stat_list: Vec::new(),
pci_cfg_acc_list: Vec::new(),
sh_mem_cfg_list: Vec::new(),
dev_cfg_list: Vec::new(),
}
}
fn add_cfg_common(&mut self, com: ComCfg) {
self.com_cfg_list.push(com);
self.com_cfg_list.sort_by(|a, b| b.rank.cmp(&a.rank));
}
fn add_cfg_notif(&mut self, notif: NotifCfg) {
self.notif_cfg_list.push(notif);
self.notif_cfg_list.sort_by(|a, b| b.rank.cmp(&a.rank));
}
fn add_cfg_isr(&mut self, isr_stat: IsrStatus) {
self.isr_stat_list.push(isr_stat);
self.isr_stat_list.sort_by(|a, b| b.rank.cmp(&a.rank));
}
fn add_cfg_alt(&mut self, pci_alt: PciCfgAlt) {
self.pci_cfg_acc_list.push(pci_alt);
self.pci_cfg_acc_list
.sort_by(|a, b| b.pci_cap.id.cmp(&a.pci_cap.id));
}
fn add_cfg_sh_mem(&mut self, sh_mem: ShMemCfg) {
self.sh_mem_cfg_list.push(sh_mem);
self.sh_mem_cfg_list.sort_by(|a, b| b.id.cmp(&a.id));
}
fn add_cfg_dev(&mut self, pci_cap: PciCap) {
self.dev_cfg_list.push(pci_cap);
self.dev_cfg_list.sort_by(|a, b| b.id.cmp(&a.id));
}
}
impl UniCapsColl {
pub fn get_dev_cfg(&mut self) -> Option<PciCap> {
self.dev_cfg_list.pop()
}
pub fn get_com_cfg(&mut self) -> Option<ComCfg> {
self.com_cfg_list.pop()
}
pub fn get_isr_cfg(&mut self) -> Option<IsrStatus> {
self.isr_stat_list.pop()
}
pub fn get_notif_cfg(&mut self) -> Option<NotifCfg> {
self.notif_cfg_list.pop()
}
}
pub struct ComCfg {
com_cfg: &'static mut ComCfgRaw,
rank: u8,
}
impl ComCfg {
fn new(raw: &'static mut ComCfgRaw, rank: u8) -> Self {
ComCfg { com_cfg: raw, rank }
}
}
pub struct VqCfgHandler<'a> {
vq_index: u16,
raw: &'a mut ComCfgRaw,
}
impl<'a> VqCfgHandler<'a> {
pub fn set_vq_size(&mut self, size: u16) -> u16 {
self.raw.queue_select = self.vq_index;
if self.raw.queue_size >= size {
self.raw.queue_size = size;
}
self.raw.queue_size
}
pub fn set_ring_addr(&mut self, addr: PhysAddr) {
self.raw.queue_select = self.vq_index;
self.raw.queue_desc = addr.as_u64();
}
pub fn set_drv_ctrl_addr(&mut self, addr: PhysAddr) {
self.raw.queue_select = self.vq_index;
self.raw.queue_driver = addr.as_u64();
}
pub fn set_dev_ctrl_addr(&mut self, addr: PhysAddr) {
self.raw.queue_select = self.vq_index;
self.raw.queue_device = addr.as_u64();
}
pub fn notif_off(&mut self) -> u16 {
self.raw.queue_select = self.vq_index;
self.raw.queue_notify_off
}
pub fn enable_queue(&mut self) {
self.raw.queue_select = self.vq_index;
self.raw.queue_enable = 1;
}
}
impl ComCfg {
pub fn select_vq(&mut self, index: u16) -> Option<VqCfgHandler<'_>> {
self.com_cfg.queue_select = index;
if self.com_cfg.queue_size == 0 {
None
} else {
Some(VqCfgHandler {
vq_index: index,
raw: self.com_cfg,
})
}
}
pub fn dev_status(&self) -> u8 {
self.com_cfg.device_status
}
pub fn reset_dev(&mut self) {
memory_barrier();
self.com_cfg.device_status = 0;
}
pub fn set_failed(&mut self) {
memory_barrier();
self.com_cfg.device_status = u8::from(device::Status::FAILED);
}
pub fn ack_dev(&mut self) {
memory_barrier();
self.com_cfg.device_status |= u8::from(device::Status::ACKNOWLEDGE);
}
pub fn set_drv(&mut self) {
memory_barrier();
self.com_cfg.device_status |= u8::from(device::Status::DRIVER);
}
pub fn features_ok(&mut self) {
memory_barrier();
self.com_cfg.device_status |= u8::from(device::Status::FEATURES_OK);
}
pub fn check_features(&self) -> bool {
memory_barrier();
self.com_cfg.device_status & u8::from(device::Status::FEATURES_OK)
== u8::from(device::Status::FEATURES_OK)
}
pub fn drv_ok(&mut self) {
memory_barrier();
self.com_cfg.device_status |= u8::from(device::Status::DRIVER_OK);
}
pub fn dev_features(&mut self) -> u64 {
memory_barrier();
self.com_cfg.device_feature_select = 1;
memory_barrier();
let mut dev_feat = u64::from(self.com_cfg.device_feature) << 32;
self.com_cfg.device_feature_select = 0;
memory_barrier();
dev_feat |= u64::from(self.com_cfg.device_feature);
dev_feat
}
pub fn set_drv_features(&mut self, feats: u64) {
let high: u32 = (feats >> 32) as u32;
let low: u32 = feats as u32;
memory_barrier();
self.com_cfg.driver_feature_select = 0;
memory_barrier();
self.com_cfg.driver_feature = low;
self.com_cfg.driver_feature_select = 1;
memory_barrier();
self.com_cfg.driver_feature = high;
}
}
#[repr(C)]
struct ComCfgRaw {
device_feature_select: u32, device_feature: u32, driver_feature_select: u32, driver_feature: u32, config_msix_vector: u16, num_queues: u16, device_status: u8, config_generation: u8,
queue_select: u16, queue_size: u16, queue_msix_vector: u16, queue_enable: u16, queue_notify_off: u16, queue_desc: u64, queue_driver: u64, queue_device: u64, }
impl ComCfgRaw {
fn map(cap: &PciCap) -> Option<&'static mut ComCfgRaw> {
if cap.bar.length < u64::from(cap.length + cap.offset) {
error!("Common config of with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
cap.id,
cap.origin.dev_id,
cap.bar.index
);
return None;
}
if cap.length < MemLen::from(mem::size_of::<ComCfgRaw>() * 8) {
error!("Common config of with id {}, does not represent actual structure specified by the standard!", cap.id);
return None;
}
let virt_addr_raw = cap.bar.mem_addr + cap.offset;
let com_cfg_raw: &mut ComCfgRaw =
unsafe { &mut *(usize::from(virt_addr_raw) as *mut ComCfgRaw) };
Some(com_cfg_raw)
}
}
pub struct NotifCfg {
base_addr: VirtMemAddr,
notify_off_multiplier: u32,
rank: u8,
length: MemLen,
}
impl NotifCfg {
fn new(cap: &PciCap) -> Option<Self> {
if cap.bar.length < u64::from(u32::from(cap.length + cap.offset)) {
error!("Notification config with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
cap.id,
cap.origin.dev_id,
cap.bar.index
);
return None;
}
let notify_off_multiplier = cap.device.read_register(
u16::try_from(cap.origin.cfg_ptr).unwrap() + u16::from(cap.origin.cap_struct.cap_len),
);
let base_addr = cap.bar.mem_addr + cap.offset;
Some(NotifCfg {
base_addr,
notify_off_multiplier,
rank: cap.id,
length: cap.length,
})
}
pub fn base(&self) -> usize {
usize::from(self.base_addr)
}
pub fn multiplier(&self) -> u32 {
self.notify_off_multiplier
}
}
pub struct NotifCtrl {
f_notif_data: bool,
notif_addr: *mut usize,
}
impl NotifCtrl {
pub fn new(notif_addr: *mut usize) -> Self {
NotifCtrl {
f_notif_data: false,
notif_addr,
}
}
pub fn enable_notif_data(&mut self) {
self.f_notif_data = true;
}
pub fn notify_dev(&self, notif_data: &[u8]) {
fence(Ordering::Acquire);
if self.f_notif_data {
let ptr = self.notif_addr as *mut u32;
unsafe {
unaligned_volatile_store(
ptr,
u32::from_ne_bytes(notif_data[0..4].try_into().unwrap()),
);
}
} else {
let ptr = self.notif_addr as *mut u16;
unsafe {
unaligned_volatile_store(
ptr,
u16::from_ne_bytes(notif_data[0..2].try_into().unwrap()),
);
}
}
fence(Ordering::Release);
}
}
pub struct IsrStatus {
isr_stat: &'static mut IsrStatusRaw,
rank: u8,
}
impl IsrStatus {
fn new(raw: &'static mut IsrStatusRaw, rank: u8) -> Self {
IsrStatus {
isr_stat: raw,
rank,
}
}
pub fn is_interrupt(&self) -> bool {
self.isr_stat.flags & 1 << 0 == 1
}
pub fn is_cfg_change(&self) -> bool {
self.isr_stat.flags & 1 << 1 == 1 << 1
}
pub fn acknowledge(&mut self) {
}
}
#[repr(C)]
struct IsrStatusRaw {
flags: u8,
}
impl IsrStatusRaw {
fn map(cap: &PciCap) -> Option<&'static mut IsrStatusRaw> {
if cap.bar.length < u64::from(cap.length + cap.offset) {
error!("ISR status config with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
cap.id,
cap.origin.dev_id,
cap.bar.index
);
return None;
}
let virt_addr_raw: VirtMemAddr = cap.bar.mem_addr + cap.offset;
let isr_stat_raw: &mut IsrStatusRaw =
unsafe { &mut *(usize::from(virt_addr_raw) as *mut IsrStatusRaw) };
Some(isr_stat_raw)
}
fn cfg_event() -> bool {
unimplemented!();
}
fn vqueue_event() -> bool {
unimplemented!();
}
}
pub struct PciCfgAlt {
pci_cap: PciCap,
pci_cfg_data: [u8; 4], }
impl PciCfgAlt {
fn new(cap: &PciCap) -> Self {
PciCfgAlt {
pci_cap: cap.clone(),
pci_cfg_data: [0; 4],
}
}
}
pub struct ShMemCfg {
mem_addr: VirtMemAddr,
length: MemLen,
sh_mem: ShMem,
id: u8,
}
impl ShMemCfg {
fn new(cap: &PciCap) -> Option<Self> {
if cap.bar.length < u64::from(cap.length + cap.offset) {
error!("Shared memory config of with id {} of device {:x}, does not fit into memory specified by bar {:x}!",
cap.id,
cap.origin.dev_id,
cap.bar.index
);
return None;
}
let offset_high = cap.device.read_register(
u16::try_from(cap.origin.cfg_ptr).unwrap() + u16::from(cap.origin.cap_struct.cap_len),
);
let offset =
MemOff::from((u64::from(offset_high) << 32) ^ u64::from(cap.origin.cap_struct.offset));
let length_high = cap.device.read_register(
u16::try_from(cap.origin.cfg_ptr).unwrap()
+ u16::from(cap.origin.cap_struct.cap_len + 4),
);
let length =
MemLen::from((u64::from(length_high) << 32) ^ u64::from(cap.origin.cap_struct.length));
let virt_addr_raw = cap.bar.mem_addr + offset;
let raw_ptr = usize::from(virt_addr_raw) as *mut u8;
unsafe {
for i in 0..usize::from(length) {
*(raw_ptr.add(i)) = 0;
}
};
assert!(mem::size_of::<usize>() == 8);
Some(ShMemCfg {
mem_addr: virt_addr_raw,
length: cap.length,
sh_mem: ShMem {
ptr: raw_ptr,
len: cap.bar.length as usize,
},
id: cap.id,
})
}
}
struct ShMem {
ptr: *mut u8,
len: usize,
}
impl core::ops::Deref for ShMem {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl core::ops::DerefMut for ShMem {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
}
}
impl Drop for ShMem {
fn drop(&mut self) {
for i in 0..self.len {
unsafe {
*(self.ptr.add(i)) = 0;
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct PciBar {
index: u8,
mem_addr: VirtMemAddr,
length: u64,
}
impl PciBar {
pub fn new(index: u8, mem_addr: VirtMemAddr, length: u64) -> Self {
PciBar {
index,
mem_addr,
length,
}
}
}
fn read_cap_raw(device: &PciDevice<PciConfigRegion>, register: u32) -> PciCapRaw {
let mut quadruple_word: [u8; 16] = [0; 16];
debug!("Converting read word from PCI device config space into native endian bytes.");
for i in 0..4 {
let word: [u8; 4] = device
.read_register((register + 4 * i).try_into().unwrap())
.to_le_bytes();
let i = 4 * i as usize;
quadruple_word[i..i + 4].copy_from_slice(&word);
}
PciCapRaw {
cap_vndr: quadruple_word[0],
cap_next: quadruple_word[1],
cap_len: quadruple_word[2],
cfg_type: quadruple_word[3],
bar_index: quadruple_word[4],
id: quadruple_word[5],
padding: quadruple_word[6..8].try_into().unwrap(),
offset: u32::from_le_bytes(quadruple_word[8..12].try_into().unwrap()),
length: u32::from_le_bytes(quadruple_word[12..16].try_into().unwrap()),
}
}
fn read_caps(
device: &PciDevice<PciConfigRegion>,
bars: Vec<PciBar>,
) -> Result<Vec<PciCap>, PciError> {
let device_id = device.device_id();
let ptr: u32 = dev_caps_ptr(device);
let mut next_ptr = if ptr >= 0x40u32 {
ptr
} else {
return Err(PciError::BadCapPtr(device_id));
};
let mut cap_list: Vec<PciCap> = Vec::new();
'cap_list: while next_ptr != 0u32 {
let mut before = read_cap_raw(device, next_ptr);
let mut cap_raw = read_cap_raw(device, next_ptr);
while before != cap_raw {
before = read_cap_raw(device, next_ptr);
cap_raw = read_cap_raw(device, next_ptr);
}
let mut iter = bars.iter();
next_ptr = u32::from(cap_raw.cap_next);
match cap_raw.cap_vndr {
0x09u8 => {
let cap_bar: PciBar = loop {
match iter.next() {
Some(bar) => {
if bar.index <= 5 && bar.index == cap_raw.bar_index {
break *bar;
}
}
None => {
error!("Found virtio capability whose BAR is not mapped or non existing. Capability of type {:x} and id {:x} for device {:x}, can not be used!",
cap_raw.cfg_type, cap_raw.id, device_id);
continue 'cap_list;
}
}
};
cap_list.push(PciCap {
cfg_type: CfgType::from(cap_raw.cfg_type),
bar: cap_bar,
id: cap_raw.id,
offset: MemOff::from(cap_raw.offset),
length: MemLen::from(cap_raw.length),
device: *device,
origin: Origin {
cfg_ptr: next_ptr,
dev_id: device_id,
cap_struct: cap_raw,
},
})
}
_ => info!(
"Non Virtio PCI capability with id {:x} found. And NOT used.",
cap_raw.cap_vndr
),
}
}
if cap_list.is_empty() {
error!("No virtio capability found for device {:x}", device_id);
Err(PciError::NoVirtioCaps(device_id))
} else {
Ok(cap_list)
}
}
fn dev_status(device: &PciDevice<PciConfigRegion>) -> u32 {
let stat_com_reg = device.read_register(DeviceHeader::PCI_COMMAND_REGISTER.into());
stat_com_reg >> 16
}
fn dev_caps_ptr(device: &PciDevice<PciConfigRegion>) -> u32 {
let cap_lst_reg = device.read_register(DeviceHeader::PCI_CAPABILITY_LIST_REGISTER.into());
cap_lst_reg & u32::from(Masks::PCI_MASK_CAPLIST_POINTER)
}
fn map_bars(device: &PciDevice<PciConfigRegion>) -> Result<Vec<PciBar>, PciError> {
crate::drivers::virtio::env::pci::map_bar_mem(device)
}
fn no_cap_list(device: &PciDevice<PciConfigRegion>) -> bool {
dev_status(device) & u32::from(Masks::PCI_MASK_STATUS_CAPABILITIES_LIST) == 0
}
fn check_caps(caps: UniCapsColl) -> Result<UniCapsColl, PciError> {
if caps.com_cfg_list.is_empty() {
error!("Device with unknown id, does not have a common config structure!");
return Err(PciError::General(0));
}
Ok(caps)
}
pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsColl, PciError> {
let device_id = device.device_id();
if no_cap_list(device) {
error!("Found virtio device without capability list. Aborting!");
return Err(PciError::NoCapPtr(device_id));
}
let bar_list = match map_bars(device) {
Ok(list) => list,
Err(pci_error) => return Err(pci_error),
};
let cap_list = match read_caps(device, bar_list) {
Ok(list) => list,
Err(pci_error) => return Err(pci_error),
};
let mut caps = UniCapsColl::new();
for pci_cap in cap_list {
match pci_cap.cfg_type {
CfgType::VIRTIO_PCI_CAP_COMMON_CFG => match ComCfgRaw::map(&pci_cap) {
Some(cap) => caps.add_cfg_common(ComCfg::new(cap, pci_cap.id)),
None => error!(
"Common config capability with id {}, of device {:x}, could not be mapped!",
pci_cap.id, device_id
),
},
CfgType::VIRTIO_PCI_CAP_NOTIFY_CFG => match NotifCfg::new(&pci_cap) {
Some(notif) => caps.add_cfg_notif(notif),
None => error!(
"Notification config capability with id {}, of device {:x} could not be used!",
pci_cap.id, device_id
),
},
CfgType::VIRTIO_PCI_CAP_ISR_CFG => match IsrStatusRaw::map(&pci_cap) {
Some(isr_stat) => caps.add_cfg_isr(IsrStatus::new(isr_stat, pci_cap.id)),
None => error!(
"ISR status config capability with id {}, of device {:x} could not be used!",
pci_cap.id, device_id
),
},
CfgType::VIRTIO_PCI_CAP_PCI_CFG => caps.add_cfg_alt(PciCfgAlt::new(&pci_cap)),
CfgType::VIRTIO_PCI_CAP_SHARED_MEMORY_CFG => match ShMemCfg::new(&pci_cap) {
Some(sh_mem) => caps.add_cfg_sh_mem(sh_mem),
None => error!(
"Shared Memory config capability with id {}, of device {:x} could not be used!",
pci_cap.id, device_id
),
},
CfgType::VIRTIO_PCI_CAP_DEVICE_CFG => caps.add_cfg_dev(pci_cap),
_ => continue,
}
}
check_caps(caps)
}
pub(crate) fn init_device(
device: &PciDevice<PciConfigRegion>,
) -> Result<VirtioDriver, DriverError> {
let device_id = device.device_id();
let virt_drv = match DevId::from(device_id) {
DevId::VIRTIO_TRANS_DEV_ID_NET
| DevId::VIRTIO_TRANS_DEV_ID_BLK
| DevId::VIRTIO_TRANS_DEV_ID_MEM_BALL
| DevId::VIRTIO_TRANS_DEV_ID_CONS
| DevId::VIRTIO_TRANS_DEV_ID_SCSI
| DevId::VIRTIO_TRANS_DEV_ID_ENTROPY
| DevId::VIRTIO_TRANS_DEV_ID_9P => {
warn!(
"Legacy/transitional Virtio device, with id: {:#x} is NOT supported, skipping!",
device_id
);
Err(DriverError::InitVirtioDevFail(
VirtioError::DevNotSupported(device_id),
))
}
#[cfg(all(not(feature = "rtl8139"), feature = "tcp"))]
DevId::VIRTIO_DEV_ID_NET => match VirtioNetDriver::init(device) {
Ok(virt_net_drv) => {
info!("Virtio network driver initialized.");
Ok(VirtioDriver::Network(virt_net_drv))
}
Err(virtio_error) => {
error!(
"Virtio networkd driver could not be initialized with device: {:x}",
device_id
);
Err(DriverError::InitVirtioDevFail(virtio_error))
}
},
#[cfg(feature = "fs")]
DevId::VIRTIO_DEV_ID_FS => {
match VirtioFsDriver::init(device) {
Ok(virt_fs_drv) => {
info!("Virtio filesystem driver initialized.");
Ok(VirtioDriver::FileSystem(virt_fs_drv))
}
Err(virtio_error) => {
error!(
"Virtio filesystem driver could not be initialized with device: {:x}",
device_id
);
Err(DriverError::InitVirtioDevFail(virtio_error))
}
}
}
_ => {
warn!(
"Virtio device with id: {:#x} is NOT supported, skipping!",
device_id
);
Err(DriverError::InitVirtioDevFail(
VirtioError::DevNotSupported(device_id),
))
}
};
match virt_drv {
Ok(drv) => {
match &drv {
#[cfg(all(not(feature = "rtl8139"), feature = "tcp"))]
VirtioDriver::Network(_) => {
let irq = device.get_irq().unwrap();
info!("Install virtio interrupt handler at line {}", irq);
irq_install_handler(irq, network_irqhandler);
add_irq_name(irq, "virtio_net");
Ok(drv)
}
#[cfg(feature = "fs")]
VirtioDriver::FileSystem(_) => Ok(drv),
}
}
Err(virt_err) => Err(virt_err),
}
}
pub(crate) enum VirtioDriver {
#[cfg(all(not(feature = "rtl8139"), feature = "tcp"))]
Network(VirtioNetDriver),
#[cfg(feature = "fs")]
FileSystem(VirtioFsDriver),
}