Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/device/blk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const QUEUE_SIZE: u16 = 16;
const SUPPORTED_FEATURES: BlkFeature = BlkFeature::RO
.union(BlkFeature::FLUSH)
.union(BlkFeature::RING_INDIRECT_DESC)
.union(BlkFeature::RING_EVENT_IDX);
.union(BlkFeature::RING_EVENT_IDX)
.union(BlkFeature::VERSION_1);

/// Driver for a VirtIO block device.
///
Expand Down
3 changes: 2 additions & 1 deletion src/device/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const QUEUE_SIZE: usize = 2;
const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX
.union(Features::RING_INDIRECT_DESC)
.union(Features::SIZE)
.union(Features::EMERG_WRITE);
.union(Features::EMERG_WRITE)
.union(Features::VERSION_1);

/// Driver for a VirtIO console device.
///
Expand Down
4 changes: 3 additions & 1 deletion src/device/gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout};

const QUEUE_SIZE: u16 = 2;
const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RING_INDIRECT_DESC);
const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX
.union(Features::RING_INDIRECT_DESC)
.union(Features::VERSION_1);

/// A virtio based graphics adapter.
///
Expand Down Expand Up @@ -264,7 +266,7 @@
rsp.check_type(Command::OK_NODATA)
}

fn update_cursor(

Check warning on line 269 in src/device/gpu.rs

View workflow job for this annotation

GitHub Actions / check

this function has too many arguments (8/7)
&mut self,
resource_id: u32,
scanout_id: u32,
Expand Down
4 changes: 3 additions & 1 deletion src/device/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,9 @@ pub struct InputEvent {

const QUEUE_EVENT: u16 = 0;
const QUEUE_STATUS: u16 = 1;
const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX.union(Feature::RING_INDIRECT_DESC);
const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX
.union(Feature::RING_INDIRECT_DESC)
.union(Feature::VERSION_1);

// a parameter that can change
const QUEUE_SIZE: usize = 32;
Expand Down
2 changes: 1 addition & 1 deletion src/device/net/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONet<H, T, QUEUE_SIZE>
const NONE_BUF: Option<RxBuffer> = 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);
let mut rx_buf = RxBuffer::new(i, buf_len, inner.legacy_header);
// SAFETY: The buffer lives as long as the queue.
let token = unsafe { inner.receive_begin(rx_buf.as_bytes_mut())? };
assert_eq!(token, i as u16);
Expand Down
87 changes: 60 additions & 27 deletions src/device/net/dev_raw.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::{Config, EthernetAddress, Features, VirtioNetHdr};
use super::{MIN_BUFFER_LEN, NET_HDR_SIZE, QUEUE_RECEIVE, QUEUE_TRANSMIT, SUPPORTED_FEATURES};
use super::{Config, EthernetAddress, Features, VirtioNetHdr, VirtioNetHdrLegacy};
use super::{MIN_BUFFER_LEN, QUEUE_RECEIVE, QUEUE_TRANSMIT, SUPPORTED_FEATURES};
use crate::config::read_config;
use crate::hal::Hal;
use crate::queue::VirtQueue;
use crate::transport::{InterruptStatus, Transport};
use crate::{Error, Result};
use core::mem::size_of;
use log::{debug, info, warn};
use zerocopy::IntoBytes;

Expand All @@ -21,6 +22,8 @@ pub struct VirtIONetRaw<H: Hal, T: Transport, const QUEUE_SIZE: usize> {
mac: EthernetAddress,
recv_queue: VirtQueue<H, QUEUE_SIZE>,
send_queue: VirtQueue<H, QUEUE_SIZE>,
/// Whether `num_buffers` is missing in the `virtio_net_hdr` struct.
pub(crate) legacy_header: bool,
}

impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZE> {
Expand Down Expand Up @@ -54,6 +57,8 @@ impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZ
mac,
recv_queue,
send_queue,
legacy_header: !negotiated_features.contains(Features::VERSION_1)
&& !negotiated_features.contains(Features::MRG_RXBUF),
})
}

Expand Down Expand Up @@ -95,8 +100,13 @@ impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZ
}

