#![allow(unused)]
use crate::arch::x86_64::kernel::pci;
use crate::arch::x86_64::kernel::virtio::{
self, consts::*, virtio_pci_common_cfg, VirtioNotification, Virtq,
};
use crate::arch::x86_64::mm::paging::{BasePageSize, PageSize};
use crate::arch::x86_64::mm::{paging, virtualmem};
use crate::drivers::net::netwakeup;
use crate::synch::spinlock::SpinlockIrqSave;
use crate::x86::io::*;
use alloc::rc::Rc;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::convert::TryInto;
use core::sync::atomic::{fence, Ordering};
use core::{fmt, mem, slice, u32, u8};
const VIRTIO_NET_F_CSUM: u32 = 0;
const VIRTIO_NET_F_GUEST_CSUM: u32 = 1;
const VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: u32 = 2;
const VIRTIO_NET_F_MTU: u32 = 3;
const VIRTIO_NET_F_MAC: u32 = 5;
const VIRTIO_NET_F_GUEST_TSO4: u32 = 7;
const VIRTIO_NET_F_GUEST_TSO6: u32 = 8;
const VIRTIO_NET_F_GUEST_ECN: u32 = 9;
const VIRTIO_NET_F_GUEST_UFO: u32 = 10;
const VIRTIO_NET_F_HOST_TSO4: u32 = 11;
const VIRTIO_NET_F_HOST_TSO6: u32 = 12;
const VIRTIO_NET_F_HOST_ECN: u32 = 13;
const VIRTIO_NET_F_HOST_UFO: u32 = 14;
const VIRTIO_NET_F_MRG_RXBUF: u32 = 15;
const VIRTIO_NET_F_STATUS: u32 = 16;
const VIRTIO_NET_F_CTRL_VQ: u32 = 17;
const VIRTIO_NET_F_CTRL_RX: u32 = 18;
const VIRTIO_NET_F_CTRL_VLAN: u32 = 19;
const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20;
const VIRTIO_NET_F_GUEST_ANNOUNCE: u32 = 21;
const VIRTIO_NET_F_MQ: u32 = 22;
const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23;
const VIRTIO_NET_F_GSO: u32 = 6;
const VIRTIO_NET_S_LINK_UP: u16 = 1;
const VIRTIO_NET_S_ANNOUNCE: u16 = 2;
const VIRTIO_NET_HDR_F_NEEDS_CSUM: u8 = 1;
const VIRTIO_NET_HDR_F_DATA_VALID: u8 = 2;
const VIRTIO_NET_HDR_GSO_NONE: u8 = 0;
const VIRTIO_NET_HDR_GSO_TCPV4: u8 = 1;
const VIRTIO_NET_HDR_GSO_UDP: u8 = 3;
const VIRTIO_NET_HDR_GSO_TCPV6: u8 = 4;
const VIRTIO_NET_HDR_GSO_ECN: u8 = 0x80;
#[repr(C)]
struct virtio_net_config {
mac: [u8; 6],
status: u16,
max_virtqueue_pairs: u16,
mtu: u16,
}
impl fmt::Debug for virtio_net_config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "virtio_net_config {{ ")?;
write!(
f,
"mac: {:x}:{:x}:{:x}:{:x}:{:x}:{:x}, ",
self.mac[0], self.mac[1], self.mac[2], self.mac[3], self.mac[4], self.mac[5]
)?;
write!(f, "max_virtqueue_pairs: {}, ", self.max_virtqueue_pairs)?;
write!(f, "mtu: {} ", self.mtu);
write!(f, "}}")
}
}
#[derive(Debug, Default)]
#[repr(C)]
struct virtio_net_hdr_legacy {
flags: u8,
gso_type: u8,
hdr_len: u16,
gso_size: u16,
csum_start: u16,
csum_offset: u16,
num_buffers: u16,
}
impl virtio_net_hdr_legacy {
pub fn init(&mut self, len: usize) {
self.flags = 0;
self.gso_type = VIRTIO_NET_HDR_GSO_NONE;
self.hdr_len = 0;
self.gso_size = 0;
self.csum_start = 0;
self.csum_offset = 0;
self.num_buffers = 0;
}
}
#[derive(Debug, Default)]
#[repr(C)]
struct virtio_net_hdr {
flags: u8,
gso_type: u8,
hdr_len: u16,
gso_size: u16,
csum_start: u16,
csum_offset: u16,
}
impl virtio_net_hdr {
pub fn init(&mut self, len: usize) {
self.flags = 0;
self.gso_type = VIRTIO_NET_HDR_GSO_NONE;
self.hdr_len = 0;
self.gso_size = 0;
self.csum_start = 0;
self.csum_offset = 0;
}
}
#[derive(Debug)]
struct RxBuffer {
pub addr: usize,
pub len: usize,
}
impl RxBuffer {
pub fn new(len: usize) -> Self {
let sz = align_up!(len, BasePageSize::SIZE);
let addr = crate::mm::allocate(sz, true);
Self { addr, len: sz }
}
}
impl Drop for RxBuffer {
fn drop(&mut self) {
crate::mm::deallocate(self.addr, self.len);
}
}
#[derive(Debug)]
struct TxBuffer {
pub addr: usize,
pub len: usize,
pub in_use: bool,
}
impl TxBuffer {
pub fn new(len: usize) -> Self {
let sz = align_up!(len + mem::size_of::<virtio_net_hdr>(), BasePageSize::SIZE);
let addr = crate::mm::allocate(sz, true);
Self {
addr,
len: sz,
in_use: false,
}
}
}
impl Drop for TxBuffer {
fn drop(&mut self) {
crate::mm::deallocate(self.addr, self.len);
}
}
pub struct VirtioNetDriver<'a> {
tx_buffers: Vec<TxBuffer>,
rx_buffers: Vec<RxBuffer>,
common_cfg: &'a mut virtio_pci_common_cfg,
device_cfg: &'a virtio_net_config,
isr_cfg: &'a mut u32,
notify_cfg: VirtioNotification,
vqueues: Option<Vec<Virtq<'a>>>,
}
impl<'a> fmt::Debug for VirtioNetDriver<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VirtioNetDriver {{ ")?;
write!(f, "common_cfg: {:?}, ", self.common_cfg)?;
write!(f, "device_cfg: {:?}, ", self.device_cfg)?;
write!(f, "isr_cfg: 0x{:x}, ", self.isr_cfg)?;
write!(f, "nofity_cfg: {:?}, ", self.notify_cfg)?;
match &self.vqueues {
None => write!(f, "Uninitialized VQs")?,
Some(vqs) => write!(f, "Initialized {} VQs", vqs.len())?,
}
write!(f, "}}")
}
}
impl<'a> VirtioNetDriver<'a> {
pub fn init_vqs(&mut self) {
let common_cfg = &mut self.common_cfg;
let device_cfg = &self.device_cfg;
let notify_cfg = &mut self.notify_cfg;
debug!("Setting up virtqueues...");
let vqnum = 2;
let mut vqueues = Vec::<Virtq>::new();
for i in 0..vqnum as u16 {
let vq = Virtq::new_from_common(i, common_cfg, notify_cfg).unwrap();
vqueues.push(vq);
}
let vqsize = common_cfg.queue_size as usize;
{
let buffer_size: usize = 65562;
let mut vec_buffer = &mut self.rx_buffers;
for i in 0..vqsize {
let buffer = RxBuffer::new(buffer_size);
let addr = buffer.addr;
vqueues[0].add_buffer(i, addr.try_into().unwrap(), buffer_size, VIRTQ_DESC_F_WRITE);
vec_buffer.push(buffer);
}
}
{
let buffer_size: usize = self.get_mtu() as usize;
let mut vec_buffer = &mut self.tx_buffers;
for i in 0..vqsize {
let buffer = TxBuffer::new(buffer_size);
let addr = buffer.addr;
vqueues[1].add_buffer(i, addr.try_into().unwrap(), buffer_size, 0);
vec_buffer.push(buffer);
}
}
self.vqueues = Some(vqueues);
}
pub fn negotiate_features(&mut self) {
let common_cfg = &mut self.common_cfg;
common_cfg.device_feature_select = 0;
let mut device_features: u64 = common_cfg.device_feature as u64;
common_cfg.device_feature_select = 1;
device_features |= (common_cfg.device_feature as u64) << 32;
let required: u64 = ((1 << VIRTIO_NET_F_MAC)
| (1 << VIRTIO_NET_F_STATUS)
| (1 << VIRTIO_NET_F_GUEST_UFO)
| (1 << VIRTIO_NET_F_GUEST_TSO4)
| (1 << VIRTIO_NET_F_GUEST_TSO6)
| (1 << VIRTIO_NET_F_GUEST_CSUM)) as u64;
if device_features & required == required {
common_cfg.driver_feature_select = 1;
common_cfg.driver_feature |= required as u32;
} else {
error!("Device doesn't offer required feature to support Virtio-Net");
}
}
pub fn init(&mut self) {
self.common_cfg.device_status = 0;
self.common_cfg.device_status |= 1;
self.common_cfg.device_status |= 2;
self.negotiate_features();
self.common_cfg.device_status |= 8;
if self.common_cfg.device_status & 8 == 0 {
error!("Device unset FEATURES_OK, aborting!");
return;
}
self.init_vqs();
self.common_cfg.device_status |= 4;
}
pub fn check_used_elements(&mut self) {
let mut buffers = &mut self.tx_buffers;
while let Some(idx) = (self.vqueues.as_deref_mut().unwrap())[1].check_used_elements() {
buffers[idx as usize].in_use = false;
}
fence(Ordering::SeqCst);
}
pub fn handle_interrupt(&mut self) -> bool {
let isr_status = *(self.isr_cfg);
if (isr_status & 0x1) == 0x1 {
if self.has_packet() {
netwakeup();
return true;
}
}
false
}
pub fn get_mac_address(&self) -> [u8; 6] {
self.device_cfg.mac
}
pub fn get_mtu(&self) -> u16 {
1500
}
pub fn get_tx_buffer(&mut self, len: usize) -> Result<(*mut u8, usize), ()> {
let mut buffers = &mut self.tx_buffers;
if buffers.iter().position(|b| !b.in_use).is_none() {
self.check_used_elements();
}
let index = (self.vqueues.as_ref().unwrap())[1].get_available_buffer()?;
let index = index as usize;
let mut buffers = &mut self.tx_buffers;
if !buffers[index].in_use {
buffers[index].in_use = true;
let header = buffers[index].addr as *mut virtio_net_hdr;
unsafe {
(*header).init(len);
}
Ok((
(buffers[index].addr + mem::size_of::<virtio_net_hdr>()) as *mut u8,
index,
))
} else {
Err(())
}
}
pub fn send_tx_buffer(&mut self, index: usize, len: usize) -> Result<(), ()> {
(self.vqueues.as_deref_mut().unwrap())[1]
.send_non_blocking(index, len + mem::size_of::<virtio_net_hdr>())
}
pub fn has_packet(&self) -> bool {
(self.vqueues.as_ref().unwrap())[0].has_packet()
}
pub fn receive_rx_buffer(&self) -> Result<&'static [u8], ()> {
let (idx, len) = (self.vqueues.as_ref().unwrap())[0].get_used_buffer()?;
let addr = self.rx_buffers[idx as usize].addr;
let virtio_net_hdr = unsafe { &*(addr as *const virtio_net_hdr) };
let rx_buffer_slice = unsafe {
slice::from_raw_parts(
(addr + mem::size_of::<virtio_net_hdr>()) as *const u8,
len as usize,
)
};
Ok(rx_buffer_slice)
}
pub fn rx_buffer_consumed(&mut self) {
(self.vqueues.as_deref_mut().unwrap())[0].buffer_consumed();
}
}
pub fn create_virtionet_driver(
adapter: &pci::PciAdapter,
) -> Option<Rc<RefCell<VirtioNetDriver<'static>>>> {
let bus = adapter.bus;
let device = adapter.device;
let status = pci::read_config(bus, device, pci::PCI_COMMAND_REGISTER) >> 16;
if status & pci::PCI_STATUS_CAPABILITIES_LIST == 0 {
error!("Found virtio device without capability list. Likely legacy-device! Aborting.");
return None;
}
let caplist = pci::read_config(bus, device, pci::PCI_CAPABILITY_LIST_REGISTER) & 0xFF;
let common_cfg =
match virtio::map_virtiocap(bus, device, adapter, caplist, VIRTIO_PCI_CAP_COMMON_CFG) {
Some((cap_common_raw, _)) => unsafe {
&mut *(cap_common_raw as *mut virtio_pci_common_cfg)
},
None => {
error!("Could not find VIRTIO_PCI_CAP_COMMON_CFG. Aborting!");
return None;
}
};
let device_cfg =
match virtio::map_virtiocap(bus, device, adapter, caplist, VIRTIO_PCI_CAP_DEVICE_CFG) {
Some((cap_device_raw, _)) => unsafe {
&mut *(cap_device_raw as *mut virtio_net_config)
},
None => {
error!("Could not find VIRTIO_PCI_CAP_DEVICE_CFG. Aborting!");
return None;
}
};
let isr_cfg = match virtio::map_virtiocap(bus, device, adapter, caplist, VIRTIO_PCI_CAP_ISR_CFG)
{
Some((cap_isr_raw, _)) => unsafe { &mut *(cap_isr_raw as *mut u32) },
None => {
error!("Could not find VIRTIO_PCI_CAP_ISR_CFG. Aborting!");
return None;
}
};
let (notification_ptr, notify_off_multiplier) =
match virtio::map_virtiocap(bus, device, adapter, caplist, VIRTIO_PCI_CAP_NOTIFY_CFG) {
Some((cap_notification_raw, notify_off_multiplier)) => {
(
cap_notification_raw as *mut u16,
notify_off_multiplier,
)
}
None => {
error!("Could not find VIRTIO_PCI_CAP_NOTIFY_CFG. Aborting!");
return None;
}
};
let notify_cfg = VirtioNotification {
notification_ptr,
notify_off_multiplier,
};
let drv = Rc::new(RefCell::new(VirtioNetDriver {
tx_buffers: Vec::new(),
rx_buffers: Vec::new(),
common_cfg,
device_cfg,
isr_cfg,
notify_cfg,
vqueues: None,
}));
trace!("Driver before init: {:?}", drv);
drv.borrow_mut().init();
trace!("Driver after init: {:?}", drv);
if device_cfg.status & VIRTIO_NET_S_LINK_UP == VIRTIO_NET_S_LINK_UP {
info!("Virtio-Net link is up");
} else {
info!("Virtio-Net link is down");
}
info!(
"Virtio-Net status: 0x{:x}",
drv.borrow().common_cfg.device_status
);
Some(drv)
}