#![allow(dead_code)]
use crate::arch::kernel::pci as kernel_pci;
use crate::arch::kernel::pci::error::PciError;
use crate::arch::kernel::pci::{PciAdapter, PciDriver};
use crate::arch::mm::PhysAddr;
use crate::synch::spinlock::SpinlockIrqSave;
use alloc::vec::Vec;
use core::mem;
use core::result::Result;
use crate::drivers::error::DriverError;
use crate::drivers::net::virtio_net::VirtioNetDriver;
use crate::drivers::virtio::device;
use crate::drivers::virtio::env;
use crate::drivers::virtio::env::memory::{MemLen, MemOff, VirtMemAddr};
use crate::drivers::virtio::error::VirtioError;
use crate::arch::x86_64::kernel::irq::*;
use crate::drivers::net::network_irqhandler;
use crate::drivers::virtio::depr::virtio_fs;
#[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)]
#[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: u8, bus: u8, 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,
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) {
self.com_cfg.device_status = 0;
}
pub fn set_failed(&mut self) {
self.com_cfg.device_status = u8::from(device::Status::FAILED);
}
pub fn ack_dev(&mut self) {
self.com_cfg.device_status |= u8::from(device::Status::ACKNOWLEDGE);
}
pub fn set_drv(&mut self) {
self.com_cfg.device_status |= u8::from(device::Status::DRIVER);
}
pub fn features_ok(&mut self) {
self.com_cfg.device_status |= u8::from(device::Status::FEATURES_OK);
}
pub fn check_features(&self) -> bool {
self.com_cfg.device_status & u8::from(device::Status::FEATURES_OK)
== u8::from(device::Status::FEATURES_OK)
}
pub fn drv_ok(&mut self) {
self.com_cfg.device_status |= u8::from(device::Status::DRIVER_OK);
}
pub fn dev_features(&mut self) -> u64 {
self.com_cfg.device_feature_select = 1;
let mut dev_feat = u64::from(self.com_cfg.device_feature) << 32;
self.com_cfg.device_feature_select = 0;
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;
self.com_cfg.driver_feature_select = 0;
self.com_cfg.driver_feature = low;
self.com_cfg.driver_feature_select = 1;
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 = env::pci::read_cfg_no_adapter(
cap.origin.bus,
cap.origin.dev,
cap.origin.cfg_ptr + u32::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]) {
if self.f_notif_data {
unsafe {
let notif_area = core::slice::from_raw_parts_mut(self.notif_addr as *mut u8, 4);
let mut notif_data = notif_data.iter();
for byte in notif_area {
*byte = *notif_data.next().unwrap();
}
}
} else {
unsafe {
let notif_area = core::slice::from_raw_parts_mut(self.notif_addr as *mut u8, 2);
let mut notif_data = notif_data.iter();
for byte in notif_area {
*byte = *notif_data.next().unwrap();
}
}
}
}
}
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 = env::pci::read_cfg_no_adapter(
cap.origin.bus,
cap.origin.bus,
cap.origin.cfg_ptr + u32::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 = env::pci::read_cfg_no_adapter(
cap.origin.bus,
cap.origin.bus,
cap.origin.cfg_ptr + u32::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(adapter: &PciAdapter, 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] = env::pci::read_config(adapter, register + 4 * i).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(adapter: &PciAdapter, bars: Vec<PciBar>) -> Result<Vec<PciCap>, PciError> {
let ptr: u32 = dev_caps_ptr(adapter);
let mut next_ptr = if ptr >= 0x40u32 {
ptr
} else {
return Err(PciError::BadCapPtr(adapter.device_id));
};
let mut cap_list: Vec<PciCap> = Vec::new();
'cap_list: while next_ptr != 0u32 {
let mut before = read_cap_raw(adapter, next_ptr);
let mut cap_raw = read_cap_raw(adapter, next_ptr);
while before != cap_raw {
before = read_cap_raw(adapter, next_ptr);
cap_raw = read_cap_raw(adapter, 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, adapter.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),
origin: Origin {
cfg_ptr: next_ptr,
dev: adapter.device,
bus: adapter.bus,
dev_id: adapter.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}",
adapter.device_id
);
Err(PciError::NoVirtioCaps(adapter.device_id))
} else {
Ok(cap_list)
}
}
fn dev_status(adapter: &PciAdapter) -> u32 {
let stat_com_reg = env::pci::read_config(
adapter,
u32::from(constants::RegisterHeader00H::PCI_COMMAND_REGISTER),
);
stat_com_reg >> 16
}
fn dev_caps_ptr(adapter: &PciAdapter) -> u32 {
let cap_lst_reg = env::pci::read_config(
adapter,
u32::from(constants::RegisterHeader00H::PCI_CAPABILITY_LIST_REGISTER),
);
cap_lst_reg & u32::from(constants::Masks::PCI_MASK_CAPLIST_POINTER)
}
fn map_bars(adapter: &PciAdapter) -> Result<Vec<PciBar>, PciError> {
crate::drivers::virtio::env::pci::map_bar_mem(adapter)
}
fn no_cap_list(adapter: &PciAdapter) -> bool {
dev_status(adapter) & u32::from(constants::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 unknwon id, does not have a common config structure!");
return Err(PciError::General(0));
}
Ok(caps)
}
pub fn map_caps(adapter: &PciAdapter) -> Result<UniCapsColl, PciError> {
if no_cap_list(adapter) {
error!("Found virtio device without capability list. Aborting!");
return Err(PciError::NoCapPtr(adapter.device_id));
}
let bar_list = match map_bars(adapter) {
Ok(list) => list,
Err(pci_error) => return Err(pci_error),
};
let cap_list = match read_caps(adapter, 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, adapter.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, adapter.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, adapter.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, adapter.device_id
),
},
CfgType::VIRTIO_PCI_CAP_DEVICE_CFG => caps.add_cfg_dev(pci_cap),
_ => continue,
}
}
check_caps(caps)
}
pub fn init_device(adapter: &PciAdapter) -> Result<VirtioDriver, DriverError> {
let virt_drv = match DevId::from(adapter.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!",
adapter.device_id
);
Err(DriverError::InitVirtioDevFail(
VirtioError::DevNotSupported(adapter.device_id),
))
}
DevId::VIRTIO_DEV_ID_NET => match VirtioNetDriver::init(adapter) {
Ok(virt_net_drv) => {
info!("Virtio network driver initialized with Virtio network device.");
Ok(VirtioDriver::Network(virt_net_drv))
}
Err(virtio_error) => {
error!(
"Virtio networkd driver could not be initialized with device: {:x}",
adapter.device_id
);
Err(DriverError::InitVirtioDevFail(virtio_error))
}
},
DevId::VIRTIO_DEV_ID_FS => {
info!("Found Virtio-FS device!");
match virtio_fs::create_virtiofs_driver(adapter) {
Some(virt_fs_drv) => {
kernel_pci::register_driver(PciDriver::VirtioFs(SpinlockIrqSave::new(
virt_fs_drv,
)));
virtio_fs::init_fs();
Ok(VirtioDriver::FileSystem)
}
None => Err(DriverError::InitVirtioDevFail(VirtioError::Unknown)),
}
}
_ => {
warn!(
"Virtio device with id: {:#x} is NOT supported, skipping!",
adapter.device_id
);
Err(DriverError::InitVirtioDevFail(
VirtioError::DevNotSupported(adapter.device_id),
))
}
};
match virt_drv {
Ok(drv) => {
match &drv {
VirtioDriver::Network(_) => {
info!("Install virtio interrupt handler at line {}", adapter.irq);
irq_install_handler(adapter.irq as u32, network_irqhandler as usize);
add_irq_name(adapter.irq as u32, "virtio_net");
Ok(drv)
}
VirtioDriver::FileSystem => Ok(drv),
}
}
Err(virt_err) => Err(virt_err),
}
}
pub enum VirtioDriver {
Network(VirtioNetDriver),
FileSystem,
}
#[allow(dead_code)]
pub mod constants {
pub const PCI_MAX_BUS_NUMBER: u8 = 32;
pub const PCI_MAX_DEVICE_NUMBER: u8 = 32;
pub const PCI_CONFIG_ADDRESS_PORT: u16 = 0xCF8;
pub const PCI_CONFIG_ADDRESS_ENABLE: u32 = 1 << 31;
pub const PCI_CONFIG_DATA_PORT: u16 = 0xCFC;
pub const PCI_CAP_ID_VNDR_VIRTIO: u32 = 0x09;
pub const PCI_MASK_IS_DEV_BUS_MASTER: u32 = 0x0000_0004u32;
#[allow(dead_code, non_camel_case_types)]
#[repr(u32)]
pub enum RegisterHeader00H {
PCI_ID_REGISTER = 0x00u32,
PCI_COMMAND_REGISTER = 0x04u32,
PCI_CLASS_REGISTER = 0x08u32,
PCI_HEADER_REGISTER = 0x0Cu32,
PCI_BAR0_REGISTER = 0x10u32,
PCI_CAPABILITY_LIST_REGISTER = 0x34u32,
PCI_INTERRUPT_REGISTER = 0x3Cu32,
}
impl From<RegisterHeader00H> for u32 {
fn from(val: RegisterHeader00H) -> u32 {
match val {
RegisterHeader00H::PCI_ID_REGISTER => 0x00u32,
RegisterHeader00H::PCI_COMMAND_REGISTER => 0x04u32,
RegisterHeader00H::PCI_CLASS_REGISTER => 0x08u32,
RegisterHeader00H::PCI_HEADER_REGISTER => 0x0Cu32,
RegisterHeader00H::PCI_BAR0_REGISTER => 0x10u32,
RegisterHeader00H::PCI_CAPABILITY_LIST_REGISTER => 0x34u32,
RegisterHeader00H::PCI_INTERRUPT_REGISTER => 0x3Cu32,
}
}
}
#[allow(dead_code, non_camel_case_types)]
#[repr(u32)]
pub enum Masks {
PCI_MASK_IS_BAR_IO_BAR = 0x0000_0001u32,
PCI_MASK_IS_MEM_BASE_ADDRESS_64BIT = 0x0000_0004u32,
PCI_MASK_IS_MEM_BAR_PREFETCHABLE = 0x0000_0008u32,
PCI_MASK_STATUS_CAPABILITIES_LIST = 0x0000_0010u32,
PCI_MASK_CAPLIST_POINTER = 0x0000_00FCu32,
PCI_MASK_HEADER_TYPE = 0x007F_0000u32,
PCI_MASK_MULTIFUNCTION = 0x0080_0000u32,
PCI_MASK_MEM_BASE_ADDRESS = 0xFFFF_FFF0u32,
PCI_MASK_IO_BASE_ADDRESS = 0xFFFF_FFFCu32,
}
impl From<Masks> for u32 {
fn from(val: Masks) -> u32 {
match val {
Masks::PCI_MASK_STATUS_CAPABILITIES_LIST => 0x0000_0010u32,
Masks::PCI_MASK_CAPLIST_POINTER => 0x0000_00FCu32,
Masks::PCI_MASK_HEADER_TYPE => 0x007F_0000u32,
Masks::PCI_MASK_MULTIFUNCTION => 0x0080_0000u32,
Masks::PCI_MASK_MEM_BASE_ADDRESS => 0xFFFF_FFF0u32,
Masks::PCI_MASK_IO_BASE_ADDRESS => 0xFFFF_FFFCu32,
Masks::PCI_MASK_IS_MEM_BAR_PREFETCHABLE => 0x0000_0008u32,
Masks::PCI_MASK_IS_MEM_BASE_ADDRESS_64BIT => 0x0000_0004u32,
Masks::PCI_MASK_IS_BAR_IO_BAR => 0x0000_0001u32,
}
}
}
}