/// Whether the length of the transmit buffer is valid.
fn check_tx_buf_len(tx_buf: &[u8]) -> Result<()> {
if tx_buf.len() < NET_HDR_SIZE {
fn check_tx_buf_len(&self, tx_buf: &[u8]) -> Result<()> {
let hdr_size = if self.legacy_header {
size_of::<VirtioNetHdrLegacy>()
} else {
size_of::<VirtioNetHdr>()
};
if tx_buf.len() < hdr_size {
warn!("Transmit buffer len {} is too small", tx_buf.len());
Err(Error::InvalidParam)
} else {
Expand All @@ -108,12 +118,21 @@ impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZ
///
/// If the `buffer` is not large enough, it returns [`Error::InvalidParam`].
pub fn fill_buffer_header(&self, buffer: &mut [u8]) -> Result<usize> {
if buffer.len() < NET_HDR_SIZE {
return Err(Error::InvalidParam);
macro_rules! fill {
($hdr:ty) => {{
if buffer.len() < size_of::<$hdr>() {
return Err(Error::InvalidParam);
}
let header = <$hdr>::default();
buffer[..size_of::<$hdr>()].copy_from_slice(header.as_bytes());
Ok(size_of::<$hdr>())
}};
}
if self.legacy_header {
fill!(VirtioNetHdrLegacy)
} else {
fill!(VirtioNetHdr)
}
let header = VirtioNetHdr::default();
buffer[..NET_HDR_SIZE].copy_from_slice(header.as_bytes());
Ok(NET_HDR_SIZE)
}

/// Submits a request to transmit a buffer immediately without waiting for
Expand Down Expand Up @@ -141,7 +160,7 @@ impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZ
/// [`poll_transmit`]: Self::poll_transmit
/// [`transmit_complete`]: Self::transmit_complete
pub unsafe fn transmit_begin(&mut self, tx_buf: &[u8]) -> Result<u16> {
Self::check_tx_buf_len(tx_buf)?;
self.check_tx_buf_len(tx_buf)?;
let token = self.send_queue.add(&[tx_buf], &mut [])?;
if self.send_queue.should_notify() {
self.transport.notify(QUEUE_TRANSMIT);
Expand Down Expand Up @@ -226,28 +245,42 @@ impl<H: Hal, T: Transport, const QUEUE_SIZE: usize> VirtIONetRaw<H, T, QUEUE_SIZ
rx_buf: &mut [u8],
) -> Result<(usize, usize)> {
let len = self.recv_queue.pop_used(token, &[], &mut [rx_buf])? as usize;
let packet_len = len.checked_sub(NET_HDR_SIZE).ok_or(Error::IoError)?;
Ok((NET_HDR_SIZE, packet_len))
let hdr_size = if self.legacy_header {
size_of::<VirtioNetHdrLegacy>()
} else {
size_of::<VirtioNetHdr>()
};
let packet_len = len.checked_sub(hdr_size).ok_or(Error::IoError)?;
Ok((hdr_size, packet_len))
}

/// Sends a packet to the network, and blocks until the request completed.
pub fn send(&mut self, tx_buf: &[u8]) -> Result {
let header = VirtioNetHdr::default();
if tx_buf.is_empty() {
// Special case sending an empty packet, to avoid adding an empty buffer to the
// virtqueue.
self.send_queue.add_notify_wait_pop(
&[header.as_bytes()],
&mut [],
&mut self.transport,
)?;
} else {
self.send_queue.add_notify_wait_pop(
&[header.as_bytes(), tx_buf],
&mut [],
&mut self.transport,
)?;
macro_rules! send {
($header:expr) => {{
let header = $header;
if tx_buf.is_empty() {
// Special case sending an empty packet, to avoid adding an empty buffer to the
// virtqueue.
self.send_queue.add_notify_wait_pop(
&[header.as_bytes()],
&mut [],
&mut self.transport,
)?;
} else {
self.send_queue.add_notify_wait_pop(
&[header.as_bytes(), tx_buf],
&mut [],
&mut self.transport,
)?;
}
}};
}
if self.legacy_header {
send!(VirtioNetHdrLegacy::default())
} else {
send!(VirtioNetHdr::default())
};
Ok(())
}

Expand Down
45 changes: 42 additions & 3 deletions src/device/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};

const MAX_BUFFER_LEN: usize = 65535;
const MIN_BUFFER_LEN: usize = 1526;
const NET_HDR_SIZE: usize = core::mem::size_of::<VirtioNetHdr>();

bitflags! {
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
Expand Down Expand Up @@ -119,17 +118,56 @@ type EthernetAddress = [u8; 6];
/// In each case, the packet itself is preceded by a header.
#[repr(C)]
#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
pub(crate) struct VirtioNetHdrLegacy {
flags: Flags,
gso_type: GsoType,
hdr_len: u16, // cannot rely on this
gso_size: u16,
csum_start: u16,
csum_offset: u16,
}

/// 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(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
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.
num_buffers: u16,
// payload starts from here
}

impl From<&VirtioNetHdrLegacy> for VirtioNetHdr {
fn from(legacy: &VirtioNetHdrLegacy) -> Self {
let VirtioNetHdrLegacy {
flags,
gso_type,
hdr_len,
gso_size,
csum_start,
csum_offset,
} = *legacy;

Self {
flags,
gso_type,
hdr_len,
gso_size,
csum_start,
csum_offset,
num_buffers: 0,
}
}
}

#[derive(
IntoBytes, Copy, Clone, Debug, Default, Eq, FromBytes, Immutable, KnownLayout, PartialEq,
)]
Expand Down Expand Up @@ -163,4 +201,5 @@ const QUEUE_TRANSMIT: u16 = 1;
const SUPPORTED_FEATURES: Features = Features::MAC
.union(Features::STATUS)
.union(Features::RING_EVENT_IDX)
.union(Features::RING_INDIRECT_DESC);
.union(Features::RING_INDIRECT_DESC)
.union(Features::VERSION_1);
35 changes: 28 additions & 7 deletions src/device/net/net_buf.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{VirtioNetHdr, NET_HDR_SIZE};
use super::{VirtioNetHdr, VirtioNetHdrLegacy};
use alloc::{vec, vec::Vec};
use core::{convert::TryInto, mem::size_of};
use zerocopy::{FromBytes, IntoBytes};
Expand All @@ -11,6 +11,8 @@ pub struct RxBuffer {
pub(crate) buf: Vec<usize>, // for alignment
pub(crate) packet_len: usize,
pub(crate) idx: u16,
/// Whether `num_buffers` is missing in the `virtio_net_hdr` struct.
legacy_header: bool,
}

impl TxBuffer {
Expand All @@ -37,11 +39,12 @@ impl TxBuffer {

impl RxBuffer {
/// Allocates a new buffer with length `buf_len`.
pub(crate) fn new(idx: usize, buf_len: usize) -> Self {
pub(crate) fn new(idx: usize, buf_len: usize, legacy_header: bool) -> Self {
Self {
buf: vec![0; buf_len / size_of::<usize>()],
packet_len: 0,
idx: idx.try_into().unwrap(),
legacy_header,
}
}

Expand All @@ -66,18 +69,36 @@ impl RxBuffer {
self.buf.as_mut_bytes()
}

/// Returns the reference of the header.
pub fn header(&self) -> &VirtioNetHdr {
FromBytes::ref_from_prefix(self.as_bytes()).unwrap().0
/// Returns a copy of the header.
pub fn header(&self) -> VirtioNetHdr {
if self.legacy_header {
VirtioNetHdrLegacy::ref_from_prefix(self.as_bytes())
.unwrap()
.0
.into()
} else {
*VirtioNetHdr::ref_from_prefix(self.as_bytes()).unwrap().0
}
}

/// 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]
let hdr_size = if self.legacy_header {
size_of::<VirtioNetHdrLegacy>()
} else {
size_of::<VirtioNetHdr>()
};

&self.buf.as_bytes()[hdr_size..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_mut_bytes()[NET_HDR_SIZE..NET_HDR_SIZE + self.packet_len]
let hdr_size = if self.legacy_header {
size_of::<VirtioNetHdrLegacy>()
} else {
size_of::<VirtioNetHdr>()
};
&mut self.buf.as_mut_bytes()[hdr_size..hdr_size + self.packet_len]
}
}
4 changes: 3 additions & 1 deletion src/device/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use crate::{
// VirtioRNG only uses one queue
const QUEUE_IDX: u16 = 0;
const QUEUE_SIZE: usize = 8;
const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC.union(Feature::RING_EVENT_IDX);
const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
.union(Feature::RING_EVENT_IDX)
.union(Feature::VERSION_1);

/// Driver for a VirtIO random number generator device.
pub struct VirtIORng<H: Hal, T: Transport> {
Expand Down
4 changes: 3 additions & 1 deletion src/device/socket/vsock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ pub(crate) const TX_QUEUE_IDX: u16 = 1;
const EVENT_QUEUE_IDX: u16 = 2;

pub(crate) const QUEUE_SIZE: usize = 8;
const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX.union(Feature::RING_INDIRECT_DESC);
const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX
.union(Feature::RING_INDIRECT_DESC)
.union(Feature::VERSION_1);

/// Information about a particular vsock connection.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
Expand Down
4 changes: 3 additions & 1 deletion src/device/sound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@
}

/// Set selected stream parameters for the specified stream ID.
pub fn pcm_set_params(

Check warning on line 365 in src/device/sound.rs

View workflow job for this annotation

GitHub Actions / check

this function has too many arguments (8/7)
&mut self,
stream_id: u32,
buffer_bytes: u32,
Expand Down Expand Up @@ -745,7 +745,9 @@
const TX_QUEUE_IDX: u16 = 2;
const RX_QUEUE_IDX: u16 = 3;

const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC.union(Feature::RING_EVENT_IDX);
const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
.union(Feature::RING_EVENT_IDX)
.union(Feature::VERSION_1);

bitflags! {
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
Expand Down
12 changes: 11 additions & 1 deletion src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,19 @@ pub trait Transport {
self.set_status(DeviceStatus::empty());
self.set_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER);

let device_features = F::from_bits_truncate(self.read_device_features());
let device_feature_bits = self.read_device_features();
let device_features = F::from_bits_truncate(device_feature_bits);
debug!("Device features: {:?}", device_features);
let negotiated_features = device_features & supported_features;
if cfg!(debug_assertions) {
use crate::device::common::Feature;

if device_feature_bits & Feature::VERSION_1.bits() > 0 {
// > 6.1 Driver Requirements: Reserved Feature Bits
// > A driver MUST accept VIRTIO_F_VERSION_1 if it is offered.
debug_assert!(negotiated_features.bits() & Feature::VERSION_1.bits() > 0, "Driver must accept VIRTIO_F_VERSION_1 in supported features because it is offered by the device.");
}
}
self.write_driver_features(negotiated_features.bits());

self.set_status(
Expand Down
Loading