//! Driver for VirtIO network devices. use crate::hal::Hal; use crate::queue::VirtQueue; use crate::transport::Transport; use crate::volatile::{volread, ReadOnly}; use crate::{Error, Result}; use alloc::{vec, vec::Vec}; use bitflags::bitflags; use core::mem::size_of; use log::{debug, info, warn}; use zerocopy::{AsBytes, FromBytes}; const MAX_BUFFER_LEN: usize = 65535; const MIN_BUFFER_LEN: usize = 1526; const NET_HDR_SIZE: usize = size_of::(); /// A buffer used for transmitting. pub struct TxBuffer(Vec); /// A buffer used for receiving. pub struct RxBuffer { buf: Vec, // for alignment packet_len: usize, idx: usize, } impl TxBuffer { /// Constructs the buffer from the given slice. pub fn from(buf: &[u8]) -> Self { Self(Vec::from(buf)) } /// Returns the network packet length. pub fn packet_len(&self) -> usize { self.0.len() } /// Returns the network packet as a slice. pub fn packet(&self) -> &[u8] { self.0.as_slice() } /// Returns the network packet as a mutable slice. pub fn packet_mut(&mut self) -> &mut [u8] { self.0.as_mut_slice() } } impl RxBuffer { /// Allocates a new buffer with length `buf_len`. fn new(idx: usize, buf_len: usize) -> Self { Self { buf: vec![0; buf_len / size_of::()], packet_len: 0, idx, } } /// Set the network packet length. fn set_packet_len(&mut self, packet_len: usize) { self.packet_len = packet_len } /// Returns the network packet length (witout header). pub const fn packet_len(&self) -> usize { self.packet_len } /// Returns all data in the buffer, including both the header and the packet. pub fn as_bytes(&self) -> &[u8] { self.buf.as_bytes() } /// Returns all data in the buffer with the mutable reference, /// including both the header and the packet. pub fn as_bytes_mut(&mut self) -> &mut [u8] { self.buf.as_bytes_mut() } /// Returns the reference of the header. pub fn header(&self) -> &VirtioNetHdr { unsafe { &*(self.buf.as_ptr() as *const VirtioNetHdr) } } /// Returns the network packet as a slice. pub fn packet(&self) -> &[u8] { &self.buf.as_bytes()[NET_HDR_SIZE..NET_HDR_SIZE + self.packet_len] } /// Returns the network packet as a mutable slice. pub fn packet_mut(&mut self) -> &mut [u8] { &mut self.buf.as_bytes_mut()[NET_HDR_SIZE..NET_HDR_SIZE + self.packet_len] } } /// The virtio network device is a virtual ethernet card. /// /// It has enhanced rapidly and demonstrates clearly how support for new /// features are added to an existing device. /// Empty buffers are placed in one virtqueue for receiving packets, and /// outgoing packets are enqueued into another for transmission in that order. /// A third command queue is used to control advanced filtering features. pub struct VirtIONet { transport: T, mac: EthernetAddress, recv_queue: VirtQueue, send_queue: VirtQueue, rx_buffers: [Option; QUEUE_SIZE], } impl VirtIONet { /// Create a new VirtIO-Net driver. pub fn new(mut transport: T, buf_len: usize) -> Result { transport.begin_init(|features| { let features = Features::from_bits_truncate(features); info!("Device features {:?}", features); let supported_features = Features::MAC | Features::STATUS; (features & supported_features).bits() }); // read configuration space let config = transport.config_space::()?; let mac; // Safe because config points to a valid MMIO region for the config space. unsafe { mac = volread!(config, mac); debug!( "Got MAC={:02x?}, status={:?}", mac, volread!(config, status) ); } if !(MIN_BUFFER_LEN..=MAX_BUFFER_LEN).contains(&buf_len) { warn!( "Receive buffer len {} is not in range [{}, {}]", buf_len, MIN_BUFFER_LEN, MAX_BUFFER_LEN ); return Err(Error::InvalidParam); } let send_queue = VirtQueue::new(&mut transport, QUEUE_TRANSMIT)?; let mut recv_queue = VirtQueue::new(&mut transport, QUEUE_RECEIVE)?; const NONE_BUF: Option = None; let mut rx_buffers = [NONE_BUF; QUEUE_SIZE]; for (i, rx_buf_place) in rx_buffers.iter_mut().enumerate() { let mut rx_buf = RxBuffer::new(i, buf_len); // Safe because the buffer lives as long as the queue. let token = unsafe { recv_queue.add(&[], &mut [rx_buf.as_bytes_mut()])? }; assert_eq!(token, i as u16); *rx_buf_place = Some(rx_buf); } if recv_queue.should_notify() { transport.notify(QUEUE_RECEIVE); } transport.finish_init(); Ok(VirtIONet { transport, mac, recv_queue, send_queue, rx_buffers, }) } /// Acknowledge interrupt. pub fn ack_interrupt(&mut self) -> bool { self.transport.ack_interrupt() } /// Get MAC address. pub fn mac_address(&self) -> EthernetAddress { self.mac } /// Whether can send packet. pub fn can_send(&self) -> bool { self.send_queue.available_desc() >= 2 } /// Whether can receive packet. pub fn can_recv(&self) -> bool { self.recv_queue.can_pop() } /// Receives a [`RxBuffer`] from network. If currently no data, returns an /// error with type [`Error::NotReady`]. /// /// It will try to pop a buffer that completed data reception in the /// NIC queue. pub fn receive(&mut self) -> Result { if let Some(token) = self.recv_queue.peek_used() { let mut rx_buf = self.rx_buffers[token as usize] .take() .ok_or(Error::WrongToken)?; if token as usize != rx_buf.idx { return Err(Error::WrongToken); } // Safe because `token` == `rx_buf.idx`, we are passing the same // buffer as we passed to `VirtQueue::add` and it is still valid. let len = unsafe { self.recv_queue .pop_used(token, &[], &mut [rx_buf.as_bytes_mut()])? }; if (len as usize) < NET_HDR_SIZE { Err(Error::IoError) } else { rx_buf.set_packet_len(len as usize - NET_HDR_SIZE); Ok(rx_buf) } } else { Err(Error::NotReady) } } /// Gives back the ownership of `rx_buf`, and recycles it for next use. /// /// It will add the buffer back to the NIC queue. pub fn recycle_rx_buffer(&mut self, mut rx_buf: RxBuffer) -> Result { let old_token = rx_buf.idx; // Safe because we take the ownership of `rx_buf` back to `rx_buffers`, // it lives as long as the queue. let new_token = unsafe { self.recv_queue.add(&[], &mut [rx_buf.as_bytes_mut()]) }?; if new_token as usize != old_token { return Err(Error::WrongToken); } self.rx_buffers[old_token] = Some(rx_buf); if self.recv_queue.should_notify() { self.transport.notify(QUEUE_RECEIVE); } Ok(()) } /// Allocate a new buffer for transmitting. pub fn new_tx_buffer(&self, buf_len: usize) -> TxBuffer { TxBuffer(vec![0; buf_len]) } /// Sends a [`TxBuffer`] to the network, and blocks until the request /// completed. pub fn send(&mut self, tx_buf: TxBuffer) -> Result { let header = VirtioNetHdr::default(); self.send_queue.add_notify_wait_pop( &[header.as_bytes(), tx_buf.packet()], &mut [], &mut self.transport, )?; Ok(()) } } impl Drop for VirtIONet { fn drop(&mut self) { // Clear any pointers pointing to DMA regions, so the device doesn't try to access them // after they have been freed. self.transport.queue_unset(QUEUE_RECEIVE); self.transport.queue_unset(QUEUE_TRANSMIT); } } bitflags! { struct Features: u64 { /// Device handles packets with partial checksum. /// This "checksum offload" is a common feature on modern network cards. const CSUM = 1 << 0; /// Driver handles packets with partial checksum. const GUEST_CSUM = 1 << 1; /// Control channel offloads reconfiguration support. const CTRL_GUEST_OFFLOADS = 1 << 2; /// Device maximum MTU reporting is supported. /// /// If offered by the device, device advises driver about the value of /// its maximum MTU. If negotiated, the driver uses mtu as the maximum /// MTU value. const MTU = 1 << 3; /// Device has given MAC address. const MAC = 1 << 5; /// Device handles packets with any GSO type. (legacy) const GSO = 1 << 6; /// Driver can receive TSOv4. const GUEST_TSO4 = 1 << 7; /// Driver can receive TSOv6. const GUEST_TSO6 = 1 << 8; /// Driver can receive TSO with ECN. const GUEST_ECN = 1 << 9; /// Driver can receive UFO. const GUEST_UFO = 1 << 10; /// Device can receive TSOv4. const HOST_TSO4 = 1 << 11; /// Device can receive TSOv6. const HOST_TSO6 = 1 << 12; /// Device can receive TSO with ECN. const HOST_ECN = 1 << 13; /// Device can receive UFO. const HOST_UFO = 1 << 14; /// Driver can merge receive buffers. const MRG_RXBUF = 1 << 15; /// Configuration status field is available. const STATUS = 1 << 16; /// Control channel is available. const CTRL_VQ = 1 << 17; /// Control channel RX mode support. const CTRL_RX = 1 << 18; /// Control channel VLAN filtering. const CTRL_VLAN = 1 << 19; /// const CTRL_RX_EXTRA = 1 << 20; /// Driver can send gratuitous packets. const GUEST_ANNOUNCE = 1 << 21; /// Device supports multiqueue with automatic receive steering. const MQ = 1 << 22; /// Set MAC address through control channel. const CTL_MAC_ADDR = 1 << 23; // device independent const RING_INDIRECT_DESC = 1 << 28; const RING_EVENT_IDX = 1 << 29; const VERSION_1 = 1 << 32; // legacy } } bitflags! { struct Status: u16 { const LINK_UP = 1; const ANNOUNCE = 2; } } bitflags! { struct InterruptStatus : u32 { const USED_RING_UPDATE = 1 << 0; const CONFIGURATION_CHANGE = 1 << 1; } } #[repr(C)] struct Config { mac: ReadOnly, status: ReadOnly, max_virtqueue_pairs: ReadOnly, mtu: ReadOnly, } type EthernetAddress = [u8; 6]; /// VirtIO 5.1.6 Device Operation: /// /// Packets are transmitted by placing them in the transmitq1. . .transmitqN, /// and buffers for incoming packets are placed in the receiveq1. . .receiveqN. /// In each case, the packet itself is preceded by a header. #[repr(C)] #[derive(AsBytes, Debug, Default, FromBytes)] pub struct VirtioNetHdr { flags: Flags, gso_type: GsoType, hdr_len: u16, // cannot rely on this gso_size: u16, csum_start: u16, csum_offset: u16, // num_buffers: u16, // only available when the feature MRG_RXBUF is negotiated. // payload starts from here } bitflags! { #[repr(transparent)] #[derive(AsBytes, Default, FromBytes)] struct Flags: u8 { const NEEDS_CSUM = 1; const DATA_VALID = 2; const RSC_INFO = 4; } } #[repr(transparent)] #[derive(AsBytes, Debug, Copy, Clone, Default, Eq, FromBytes, PartialEq)] struct GsoType(u8); impl GsoType { const NONE: GsoType = GsoType(0); const TCPV4: GsoType = GsoType(1); const UDP: GsoType = GsoType(3); const TCPV6: GsoType = GsoType(4); const ECN: GsoType = GsoType(0x80); } const QUEUE_RECEIVE: u16 = 0; const QUEUE_TRANSMIT: u16 = 1;