use alloc::collections::VecDeque;
use alloc::rc::Rc;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::cmp::Ordering;
use core::mem;
use core::result::Result;
use pci_types::InterruptLine;
use zerocopy::AsBytes;
use self::constants::{FeatureSet, Features, NetHdrFlag, NetHdrGSO, Status, MAX_NUM_VQ};
use self::error::VirtioNetError;
use crate::arch::kernel::core_local::increment_irq_counter;
use crate::config::VIRTIO_MAX_QUEUE_SIZE;
#[cfg(not(feature = "pci"))]
use crate::drivers::net::virtio_mmio::NetDevCfgRaw;
#[cfg(feature = "pci")]
use crate::drivers::net::virtio_pci::NetDevCfgRaw;
use crate::drivers::net::NetworkDriver;
#[cfg(not(feature = "pci"))]
use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
#[cfg(feature = "pci")]
use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg};
use crate::drivers::virtio::virtqueue::{
BuffSpec, BufferToken, Bytes, Transfer, Virtq, VqIndex, VqSize, VqType,
};
use crate::executor::device::{RxToken, TxToken};
pub const ETH_HDR: usize = 14usize;
pub(crate) struct NetDevCfg {
pub raw: &'static NetDevCfgRaw,
pub dev_id: u16,
pub features: FeatureSet,
}
#[derive(AsBytes, Debug)]
#[repr(C)]
pub struct VirtioNetHdr {
flags: NetHdrFlag,
gso_type: NetHdrGSO,
hdr_len: u16,
gso_size: u16,
csum_start: u16,
csum_offset: u16,
num_buffers: u16,
}
impl Default for VirtioNetHdr {
fn default() -> Self {
Self {
flags: NetHdrFlag::VIRTIO_NET_HDR_F_NONE,
gso_type: NetHdrGSO::VIRTIO_NET_HDR_GSO_NONE,
hdr_len: 0,
gso_size: 0,
csum_start: 0,
csum_offset: 0,
num_buffers: 0,
}
}
}
pub struct CtrlQueue(Option<Rc<Virtq>>);
impl CtrlQueue {
pub fn new(vq: Option<Rc<Virtq>>) -> Self {
CtrlQueue(vq)
}
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum CtrlClass {
VIRTIO_NET_CTRL_RX = 1 << 0,
VIRTIO_NET_CTRL_MAC = 1 << 1,
VIRTIO_NET_CTRL_VLAN = 1 << 2,
VIRTIO_NET_CTRL_ANNOUNCE = 1 << 3,
VIRTIO_NET_CTRL_MQ = 1 << 4,
}
impl From<CtrlClass> for u8 {
fn from(val: CtrlClass) -> Self {
match val {
CtrlClass::VIRTIO_NET_CTRL_RX => 1 << 0,
CtrlClass::VIRTIO_NET_CTRL_MAC => 1 << 1,
CtrlClass::VIRTIO_NET_CTRL_VLAN => 1 << 2,
CtrlClass::VIRTIO_NET_CTRL_ANNOUNCE => 1 << 3,
CtrlClass::VIRTIO_NET_CTRL_MQ => 1 << 4,
}
}
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum RxCmd {
VIRTIO_NET_CTRL_RX_PROMISC = 1 << 0,
VIRTIO_NET_CTRL_RX_ALLMULTI = 1 << 1,
VIRTIO_NET_CTRL_RX_ALLUNI = 1 << 2,
VIRTIO_NET_CTRL_RX_NOMULTI = 1 << 3,
VIRTIO_NET_CTRL_RX_NOUNI = 1 << 4,
VIRTIO_NET_CTRL_RX_NOBCAST = 1 << 5,
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum MacCmd {
VIRTIO_NET_CTRL_MAC_TABLE_SET = 1 << 0,
VIRTIO_NET_CTRL_MAC_ADDR_SET = 1 << 1,
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum VlanCmd {
VIRTIO_NET_CTRL_VLAN_ADD = 1 << 0,
VIRTIO_NET_CTRL_VLAN_DEL = 1 << 1,
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum AnceCmd {
VIRTIO_NET_CTRL_ANNOUNCE_ACK = 1 << 0,
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum MqCmd {
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET = 1 << 0,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN = 1 << 1,
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX = 0x80,
}
pub struct RxQueues {
vqs: Vec<Rc<Virtq>>,
poll_queue: Rc<RefCell<VecDeque<Transfer>>>,
is_multi: bool,
}
impl RxQueues {
pub fn new(
vqs: Vec<Rc<Virtq>>,
poll_queue: Rc<RefCell<VecDeque<Transfer>>>,
is_multi: bool,
) -> Self {
Self {
vqs,
poll_queue,
is_multi,
}
}
fn post_processing(transfer: Transfer) -> Result<Transfer, VirtioNetError> {
if transfer.poll() {
Ok(transfer)
} else {
warn!("Unfinished transfer in post processing. Returning buffer to queue. This will need explicit cleanup.");
transfer.close();
Err(VirtioNetError::ProcessOngoing)
}
}
fn add(&mut self, vq: Virtq, dev_cfg: &NetDevCfg) {
let rc_vq = Rc::new(vq);
let vq = &rc_vq;
if dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_TSO4)
| dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_TSO6)
| dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_UFO)
{
let buff_def = [
Bytes::new(mem::size_of::<VirtioNetHdr>()).unwrap(),
Bytes::new(65550 + ETH_HDR).unwrap(),
];
let spec = if dev_cfg
.features
.is_feature(Features::VIRTIO_F_RING_INDIRECT_DESC)
{
BuffSpec::Indirect(&buff_def)
} else {
BuffSpec::Single(
Bytes::new(mem::size_of::<VirtioNetHdr>() + 65550 + ETH_HDR).unwrap(),
)
};
let num_buff: u16 = vq.size().into();
for _ in 0..num_buff {
let buff_tkn = match vq.prep_buffer(Rc::clone(vq), None, Some(spec.clone())) {
Ok(tkn) => tkn,
Err(_vq_err) => {
error!("Setup of network queue failed, which should not happen!");
panic!("setup of network queue failed!");
}
};
buff_tkn
.provide()
.dispatch_await(Rc::clone(&self.poll_queue), false);
}
} else {
let buff_def = [
Bytes::new(mem::size_of::<VirtioNetHdr>()).unwrap(),
Bytes::new((dev_cfg.raw.get_mtu() as usize) + ETH_HDR).unwrap(),
];
let spec = if dev_cfg
.features
.is_feature(Features::VIRTIO_F_RING_INDIRECT_DESC)
{
BuffSpec::Indirect(&buff_def)
} else {
BuffSpec::Single(
Bytes::new(
mem::size_of::<VirtioNetHdr>() + dev_cfg.raw.get_mtu() as usize + ETH_HDR,
)
.unwrap(),
)
};
let num_buff: u16 = vq.size().into();
for _ in 0..num_buff {
let buff_tkn = match vq.prep_buffer(Rc::clone(vq), None, Some(spec.clone())) {
Ok(tkn) => tkn,
Err(_vq_err) => {
error!("Setup of network queue failed, which should not happen!");
panic!("setup of network queue failed!");
}
};
buff_tkn
.provide()
.dispatch_await(Rc::clone(&self.poll_queue), false);
}
}
self.vqs.push(rc_vq);
if self.vqs.len() > 1 {
self.is_multi = true;
}
}
fn get_next(&mut self) -> Option<Transfer> {
let transfer = self.poll_queue.borrow_mut().pop_front();
transfer.or_else(|| {
self.poll();
self.poll_queue.borrow_mut().pop_front()
})
}
fn poll(&self) {
if self.is_multi {
for vq in &self.vqs {
vq.poll();
}
} else {
self.vqs[0].poll();
}
}
fn enable_notifs(&self) {
if self.is_multi {
for vq in &self.vqs {
vq.enable_notifs();
}
} else {
self.vqs[0].enable_notifs();
}
}
fn disable_notifs(&self) {
if self.is_multi {
for vq in &self.vqs {
vq.disable_notifs();
}
} else {
self.vqs[0].disable_notifs();
}
}
}
pub struct TxQueues {
vqs: Vec<Rc<Virtq>>,
poll_queue: Rc<RefCell<VecDeque<Transfer>>>,
ready_queue: Vec<BufferToken>,
is_multi: bool,
}
impl TxQueues {
pub fn new(
vqs: Vec<Rc<Virtq>>,
poll_queue: Rc<RefCell<VecDeque<Transfer>>>,
ready_queue: Vec<BufferToken>,
is_multi: bool,
) -> Self {
Self {
vqs,
poll_queue,
ready_queue,
is_multi,
}
}
#[allow(dead_code)]
fn enable_notifs(&self) {
if self.is_multi {
for vq in &self.vqs {
vq.enable_notifs();
}
} else {
self.vqs[0].enable_notifs();
}
}
#[allow(dead_code)]
fn disable_notifs(&self) {
if self.is_multi {
for vq in &self.vqs {
vq.disable_notifs();
}
} else {
self.vqs[0].disable_notifs();
}
}
fn poll(&self) {
if self.is_multi {
for vq in &self.vqs {
vq.poll();
}
} else {
self.vqs[0].poll();
}
}
fn add(&mut self, vq: Virtq, dev_cfg: &NetDevCfg) {
self.vqs.push(Rc::new(vq));
if self.vqs.len() == 1 {
let vq = self.vqs.get(0).unwrap();
if dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_TSO4)
| dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_TSO6)
| dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_UFO)
{
let buff_def =
Bytes::new(mem::size_of::<VirtioNetHdr>() + 65550 + ETH_HDR).unwrap();
let spec = BuffSpec::Single(buff_def);
let num_buff: u16 = vq.size().into();
for _ in 0..num_buff {
self.ready_queue.push(
vq.prep_buffer(Rc::clone(vq), Some(spec.clone()), None)
.unwrap()
.write_seq(Some(&VirtioNetHdr::default()), None::<&VirtioNetHdr>)
.unwrap(),
)
}
} else {
let buff_def = Bytes::new(
mem::size_of::<VirtioNetHdr>() + (dev_cfg.raw.get_mtu() as usize) + ETH_HDR,
)
.unwrap();
let spec = BuffSpec::Single(buff_def);
let num_buff: u16 = vq.size().into();
for _ in 0..num_buff {
self.ready_queue.push(
vq.prep_buffer(Rc::clone(vq), Some(spec.clone()), None)
.unwrap()
.write_seq(Some(&VirtioNetHdr::default()), None::<&VirtioNetHdr>)
.unwrap(),
)
}
}
} else {
self.is_multi = true;
}
}
fn get_tkn(&mut self, len: usize) -> Option<(BufferToken, usize)> {
while let Some(mut tkn) = self.ready_queue.pop() {
let (send_len, _) = tkn.len();
match send_len.cmp(&len) {
Ordering::Less => {}
Ordering::Equal => return Some((tkn, 0)),
Ordering::Greater => {
tkn.restr_size(Some(len), None).unwrap();
return Some((tkn, 0));
}
}
}
if self.poll_queue.borrow().is_empty() {
self.poll();
}
while let Some(transfer) = self.poll_queue.borrow_mut().pop_back() {
let mut tkn = transfer.reuse().unwrap();
let (send_len, _) = tkn.len();
match send_len.cmp(&len) {
Ordering::Less => {}
Ordering::Equal => return Some((tkn, 0)),
Ordering::Greater => {
tkn.restr_size(Some(len), None).unwrap();
return Some((tkn, 0));
}
}
}
let spec = BuffSpec::Single(Bytes::new(len).unwrap());
match self.vqs[0].prep_buffer(Rc::clone(&self.vqs[0]), Some(spec), None) {
Ok(tkn) => Some((tkn, 0)),
Err(_) => {
None
}
}
}
}
pub(crate) struct VirtioNetDriver {
pub(super) dev_cfg: NetDevCfg,
pub(super) com_cfg: ComCfg,
pub(super) isr_stat: IsrStatus,
pub(super) notif_cfg: NotifCfg,
pub(super) ctrl_vq: CtrlQueue,
pub(super) recv_vqs: RxQueues,
pub(super) send_vqs: TxQueues,
pub(super) num_vqs: u16,
pub(super) irq: InterruptLine,
pub(super) mtu: u16,
}
impl NetworkDriver for VirtioNetDriver {
fn get_mac_address(&self) -> [u8; 6] {
if self.dev_cfg.features.is_feature(Features::VIRTIO_NET_F_MAC) {
self.dev_cfg.raw.get_mac()
} else {
unreachable!("Currently VIRTIO_NET_F_MAC must be negotiated!")
}
}
fn get_mtu(&self) -> u16 {
self.mtu
}
fn has_packet(&self) -> bool {
self.recv_vqs.poll();
!self.recv_vqs.poll_queue.borrow().is_empty()
}
fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
if let Some((mut buff_tkn, _vq_index)) = self
.send_vqs
.get_tkn(len + core::mem::size_of::<VirtioNetHdr>())
{
let (send_ptrs, _) = buff_tkn.raw_ptrs();
let (buff_ptr, _) = send_ptrs.unwrap()[0];
let header = unsafe { &mut *(buff_ptr as *mut VirtioNetHdr) };
*header = Default::default();
let buff_ptr = unsafe {
buff_ptr.offset(isize::try_from(core::mem::size_of::<VirtioNetHdr>()).unwrap())
};
let buf_slice: &'static mut [u8] =
unsafe { core::slice::from_raw_parts_mut(buff_ptr, len) };
let result = f(buf_slice);
if !self.with_checksums() {
let type_ = unsafe { u16::from_be(*(buff_ptr.offset(12) as *const u16)) };
match type_ {
0x0800 => {
let protocol = unsafe { *(buff_ptr.offset((14+9).try_into().unwrap()) as *const u8) };
if protocol == 6 {
header.flags = NetHdrFlag::VIRTIO_NET_HDR_F_NEEDS_CSUM;
header.csum_start = 14+20;
header.csum_offset = 16;
} else if protocol == 17 {
header.flags = NetHdrFlag::VIRTIO_NET_HDR_F_NEEDS_CSUM;
header.csum_start = 14+20;
header.csum_offset = 6;
}
},
0x86DD => {
let protocol = unsafe { *(buff_ptr.offset((14+9).try_into().unwrap()) as *const u8) };
if protocol == 6 {
header.flags = NetHdrFlag::VIRTIO_NET_HDR_F_NEEDS_CSUM;
header.csum_start = 14+40;
header.csum_offset = 16;
} else if protocol == 17 {
header.flags = NetHdrFlag::VIRTIO_NET_HDR_F_NEEDS_CSUM;
header.csum_start = 14+40;
header.csum_offset = 6;
}
},
_ => {},
}
}
buff_tkn
.provide()
.dispatch_await(Rc::clone(&self.send_vqs.poll_queue), false);
result
} else {
panic!("Unable to get token for send queue");
}
}
fn receive_packet(&mut self) -> Option<(RxToken, TxToken)> {
match self.recv_vqs.get_next() {
Some(transfer) => {
let transfer = match RxQueues::post_processing(transfer) {
Ok(trf) => trf,
Err(vnet_err) => {
warn!("Post processing failed. Err: {:?}", vnet_err);
return None;
}
};
let (_, recv_data_opt) = transfer.as_slices().unwrap();
let mut recv_data = recv_data_opt.unwrap();
if recv_data.len() == 2 {
let recv_payload = recv_data.pop().unwrap();
let vec_data = recv_payload.to_vec();
transfer
.reuse()
.unwrap()
.provide()
.dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false);
Some((RxToken::new(vec_data), TxToken::new()))
} else if recv_data.len() == 1 {
let packet = recv_data.pop().unwrap();
let vec_data = packet[mem::size_of::<VirtioNetHdr>()..].to_vec();
transfer
.reuse()
.unwrap()
.provide()
.dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false);
Some((RxToken::new(vec_data), TxToken::new()))
} else {
error!("Empty transfer, or with wrong buffer layout. Reusing and returning error to user-space network driver...");
transfer
.reuse()
.unwrap()
.write_seq(None::<&VirtioNetHdr>, Some(&VirtioNetHdr::default()))
.unwrap()
.provide()
.dispatch_await(Rc::clone(&self.recv_vqs.poll_queue), false);
None
}
}
None => None,
}
}
fn set_polling_mode(&mut self, value: bool) {
if value {
self.disable_interrupts();
} else {
self.enable_interrupts();
}
}
fn handle_interrupt(&mut self) -> bool {
increment_irq_counter(32 + self.irq);
let result = if self.isr_stat.is_interrupt() {
true
} else if self.isr_stat.is_cfg_change() {
info!("Configuration changes are not possible! Aborting");
todo!("Implement possibiity to change config on the fly...")
} else {
false
};
self.isr_stat.acknowledge();
result
}
fn with_checksums(&self) -> bool {
!self
.dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_GUEST_CSUM)
}
}
impl VirtioNetDriver {
#[cfg(feature = "pci")]
pub fn get_dev_id(&self) -> u16 {
self.dev_cfg.dev_id
}
#[cfg(feature = "pci")]
pub fn set_failed(&mut self) {
self.com_cfg.set_failed();
}
#[cfg(not(feature = "pci"))]
pub fn dev_status(&self) -> u16 {
if self
.dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_STATUS)
{
self.dev_cfg.raw.get_status()
} else {
0
}
}
#[cfg(feature = "pci")]
pub fn is_link_up(&self) -> bool {
if self
.dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_STATUS)
{
self.dev_cfg.raw.get_status() & u16::from(Status::VIRTIO_NET_S_LINK_UP)
== u16::from(Status::VIRTIO_NET_S_LINK_UP)
} else {
true
}
}
#[allow(dead_code)]
pub fn is_announce(&self) -> bool {
if self
.dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_STATUS)
{
self.dev_cfg.raw.get_status() & u16::from(Status::VIRTIO_NET_S_ANNOUNCE)
== u16::from(Status::VIRTIO_NET_S_ANNOUNCE)
} else {
false
}
}
#[allow(dead_code)]
pub fn get_max_vq_pairs(&self) -> u16 {
if self.dev_cfg.features.is_feature(Features::VIRTIO_NET_F_MQ) {
self.dev_cfg.raw.get_max_virtqueue_pairs()
} else {
1
}
}
pub fn disable_interrupts(&self) {
self.recv_vqs.disable_notifs();
}
pub fn enable_interrupts(&self) {
self.recv_vqs.enable_notifs();
}
pub fn init_dev(&mut self) -> Result<(), VirtioNetError> {
self.com_cfg.reset_dev();
self.com_cfg.ack_dev();
self.com_cfg.set_drv();
let min_feats: Vec<Features> = vec![
Features::VIRTIO_F_VERSION_1,
Features::VIRTIO_NET_F_MAC,
Features::VIRTIO_NET_F_STATUS,
];
let mut min_feat_set = FeatureSet::new(0);
min_feat_set.set_features(&min_feats);
let mut feats: Vec<Features> = min_feats;
feats.push(Features::VIRTIO_F_RING_INDIRECT_DESC);
feats.push(Features::VIRTIO_NET_F_MTU);
feats.push(Features::VIRTIO_F_RING_PACKED);
feats.push(Features::VIRTIO_NET_F_GUEST_CSUM);
match self.negotiate_features(&feats) {
Ok(_) => info!(
"Driver found a subset of features for virtio device {:x}. Features are: {:?}",
self.dev_cfg.dev_id, &feats
),
Err(vnet_err) => {
match vnet_err {
VirtioNetError::FeatReqNotMet(feat_set) => {
error!("Network drivers feature set {:x} does not satisfy rules in section 5.1.3.1 of specification v1.1. Aborting!", u64::from(feat_set));
return Err(vnet_err);
}
VirtioNetError::IncompFeatsSet(drv_feats, dev_feats) => {
if (min_feat_set & dev_feats) != min_feat_set {
error!("Device features set, does not satisfy minimal features needed. Aborting!");
return Err(VirtioNetError::FailFeatureNeg(self.dev_cfg.dev_id));
} else {
feats = match Features::from_set(dev_feats & drv_feats) {
Some(feats) => feats,
None => {
error!("Feature negotiation failed with minimal feature set. Aborting!");
return Err(VirtioNetError::FailFeatureNeg(
self.dev_cfg.dev_id,
));
}
};
match self.negotiate_features(&feats) {
Ok(_) => info!("Driver found a subset of features for virtio device {:x}. Features are: {:?}", self.dev_cfg.dev_id, &feats),
Err(vnet_err) => {
match vnet_err {
VirtioNetError::FeatReqNotMet(feat_set) => {
error!("Network device offers a feature set {:x} when used completely does not satisfy rules in section 5.1.3.1 of specification v1.1. Aborting!", u64::from(feat_set));
return Err(vnet_err);
},
_ => {
error!("Feature Set after reduction still not usable. Set: {:?}. Aborting!", feats);
return Err(vnet_err);
}
}
}
}
}
}
_ => {
error!(
"Wanted set of features is NOT supported by device. Set: {:?}",
feats
);
return Err(vnet_err);
}
}
}
}
self.com_cfg.features_ok();
if self.com_cfg.check_features() {
info!(
"Features have been negotiated between virtio network device {:x} and driver.",
self.dev_cfg.dev_id
);
self.dev_cfg.features.set_features(&feats);
} else {
return Err(VirtioNetError::FailFeatureNeg(self.dev_cfg.dev_id));
}
match self.dev_spec_init() {
Ok(_) => info!(
"Device specific initialization for Virtio network device {:x} finished",
self.dev_cfg.dev_id
),
Err(vnet_err) => return Err(vnet_err),
}
self.com_cfg.drv_ok();
Ok(())
}
fn negotiate_features(&mut self, wanted_feats: &[Features]) -> Result<(), VirtioNetError> {
let mut drv_feats = FeatureSet::new(0);
for feat in wanted_feats.iter() {
drv_feats |= *feat;
}
let dev_feats = FeatureSet::new(self.com_cfg.dev_features());
match FeatureSet::check_features(wanted_feats) {
Ok(_) => {
info!("Feature set wanted by network driver are in conformance with specification.")
}
Err(vnet_err) => return Err(vnet_err),
}
if (dev_feats & drv_feats) == drv_feats {
self.com_cfg.set_drv_features(drv_feats.into());
Ok(())
} else {
Err(VirtioNetError::IncompFeatsSet(drv_feats, dev_feats))
}
}
fn dev_spec_init(&mut self) -> Result<(), VirtioNetError> {
match self.virtqueue_init() {
Ok(_) => info!("Network driver successfully initialized virtqueues."),
Err(vnet_err) => return Err(vnet_err),
}
if self
.dev_cfg
.features
.is_feature(Features::VIRTIO_NET_F_CTRL_VQ)
{
if self
.dev_cfg
.features
.is_feature(Features::VIRTIO_F_RING_PACKED)
{
self.ctrl_vq = CtrlQueue(Some(Rc::new(Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Packed,
VqIndex::from(self.num_vqs),
self.dev_cfg.features.into(),
))));
} else {
self.ctrl_vq = CtrlQueue(Some(Rc::new(Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Split,
VqIndex::from(self.num_vqs),
self.dev_cfg.features.into(),
))));
}
self.ctrl_vq.0.as_ref().unwrap().enable_notifs();
}
if !self.dev_cfg.features.is_feature(Features::VIRTIO_NET_F_MAC) {
todo!("Driver created MAC address should be passed to device here.")
}
Ok(())
}
fn virtqueue_init(&mut self) -> Result<(), VirtioNetError> {
if self.dev_cfg.features.is_feature(Features::VIRTIO_NET_F_MQ) {
if self.dev_cfg.raw.get_max_virtqueue_pairs() * 2 >= MAX_NUM_VQ {
self.num_vqs = MAX_NUM_VQ;
} else {
self.num_vqs = self.dev_cfg.raw.get_max_virtqueue_pairs() * 2;
}
} else {
self.num_vqs = 2;
}
assert_eq!(self.num_vqs % 2, 0);
for i in 0..(self.num_vqs / 2) {
if self
.dev_cfg
.features
.is_feature(Features::VIRTIO_F_RING_PACKED)
{
let vq = Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Packed,
VqIndex::from(2 * i),
self.dev_cfg.features.into(),
);
vq.enable_notifs();
self.recv_vqs.add(vq, &self.dev_cfg);
let vq = Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Packed,
VqIndex::from(2 * i + 1),
self.dev_cfg.features.into(),
);
vq.disable_notifs();
self.send_vqs.add(vq, &self.dev_cfg);
} else {
let vq = Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Split,
VqIndex::from(2 * i),
self.dev_cfg.features.into(),
);
vq.enable_notifs();
self.recv_vqs.add(vq, &self.dev_cfg);
let vq = Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Split,
VqIndex::from(2 * i + 1),
self.dev_cfg.features.into(),
);
vq.disable_notifs();
self.send_vqs.add(vq, &self.dev_cfg);
}
}
Ok(())
}
}
pub mod constants {
use alloc::vec::Vec;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
use zerocopy::AsBytes;
pub use super::error::VirtioNetError;
pub const MAX_NUM_VQ: u16 = 2;
#[allow(dead_code, non_camel_case_types)]
#[derive(AsBytes, Copy, Clone, Debug)]
#[repr(u8)]
pub enum NetHdrFlag {
VIRTIO_NET_HDR_F_NONE = 0,
VIRTIO_NET_HDR_F_NEEDS_CSUM = 1,
VIRTIO_NET_HDR_F_DATA_VALID = 2,
VIRTIO_NET_HDR_F_RSC_INFO = 4,
}
impl From<NetHdrFlag> for u8 {
fn from(val: NetHdrFlag) -> Self {
match val {
NetHdrFlag::VIRTIO_NET_HDR_F_NONE => 0,
NetHdrFlag::VIRTIO_NET_HDR_F_NEEDS_CSUM => 1,
NetHdrFlag::VIRTIO_NET_HDR_F_DATA_VALID => 2,
NetHdrFlag::VIRTIO_NET_HDR_F_RSC_INFO => 4,
}
}
}
#[allow(dead_code, non_camel_case_types)]
#[derive(AsBytes, Copy, Clone, Debug)]
#[repr(u8)]
pub enum NetHdrGSO {
VIRTIO_NET_HDR_GSO_NONE = 0,
VIRTIO_NET_HDR_GSO_TCPV4 = 1,
VIRTIO_NET_HDR_GSO_UDP = 3,
VIRTIO_NET_HDR_GSO_TCPV6 = 4,
VIRTIO_NET_HDR_GSO_ECN = 0x80,
}
impl From<NetHdrGSO> for u8 {
fn from(val: NetHdrGSO) -> Self {
match val {
NetHdrGSO::VIRTIO_NET_HDR_GSO_NONE => 0,
NetHdrGSO::VIRTIO_NET_HDR_GSO_TCPV4 => 1,
NetHdrGSO::VIRTIO_NET_HDR_GSO_UDP => 3,
NetHdrGSO::VIRTIO_NET_HDR_GSO_TCPV6 => 4,
NetHdrGSO::VIRTIO_NET_HDR_GSO_ECN => 0x80,
}
}
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u64)]
pub enum Features {
VIRTIO_NET_F_CSUM = 1 << 0,
VIRTIO_NET_F_GUEST_CSUM = 1 << 1,
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS = 1 << 2,
VIRTIO_NET_F_MTU = 1 << 3,
VIRTIO_NET_F_MAC = 1 << 5,
VIRTIO_NET_F_GUEST_TSO4 = 1 << 7,
VIRTIO_NET_F_GUEST_TSO6 = 1 << 8,
VIRTIO_NET_F_GUEST_ECN = 1 << 9,
VIRTIO_NET_F_GUEST_UFO = 1 << 10,
VIRTIO_NET_F_HOST_TSO4 = 1 << 11,
VIRTIO_NET_F_HOST_TSO6 = 1 << 12,
VIRTIO_NET_F_HOST_ECN = 1 << 13,
VIRTIO_NET_F_HOST_UFO = 1 << 14,
VIRTIO_NET_F_MRG_RXBUF = 1 << 15,
VIRTIO_NET_F_STATUS = 1 << 16,
VIRTIO_NET_F_CTRL_VQ = 1 << 17,
VIRTIO_NET_F_CTRL_RX = 1 << 18,
VIRTIO_NET_F_CTRL_VLAN = 1 << 19,
VIRTIO_NET_F_GUEST_ANNOUNCE = 1 << 21,
VIRTIO_NET_F_MQ = 1 << 22,
VIRTIO_NET_F_CTRL_MAC_ADDR = 1 << 23,
VIRTIO_F_RING_INDIRECT_DESC = 1 << 28,
VIRTIO_F_RING_EVENT_IDX = 1 << 29,
VIRTIO_F_VERSION_1 = 1 << 32,
VIRTIO_F_ACCESS_PLATFORM = 1 << 33,
VIRTIO_F_RING_PACKED = 1 << 34,
VIRTIO_F_IN_ORDER = 1 << 35,
VIRTIO_F_ORDER_PLATFORM = 1 << 36,
VIRTIO_F_SR_IOV = 1 << 37,
VIRTIO_F_NOTIFICATION_DATA = 1 << 38,
VIRTIO_NET_F_GUEST_HDRLEN = 1 << 59,
VIRTIO_NET_F_RSC_EXT = 1 << 61,
VIRTIO_NET_F_STANDBY = 1 << 62,
}
impl From<Features> for u64 {
fn from(val: Features) -> Self {
match val {
Features::VIRTIO_NET_F_CSUM => 1 << 0,
Features::VIRTIO_NET_F_GUEST_CSUM => 1 << 1,
Features::VIRTIO_NET_F_CTRL_GUEST_OFFLOADS => 1 << 2,
Features::VIRTIO_NET_F_MTU => 1 << 3,
Features::VIRTIO_NET_F_MAC => 1 << 5,
Features::VIRTIO_NET_F_GUEST_TSO4 => 1 << 7,
Features::VIRTIO_NET_F_GUEST_TSO6 => 1 << 8,
Features::VIRTIO_NET_F_GUEST_ECN => 1 << 9,
Features::VIRTIO_NET_F_GUEST_UFO => 1 << 10,
Features::VIRTIO_NET_F_HOST_TSO4 => 1 << 11,
Features::VIRTIO_NET_F_HOST_TSO6 => 1 << 12,
Features::VIRTIO_NET_F_HOST_ECN => 1 << 13,
Features::VIRTIO_NET_F_HOST_UFO => 1 << 14,
Features::VIRTIO_NET_F_MRG_RXBUF => 1 << 15,
Features::VIRTIO_NET_F_STATUS => 1 << 16,
Features::VIRTIO_NET_F_CTRL_VQ => 1 << 17,
Features::VIRTIO_NET_F_CTRL_RX => 1 << 18,
Features::VIRTIO_NET_F_CTRL_VLAN => 1 << 19,
Features::VIRTIO_NET_F_GUEST_ANNOUNCE => 1 << 21,
Features::VIRTIO_NET_F_MQ => 1 << 22,
Features::VIRTIO_NET_F_CTRL_MAC_ADDR => 1 << 23,
Features::VIRTIO_F_RING_INDIRECT_DESC => 1 << 28,
Features::VIRTIO_F_RING_EVENT_IDX => 1 << 29,
Features::VIRTIO_F_VERSION_1 => 1 << 32,
Features::VIRTIO_F_ACCESS_PLATFORM => 1 << 33,
Features::VIRTIO_F_RING_PACKED => 1 << 34,
Features::VIRTIO_F_IN_ORDER => 1 << 35,
Features::VIRTIO_F_ORDER_PLATFORM => 1 << 36,
Features::VIRTIO_F_SR_IOV => 1 << 37,
Features::VIRTIO_F_NOTIFICATION_DATA => 1 << 38,
Features::VIRTIO_NET_F_GUEST_HDRLEN => 1 << 59,
Features::VIRTIO_NET_F_RSC_EXT => 1 << 61,
Features::VIRTIO_NET_F_STANDBY => 1 << 62,
}
}
}
impl BitOr for Features {
type Output = u64;
fn bitor(self, rhs: Self) -> Self::Output {
u64::from(self) | u64::from(rhs)
}
}
impl BitOr<Features> for u64 {
type Output = u64;
fn bitor(self, rhs: Features) -> Self::Output {
self | u64::from(rhs)
}
}
impl BitOrAssign<Features> for u64 {
fn bitor_assign(&mut self, rhs: Features) {
*self |= u64::from(rhs);
}
}
impl BitAnd for Features {
type Output = u64;
fn bitand(self, rhs: Features) -> Self::Output {
u64::from(self) & u64::from(rhs)
}
}
impl BitAnd<Features> for u64 {
type Output = u64;
fn bitand(self, rhs: Features) -> Self::Output {
self & u64::from(rhs)
}
}
impl BitAndAssign<Features> for u64 {
fn bitand_assign(&mut self, rhs: Features) {
*self &= u64::from(rhs);
}
}
impl core::fmt::Display for Features {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Features::VIRTIO_NET_F_CSUM => write!(f, "VIRTIO_NET_F_CSUM"),
Features::VIRTIO_NET_F_GUEST_CSUM => write!(f, "VIRTIO_NET_F_GUEST_CSUM"),
Features::VIRTIO_NET_F_CTRL_GUEST_OFFLOADS => {
write!(f, "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS")
}
Features::VIRTIO_NET_F_MTU => write!(f, "VIRTIO_NET_F_MTU"),
Features::VIRTIO_NET_F_MAC => write!(f, "VIRTIO_NET_F_MAC"),
Features::VIRTIO_NET_F_GUEST_TSO4 => write!(f, "VIRTIO_NET_F_GUEST_TSO4"),
Features::VIRTIO_NET_F_GUEST_TSO6 => write!(f, "VIRTIO_NET_F_GUEST_TSO6"),
Features::VIRTIO_NET_F_GUEST_ECN => write!(f, "VIRTIO_NET_F_GUEST_ECN"),
Features::VIRTIO_NET_F_GUEST_UFO => write!(f, "VIRTIO_NET_FGUEST_UFO"),
Features::VIRTIO_NET_F_HOST_TSO4 => write!(f, "VIRTIO_NET_F_HOST_TSO4"),
Features::VIRTIO_NET_F_HOST_TSO6 => write!(f, "VIRTIO_NET_F_HOST_TSO6"),
Features::VIRTIO_NET_F_HOST_ECN => write!(f, "VIRTIO_NET_F_HOST_ECN"),
Features::VIRTIO_NET_F_HOST_UFO => write!(f, "VIRTIO_NET_F_HOST_UFO"),
Features::VIRTIO_NET_F_MRG_RXBUF => write!(f, "VIRTIO_NET_F_MRG_RXBUF"),
Features::VIRTIO_NET_F_STATUS => write!(f, "VIRTIO_NET_F_STATUS"),
Features::VIRTIO_NET_F_CTRL_VQ => write!(f, "VIRTIO_NET_F_CTRL_VQ"),
Features::VIRTIO_NET_F_CTRL_RX => write!(f, "VIRTIO_NET_F_CTRL_RX"),
Features::VIRTIO_NET_F_CTRL_VLAN => write!(f, "VIRTIO_NET_F_CTRL_VLAN"),
Features::VIRTIO_NET_F_GUEST_ANNOUNCE => write!(f, "VIRTIO_NET_F_GUEST_ANNOUNCE"),
Features::VIRTIO_NET_F_MQ => write!(f, "VIRTIO_NET_F_MQ"),
Features::VIRTIO_NET_F_CTRL_MAC_ADDR => write!(f, "VIRTIO_NET_F_CTRL_MAC_ADDR"),
Features::VIRTIO_F_RING_INDIRECT_DESC => write!(f, "VIRTIO_F_RING_INDIRECT_DESC"),
Features::VIRTIO_F_RING_EVENT_IDX => write!(f, "VIRTIO_F_RING_EVENT_IDX"),
Features::VIRTIO_F_VERSION_1 => write!(f, "VIRTIO_F_VERSION_1"),
Features::VIRTIO_F_ACCESS_PLATFORM => write!(f, "VIRTIO_F_ACCESS_PLATFORM"),
Features::VIRTIO_F_RING_PACKED => write!(f, "VIRTIO_F_RING_PACKED"),
Features::VIRTIO_F_IN_ORDER => write!(f, "VIRTIO_F_IN_ORDER"),
Features::VIRTIO_F_ORDER_PLATFORM => write!(f, "VIRTIO_F_ORDER_PLATFORM"),
Features::VIRTIO_F_SR_IOV => write!(f, "VIRTIO_F_SR_IOV"),
Features::VIRTIO_F_NOTIFICATION_DATA => write!(f, "VIRTIO_F_NOTIFICATION_DATA"),
Features::VIRTIO_NET_F_GUEST_HDRLEN => write!(f, "VIRTIO_NET_F_GUEST_HDRLEN"),
Features::VIRTIO_NET_F_RSC_EXT => write!(f, "VIRTIO_NET_F_RSC_EXT"),
Features::VIRTIO_NET_F_STANDBY => write!(f, "VIRTIO_NET_F_STANDBY"),
}
}
}
impl Features {
pub fn from_set(feat_set: FeatureSet) -> Option<Vec<Features>> {
let mut vec_of_feats: Vec<Features> = Vec::new();
let feats = feat_set.0;
if feats & (1 << 0) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_CSUM)
}
if feats & (1 << 1) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_CSUM)
}
if feats & (1 << 2) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)
}
if feats & (1 << 3) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_MTU)
}
if feats & (1 << 5) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_MAC)
}
if feats & (1 << 7) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_TSO4)
}
if feats & (1 << 8) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_TSO6)
}
if feats & (1 << 9) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_ECN)
}
if feats & (1 << 10) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_UFO)
}
if feats & (1 << 11) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_HOST_TSO4)
}
if feats & (1 << 12) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_HOST_TSO6)
}
if feats & (1 << 13) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_HOST_ECN)
}
if feats & (1 << 14) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_HOST_UFO)
}
if feats & (1 << 15) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_MRG_RXBUF)
}
if feats & (1 << 16) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_STATUS)
}
if feats & (1 << 17) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_CTRL_VQ)
}
if feats & (1 << 18) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_CTRL_RX)
}
if feats & (1 << 19) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_CTRL_VLAN)
}
if feats & (1 << 21) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_ANNOUNCE)
}
if feats & (1 << 22) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_MQ)
}
if feats & (1 << 23) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_CTRL_MAC_ADDR)
}
if feats & (1 << 28) != 0 {
vec_of_feats.push(Features::VIRTIO_F_RING_INDIRECT_DESC)
}
if feats & (1 << 29) != 0 {
vec_of_feats.push(Features::VIRTIO_F_RING_EVENT_IDX)
}
if feats & (1 << 32) != 0 {
vec_of_feats.push(Features::VIRTIO_F_VERSION_1)
}
if feats & (1 << 33) != 0 {
vec_of_feats.push(Features::VIRTIO_F_ACCESS_PLATFORM)
}
if feats & (1 << 34) != 0 {
vec_of_feats.push(Features::VIRTIO_F_RING_PACKED)
}
if feats & (1 << 35) != 0 {
vec_of_feats.push(Features::VIRTIO_F_IN_ORDER)
}
if feats & (1 << 36) != 0 {
vec_of_feats.push(Features::VIRTIO_F_ORDER_PLATFORM)
}
if feats & (1 << 37) != 0 {
vec_of_feats.push(Features::VIRTIO_F_SR_IOV)
}
if feats & (1 << 38) != 0 {
vec_of_feats.push(Features::VIRTIO_F_NOTIFICATION_DATA)
}
if feats & (1 << 59) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_GUEST_HDRLEN)
}
if feats & (1 << 61) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_RSC_EXT)
}
if feats & (1 << 62) != 0 {
vec_of_feats.push(Features::VIRTIO_NET_F_STANDBY)
}
if vec_of_feats.is_empty() {
None
} else {
Some(vec_of_feats)
}
}
}
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u16)]
pub enum Status {
VIRTIO_NET_S_LINK_UP = 1 << 0,
VIRTIO_NET_S_ANNOUNCE = 1 << 1,
}
impl From<Status> for u16 {
fn from(stat: Status) -> Self {
match stat {
Status::VIRTIO_NET_S_LINK_UP => 1,
Status::VIRTIO_NET_S_ANNOUNCE => 2,
}
}
}
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq)]
pub struct FeatureSet(u64);
impl BitOr for FeatureSet {
type Output = FeatureSet;
fn bitor(self, rhs: Self) -> Self::Output {
FeatureSet(self.0 | rhs.0)
}
}
impl BitOr<FeatureSet> for u64 {
type Output = u64;
fn bitor(self, rhs: FeatureSet) -> Self::Output {
self | u64::from(rhs)
}
}
impl BitOrAssign<FeatureSet> for u64 {
fn bitor_assign(&mut self, rhs: FeatureSet) {
*self |= u64::from(rhs);
}
}
impl BitOrAssign<Features> for FeatureSet {
fn bitor_assign(&mut self, rhs: Features) {
self.0 = self.0 | u64::from(rhs);
}
}
impl BitAnd for FeatureSet {
type Output = FeatureSet;
fn bitand(self, rhs: FeatureSet) -> Self::Output {
FeatureSet(self.0 & rhs.0)
}
}
impl BitAnd<FeatureSet> for u64 {
type Output = u64;
fn bitand(self, rhs: FeatureSet) -> Self::Output {
self & u64::from(rhs)
}
}
impl BitAndAssign<FeatureSet> for u64 {
fn bitand_assign(&mut self, rhs: FeatureSet) {
*self &= u64::from(rhs);
}
}
impl From<FeatureSet> for u64 {
fn from(feature_set: FeatureSet) -> Self {
feature_set.0
}
}
impl FeatureSet {
pub fn check_features(feats: &[Features]) -> Result<(), VirtioNetError> {
let mut feat_bits = 0u64;
for feat in feats.iter() {
feat_bits |= *feat;
}
for feat in feats {
match feat {
Features::VIRTIO_NET_F_CSUM => continue,
Features::VIRTIO_NET_F_GUEST_CSUM => continue,
Features::VIRTIO_NET_F_CTRL_GUEST_OFFLOADS => continue,
Features::VIRTIO_NET_F_MTU => continue,
Features::VIRTIO_NET_F_MAC => continue,
Features::VIRTIO_NET_F_GUEST_TSO4 => {
if feat_bits & Features::VIRTIO_NET_F_GUEST_CSUM != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_GUEST_TSO6 => {
if feat_bits & Features::VIRTIO_NET_F_GUEST_CSUM != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_GUEST_ECN => {
if feat_bits
& (Features::VIRTIO_NET_F_GUEST_TSO4
| Features::VIRTIO_NET_F_GUEST_TSO6)
!= 0
{
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_GUEST_UFO => {
if feat_bits & Features::VIRTIO_NET_F_GUEST_CSUM != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_HOST_TSO4 => {
if feat_bits & Features::VIRTIO_NET_F_CSUM != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_HOST_TSO6 => {
if feat_bits & Features::VIRTIO_NET_F_CSUM != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_HOST_ECN => {
if feat_bits
& (Features::VIRTIO_NET_F_HOST_TSO4 | Features::VIRTIO_NET_F_HOST_TSO6)
!= 0
{
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_HOST_UFO => {
if feat_bits & Features::VIRTIO_NET_F_CSUM != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_MRG_RXBUF => continue,
Features::VIRTIO_NET_F_STATUS => continue,
Features::VIRTIO_NET_F_CTRL_VQ => continue,
Features::VIRTIO_NET_F_CTRL_RX => {
if feat_bits & Features::VIRTIO_NET_F_CTRL_VQ != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_CTRL_VLAN => {
if feat_bits & Features::VIRTIO_NET_F_CTRL_VQ != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_GUEST_ANNOUNCE => {
if feat_bits & Features::VIRTIO_NET_F_CTRL_VQ != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_MQ => {
if feat_bits & Features::VIRTIO_NET_F_CTRL_VQ != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_CTRL_MAC_ADDR => {
if feat_bits & Features::VIRTIO_NET_F_CTRL_VQ != 0 {
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_GUEST_HDRLEN => continue,
Features::VIRTIO_NET_F_RSC_EXT => {
if feat_bits
& (Features::VIRTIO_NET_F_HOST_TSO4 | Features::VIRTIO_NET_F_HOST_TSO6)
!= 0
{
continue;
} else {
return Err(VirtioNetError::FeatReqNotMet(FeatureSet(feat_bits)));
}
}
Features::VIRTIO_NET_F_STANDBY => continue,
Features::VIRTIO_F_RING_INDIRECT_DESC => continue,
Features::VIRTIO_F_RING_EVENT_IDX => continue,
Features::VIRTIO_F_VERSION_1 => continue,
Features::VIRTIO_F_ACCESS_PLATFORM => continue,
Features::VIRTIO_F_RING_PACKED => continue,
Features::VIRTIO_F_IN_ORDER => continue,
Features::VIRTIO_F_ORDER_PLATFORM => continue,
Features::VIRTIO_F_SR_IOV => continue,
Features::VIRTIO_F_NOTIFICATION_DATA => continue,
}
}
Ok(())
}
pub fn is_feature(self, feat: Features) -> bool {
self.0 & feat != 0
}
pub fn set_features(&mut self, feats: &[Features]) {
for feat in feats {
self.0 |= *feat;
}
}
pub fn new(val: u64) -> Self {
FeatureSet(val)
}
}
}
pub mod error {
use super::constants::FeatureSet;
#[derive(Debug, Copy, Clone)]
pub enum VirtioNetError {
General,
NoDevCfg(u16),
NoComCfg(u16),
NoIsrCfg(u16),
NoNotifCfg(u16),
FailFeatureNeg(u16),
FeatReqNotMet(FeatureSet),
IncompFeatsSet(FeatureSet, FeatureSet),
ProcessOngoing,
Unknown,
}
}