#![allow(dead_code)]
use crate::arch::mm::PhysAddr;
use core::convert::TryInto;
use core::ptr::{read_volatile, write_volatile};
use core::result::Result;
use core::sync::atomic::fence;
use core::sync::atomic::Ordering;
use core::u8;
use crate::drivers::error::DriverError;
use crate::drivers::net::virtio_net::VirtioNetDriver;
use crate::drivers::virtio::device;
use crate::drivers::virtio::error::VirtioError;
use crate::arch::x86_64::kernel::irq::*;
use crate::drivers::net::network_irqhandler;
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[repr(u32)]
pub enum DevId {
INVALID = 0x0,
VIRTIO_DEV_ID_NET = 1,
VIRTIO_DEV_ID_BLK = 2,
VIRTIO_DEV_ID_CONSOLE = 3,
}
impl From<DevId> for u32 {
fn from(val: DevId) -> u32 {
match val {
DevId::VIRTIO_DEV_ID_NET => 1,
DevId::VIRTIO_DEV_ID_BLK => 2,
DevId::VIRTIO_DEV_ID_CONSOLE => 3,
DevId::INVALID => 0x0,
}
}
}
impl From<u32> for DevId {
fn from(val: u32) -> Self {
match val {
1 => DevId::VIRTIO_DEV_ID_NET,
2 => DevId::VIRTIO_DEV_ID_BLK,
3 => DevId::VIRTIO_DEV_ID_CONSOLE,
_ => DevId::INVALID,
}
}
}
pub struct VqCfgHandler<'a> {
vq_index: u32,
raw: &'a mut MmioRegisterLayout,
}
impl<'a> VqCfgHandler<'a> {
pub fn set_vq_size(&mut self, size: u16) -> u16 {
self.raw
.set_queue_size(self.vq_index, size as u32)
.try_into()
.unwrap()
}
pub fn set_ring_addr(&mut self, addr: PhysAddr) {
self.raw.set_ring_addr(self.vq_index, addr);
}
pub fn set_drv_ctrl_addr(&mut self, addr: PhysAddr) {
self.raw.set_drv_ctrl_addr(self.vq_index, addr);
}
pub fn set_dev_ctrl_addr(&mut self, addr: PhysAddr) {
self.raw.set_dev_ctrl_addr(self.vq_index, addr);
}
pub fn notif_off(&mut self) -> u16 {
0
}
pub fn enable_queue(&mut self) {
self.raw.enable_queue(self.vq_index);
}
}
pub struct ComCfg {
com_cfg: &'static mut MmioRegisterLayout,
rank: u8,
}
impl ComCfg {
pub fn new(raw: &'static mut MmioRegisterLayout, rank: u8) -> Self {
ComCfg { com_cfg: raw, rank }
}
pub fn select_vq(&mut self, index: u16) -> Option<VqCfgHandler<'_>> {
if self.com_cfg.get_max_queue_size(u32::from(index)) == 0 {
None
} else {
Some(VqCfgHandler {
vq_index: index as u32,
raw: self.com_cfg,
})
}
}
pub fn get_max_queue_size(&mut self, sel: u32) -> u32 {
self.com_cfg.get_max_queue_size(sel)
}
pub fn is_queue_ready(&mut self, sel: u32) -> bool {
self.com_cfg.is_queue_ready(sel)
}
pub fn dev_status(&self) -> u8 {
unsafe { read_volatile(&self.com_cfg.status).try_into().unwrap() }
}
pub fn reset_dev(&mut self) {
unsafe {
write_volatile(&mut self.com_cfg.status, 0u32);
}
}
pub fn set_failed(&mut self) {
unsafe {
write_volatile(&mut self.com_cfg.status, u32::from(device::Status::FAILED));
}
}
pub fn ack_dev(&mut self) {
unsafe {
let status = read_volatile(&self.com_cfg.status);
write_volatile(
&mut self.com_cfg.status,
status | u32::from(device::Status::ACKNOWLEDGE),
);
}
}
pub fn set_drv(&mut self) {
unsafe {
let status = read_volatile(&self.com_cfg.status);
write_volatile(
&mut self.com_cfg.status,
status | u32::from(device::Status::DRIVER),
);
}
}
pub fn features_ok(&mut self) {
unsafe {
let status = read_volatile(&self.com_cfg.status);
write_volatile(
&mut self.com_cfg.status,
status | u32::from(device::Status::FEATURES_OK),
);
}
}
pub fn check_features(&self) -> bool {
unsafe {
let status = read_volatile(&self.com_cfg.status);
status & u32::from(device::Status::FEATURES_OK)
== u32::from(device::Status::FEATURES_OK)
}
}
pub fn drv_ok(&mut self) {
unsafe {
let status = read_volatile(&self.com_cfg.status);
write_volatile(
&mut self.com_cfg.status,
status | u32::from(device::Status::DRIVER_OK),
);
}
}
pub fn dev_features(&mut self) -> u64 {
self.com_cfg.dev_features()
}
pub fn set_drv_features(&mut self, feats: u64) {
self.com_cfg.set_drv_features(feats);
}
pub fn print_information(&mut self) {
self.com_cfg.print_information();
}
}
pub struct NotifCfg {
queue_notify: *mut u32,
}
impl NotifCfg {
pub fn new(registers: &mut MmioRegisterLayout) -> Self {
let raw = &mut registers.queue_notify as *mut u32;
NotifCfg { queue_notify: raw }
}
pub fn base(&self) -> usize {
self.queue_notify as usize
}
pub fn multiplier(&self) -> u32 {
0
}
}
pub struct NotifCtrl {
f_notif_data: bool,
notif_addr: *mut u32,
}
impl NotifCtrl {
pub fn new(notif_addr: *mut usize) -> Self {
NotifCtrl {
f_notif_data: false,
notif_addr: notif_addr as *mut u32,
}
}
pub fn enable_notif_data(&mut self) {
self.f_notif_data = true;
}
pub fn notify_dev(&self, notif_data: &[u8]) {
let data = u32::from_ne_bytes(notif_data.try_into().unwrap());
unsafe {
*self.notif_addr = data;
}
}
}
pub struct IsrStatus {
raw: &'static mut IsrStatusRaw,
}
impl IsrStatus {
pub fn new(registers: &mut MmioRegisterLayout) -> Self {
let ptr = &mut registers.interrupt_status as *mut _;
let raw: &'static mut IsrStatusRaw = unsafe { &mut *(ptr as *mut IsrStatusRaw) };
IsrStatus { raw: raw }
}
pub fn is_interrupt(&self) -> bool {
unsafe {
let status = read_volatile(&self.raw.interrupt_status);
status & 0x1 == 0x1
}
}
pub fn is_cfg_change(&self) -> bool {
unsafe {
let status = read_volatile(&self.raw.interrupt_status);
status & 0x2 == 0x2
}
}
pub fn acknowledge(&mut self) {
unsafe {
let status = read_volatile(&self.raw.interrupt_status);
write_volatile(&mut self.raw.interrupt_ack, status);
}
}
}
#[repr(C)]
struct IsrStatusRaw {
interrupt_status: u32,
interrupt_ack: u32,
}
pub enum VirtioDriver {
Network(VirtioNetDriver),
}
pub fn init_device(
registers: &'static mut MmioRegisterLayout,
irq_no: u32,
) -> Result<VirtioDriver, DriverError> {
let dev_id: u16 = 0;
if registers.version == 0x1 {
error!("Legacy interface isn't supported!");
return Err(DriverError::InitVirtioDevFail(
VirtioError::DevNotSupported(dev_id),
));
}
match registers.device_id {
DevId::VIRTIO_DEV_ID_NET => {
match VirtioNetDriver::init(dev_id, registers, irq_no) {
Ok(virt_net_drv) => {
info!("Virtio network driver initialized.");
irq_install_handler(irq_no, network_irqhandler as usize);
add_irq_name(irq_no, "virtio_net");
Ok(VirtioDriver::Network(virt_net_drv))
}
Err(virtio_error) => {
error!("Virtio network driver could not be initialized with device");
Err(DriverError::InitVirtioDevFail(virtio_error))
}
}
}
_ => {
error!(
"Device with id {:?} is currently not supported!",
registers.device_id
);
Err(DriverError::InitVirtioDevFail(
VirtioError::DevNotSupported(dev_id),
))
}
}
}
#[repr(C, align(4))]
pub struct MmioRegisterLayout {
magic_value: u32,
version: u32,
device_id: DevId,
vendor_id: u32,
device_features: u32,
device_features_sel: u32,
_reserved0: [u32; 2],
driver_features: u32,
driver_features_sel: u32,
guest_page_size: u32, _reserved1: u32,
queue_sel: u32,
queue_num_max: u32,
queue_num: u32,
queue_align: u32, queue_pfn: u32, queue_ready: u32, _reserved2: [u32; 2],
queue_notify: u32,
_reserved3: [u32; 3],
interrupt_status: u32,
interrupt_ack: u32,
_reserved4: [u32; 2],
status: u32,
_reserved5: [u32; 3],
queue_desc_low: u32, queue_desc_high: u32, _reserved6: [u32; 2],
queue_driver_low: u32, queue_driver_high: u32, _reserved7: [u32; 2],
queue_device_low: u32, queue_device_high: u32, _reserved8: [u32; 21],
config_generation: u32, config: [u32; 3],
}
impl MmioRegisterLayout {
pub fn get_magic_value(&self) -> u32 {
unsafe { read_volatile(&self.magic_value) }
}
pub fn get_version(&self) -> u32 {
unsafe { read_volatile(&self.version) }
}
pub fn get_device_id(&self) -> DevId {
unsafe { read_volatile(&self.device_id) }
}
pub fn enable_queue(&mut self, sel: u32) {
unsafe {
write_volatile(&mut self.queue_sel, sel);
write_volatile(&mut self.queue_ready, 1u32);
}
}
pub fn get_max_queue_size(&mut self, sel: u32) -> u32 {
unsafe {
write_volatile(&mut self.queue_sel, sel);
read_volatile(&self.queue_num_max)
}
}
pub fn set_queue_size(&mut self, sel: u32, size: u32) -> u32 {
unsafe {
write_volatile(&mut self.queue_sel, sel);
let num_max = read_volatile(&self.queue_num_max);
if num_max >= size {
write_volatile(&mut self.queue_num, size);
size
} else {
write_volatile(&mut self.queue_num, num_max);
num_max
}
}
}
pub fn set_ring_addr(&mut self, sel: u32, addr: PhysAddr) {
unsafe {
write_volatile(&mut self.queue_sel, sel);
write_volatile(&mut self.queue_desc_low, addr.as_u64() as u32);
write_volatile(&mut self.queue_desc_high, (addr.as_u64() >> 32) as u32);
}
}
pub fn set_drv_ctrl_addr(&mut self, sel: u32, addr: PhysAddr) {
unsafe {
write_volatile(&mut self.queue_sel, sel);
write_volatile(&mut self.queue_driver_low, addr.as_u64() as u32);
write_volatile(&mut self.queue_driver_high, (addr.as_u64() >> 32) as u32);
}
}
pub fn set_dev_ctrl_addr(&mut self, sel: u32, addr: PhysAddr) {
unsafe {
write_volatile(&mut self.queue_sel, sel);
write_volatile(&mut self.queue_device_low, addr.as_u64() as u32);
write_volatile(&mut self.queue_device_high, (addr.as_u64() >> 32) as u32);
}
}
pub fn is_queue_ready(&mut self, sel: u32) -> bool {
unsafe {
write_volatile(&mut self.queue_sel, sel);
read_volatile(&self.queue_ready) != 0
}
}
pub fn dev_features(&mut self) -> u64 {
unsafe {
write_volatile(&mut self.device_features_sel, 1u32);
let mut dev_feat = u64::from(read_volatile(&self.device_features)) << 32;
write_volatile(&mut self.device_features_sel, 0u32);
dev_feat |= u64::from(read_volatile(&self.device_features));
dev_feat
}
}
pub fn set_drv_features(&mut self, feats: u64) {
let high: u32 = (feats >> 32) as u32;
let low: u32 = feats as u32;
unsafe {
write_volatile(&mut self.driver_features_sel, 0u32);
write_volatile(&mut self.driver_features, low);
write_volatile(&mut self.driver_features_sel, 1u32);
write_volatile(&mut self.driver_features, high);
}
}
pub fn get_config(&mut self) -> [u32; 3] {
unsafe {
loop {
let before = read_volatile(&self.config_generation);
fence(Ordering::SeqCst);
let config = read_volatile(&self.config);
fence(Ordering::SeqCst);
let after = read_volatile(&self.config_generation);
fence(Ordering::SeqCst);
if before == after {
return config;
}
}
}
}
pub fn print_information(&mut self) {
infoheader!(" MMIO RREGISTER LAYOUT INFORMATION ");
infoentry!("Device version", "{:#X}", self.get_version());
infoentry!("Device ID", "{:?}", unsafe {
read_volatile(&self.device_id)
});
infoentry!("Vendor ID", "{:#X}", unsafe {
read_volatile(&self.vendor_id)
});
infoentry!("Device Features", "{:#X}", self.dev_features());
infoentry!("Interrupt status", "{:#X}", unsafe {
read_volatile(&self.interrupt_status)
});
infoentry!("Device status", "{:#X}", unsafe {
read_volatile(&self.status)
});
infoentry!("Configuration space", "{:#X?}", self.get_config());
infofooter!();
}
}