mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-29 16:13:27 +00:00
Add virtio net device driver
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
2985cdced6
commit
7304e06c88
@ -9,10 +9,11 @@ use jinux_pci::{
|
||||
};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
|
||||
use self::input::device::InputDevice;
|
||||
use self::{input::device::InputDevice, network::device::NetworkDevice};
|
||||
|
||||
pub mod block;
|
||||
pub mod input;
|
||||
pub mod network;
|
||||
|
||||
pub(crate) const PCI_VIRTIO_CAP_COMMON_CFG: u8 = 1;
|
||||
pub(crate) const PCI_VIRTIO_CAP_NOTIFY_CFG: u8 = 2;
|
||||
@ -20,9 +21,8 @@ pub(crate) const PCI_VIRTIO_CAP_ISR_CFG: u8 = 3;
|
||||
pub(crate) const PCI_VIRTIO_CAP_DEVICE_CFG: u8 = 4;
|
||||
pub(crate) const PCI_VIRTIO_CAP_PCI_CFG: u8 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VirtioDevice {
|
||||
Network,
|
||||
Network(NetworkDevice),
|
||||
Block(BLKDevice),
|
||||
Console,
|
||||
Entropy,
|
||||
@ -141,6 +141,14 @@ impl VirtioDevice {
|
||||
virtio_info.notify_off_multiplier,
|
||||
msix_vector_left,
|
||||
)?),
|
||||
VirtioDeviceType::Network => VirtioDevice::Network(NetworkDevice::new(
|
||||
&virtio_info.device_cap_cfg,
|
||||
bars,
|
||||
&virtio_info.common_cfg_frame_ptr,
|
||||
virtio_info.notify_base_address as usize,
|
||||
virtio_info.notify_off_multiplier,
|
||||
msix_vector_left,
|
||||
)?),
|
||||
_ => {
|
||||
panic!("initialize PCIDevice failed, unsupport Virtio Device Type")
|
||||
}
|
||||
@ -152,7 +160,9 @@ impl VirtioDevice {
|
||||
let mask = ((1u64 << 24) - 1) | (((1u64 << 24) - 1) << 50);
|
||||
let device_specified_features = features & mask;
|
||||
let device_support_features = match device_type {
|
||||
VirtioDeviceType::Network => todo!(),
|
||||
VirtioDeviceType::Network => {
|
||||
NetworkDevice::negotiate_features(device_specified_features)
|
||||
}
|
||||
VirtioDeviceType::Block => BLKDevice::negotiate_features(device_specified_features),
|
||||
VirtioDeviceType::Console => todo!(),
|
||||
VirtioDeviceType::Entropy => todo!(),
|
||||
|
84
services/comps/virtio/src/device/network/buffer.rs
Normal file
84
services/comps/virtio/src/device/network/buffer.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use align_ext::AlignExt;
|
||||
use bytes::BytesMut;
|
||||
use pod::Pod;
|
||||
|
||||
use crate::device::network::header::VIRTIO_NET_HDR_LEN;
|
||||
|
||||
use super::header::VirtioNetHdr;
|
||||
|
||||
/// Buffer for receive packet
|
||||
#[derive(Debug)]
|
||||
pub struct RxBuffer {
|
||||
/// Packet Buffer, length align 8.
|
||||
buf: BytesMut,
|
||||
/// Packet len
|
||||
packet_len: usize,
|
||||
}
|
||||
|
||||
impl RxBuffer {
|
||||
pub fn new(len: usize) -> Self {
|
||||
let len = len.align_up(8);
|
||||
let buf = BytesMut::zeroed(len);
|
||||
Self { buf, packet_len: 0 }
|
||||
}
|
||||
|
||||
pub const fn packet_len(&self) -> usize {
|
||||
self.packet_len
|
||||
}
|
||||
|
||||
pub fn set_packet_len(&mut self, packet_len: usize) {
|
||||
self.packet_len = packet_len;
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
pub fn buf_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buf
|
||||
}
|
||||
|
||||
/// Packet payload slice, which is inner buffer excluding VirtioNetHdr.
|
||||
pub fn packet(&self) -> &[u8] {
|
||||
debug_assert!(VIRTIO_NET_HDR_LEN + self.packet_len <= self.buf.len());
|
||||
&self.buf[VIRTIO_NET_HDR_LEN..VIRTIO_NET_HDR_LEN + self.packet_len]
|
||||
}
|
||||
|
||||
/// Mutable packet payload slice.
|
||||
pub fn packet_mut(&mut self) -> &mut [u8] {
|
||||
debug_assert!(VIRTIO_NET_HDR_LEN + self.packet_len <= self.buf.len());
|
||||
&mut self.buf[VIRTIO_NET_HDR_LEN..VIRTIO_NET_HDR_LEN + self.packet_len]
|
||||
}
|
||||
|
||||
pub fn virtio_net_header(&self) -> VirtioNetHdr {
|
||||
VirtioNetHdr::from_bytes(&self.buf[..VIRTIO_NET_HDR_LEN])
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffer for transmit packet
|
||||
#[derive(Debug)]
|
||||
pub struct TxBuffer {
|
||||
buf: BytesMut,
|
||||
}
|
||||
|
||||
impl TxBuffer {
|
||||
pub fn with_len(buf_len: usize) -> Self {
|
||||
Self {
|
||||
buf: BytesMut::zeroed(buf_len),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(buf: &[u8]) -> Self {
|
||||
Self {
|
||||
buf: BytesMut::from(buf),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buf(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
pub fn buf_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buf
|
||||
}
|
||||
}
|
82
services/comps/virtio/src/device/network/config.rs
Normal file
82
services/comps/virtio/src/device/network/config.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use bitflags::bitflags;
|
||||
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
|
||||
use jinux_util::frame_ptr::InFramePtr;
|
||||
use pod::Pod;
|
||||
|
||||
use super::device::EthernetAddr;
|
||||
|
||||
bitflags! {
|
||||
/// Virtio Net Feature bits.
|
||||
pub struct NetworkFeatures: u64 {
|
||||
const VIRTIO_NET_F_CSUM = 1 << 0; // Device handles packets with partial checksum.
|
||||
const VIRTIO_NET_F_GUEST_CSUM = 1 << 1; // Driver handles packets with partial checksum
|
||||
const VIRTIO_NET_F_CTRL_GUEST_OFFLOADS = 1 << 2;// Control channel offloads reconfiguration support
|
||||
const VIRTIO_NET_F_MTU = 1 << 3; // Device maximum MTU reporting is supported
|
||||
const VIRTIO_NET_F_MAC = 1 << 5; // Device has given MAC address.
|
||||
const VIRTIO_NET_F_GUEST_TSO4 = 1 << 7; // Driver can receive TSOv4.
|
||||
const VIRTIO_NET_F_GUEST_TSO6 = 1 <<8; // Driver can receive TSOv6.
|
||||
const VIRTIO_NET_F_GUEST_ECN = 1 << 9; // Driver can receive TSO with ECN.
|
||||
const VIRTIO_NET_F_GUEST_UFO = 1 << 10; // Driver can receive UFO.
|
||||
const VIRTIO_NET_F_HOST_TSO4 = 1 << 11; // Device can receive TSOv4.
|
||||
const VIRTIO_NET_F_HOST_TSO6 = 1 <<12; // Device can receive TSOv6.
|
||||
const VIRTIO_NET_F_HOST_ECN = 1 << 13; // Device can receive TSO with ECN.
|
||||
const VIRTIO_NET_F_HOST_UFO = 1 << 14; // Device can receive UFO.
|
||||
const VIRTIO_NET_F_MRG_RXBUF = 1 << 15; // Driver can merge receive buffers.
|
||||
const VIRTIO_NET_F_STATUS = 1 << 16; // Configuration status field is available.
|
||||
const VIRTIO_NET_F_CTRL_VQ = 1 << 17; // Control channel is available.
|
||||
const VIRTIO_NET_F_CTRL_RX = 1 << 18; // Control channel RX mode support.
|
||||
const VIRTIO_NET_F_CTRL_VLAN = 1 << 19; // Control channel VLAN filtering.
|
||||
const VIRTIO_NET_F_EXTRA = 1 << 20; //
|
||||
const VIRTIO_NET_F_GUEST_ANNOUNCE = 1 << 21; // Driver can send gratuitous packets.
|
||||
const VIRTIO_NET_F_MQ = 1 << 22; // Device supports multiqueue with automatic receive steering.
|
||||
const VIRTIO_NET_F_CTRL_MAC_ADDR = 1 << 23; // Set MAC address through control channel.
|
||||
// const VIRTIO_NET_F_HOST_USO = 1 << 56; // Device can receive USO packets.
|
||||
// const VIRTIO_NET_F_HASH_REPORT = 1 << 57; // Device can report per-packet hash value and a type of calculated hash.
|
||||
// const VIRTIO_NET_F_GUEST_HDRLEN = 1 << 59; // Driver can provide the exact hdr_len value. Device benefits from knowing the exact header length.
|
||||
// const VIRTIO_NET_F_RSS = 1 << 60; // Device supports RSS (receive-side scaling) with Toeplitz hash calculation and configurable hash parameters for receive steering.
|
||||
// const VIRTIO_NET_F_RSC_EXT = 1 << 61; // DevicecanprocessduplicatedACKsandreportnumberofcoalescedseg- ments and duplicated ACKs.
|
||||
// const VIRTIO_NET_F_STANDBY = 1 << 62; // Device may act as a standby for a primary device with the same MAC address.
|
||||
// const VIRTIO_NET_F_SPEED_DUPLEX = 1 << 63; // Device reports speed and duplex.
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkFeatures {
|
||||
pub fn support_features() -> Self {
|
||||
NetworkFeatures::VIRTIO_NET_F_MAC | NetworkFeatures::VIRTIO_NET_F_STATUS
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Pod)]
|
||||
pub struct Status: u16 {
|
||||
const VIRTIO_NET_S_LINK_UP = 1;
|
||||
const VIRTIO_NET_S_ANNOUNCE = 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct VirtioNetConfig {
|
||||
pub mac: EthernetAddr,
|
||||
pub status: Status,
|
||||
max_virtqueue_pairs: u16,
|
||||
mtu: u16,
|
||||
speed: u32,
|
||||
duplex: u8,
|
||||
rss_max_key_size: u8,
|
||||
rss_max_indirection_table_length: u16,
|
||||
supported_hash_types: u32,
|
||||
}
|
||||
|
||||
impl VirtioNetConfig {
|
||||
pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option<BAR>; 6]) -> InFramePtr<Self> {
|
||||
let bar = cap.bar;
|
||||
let offset = cap.offset;
|
||||
match bars[bar as usize].expect("Virtio pci net cfg:bar is none") {
|
||||
BAR::Memory(address, _, _, _) => InFramePtr::new(address as usize + offset as usize)
|
||||
.expect("can not get in frame ptr for virtio net config"),
|
||||
BAR::IO(_, _) => panic!("Virtio pci net cfg:bar is IO type"),
|
||||
}
|
||||
}
|
||||
}
|
219
services/comps/virtio/src/device/network/device.rs
Normal file
219
services/comps/virtio/src/device/network/device.rs
Normal file
@ -0,0 +1,219 @@
|
||||
use core::hint::spin_loop;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use jinux_frame::offset_of;
|
||||
use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR};
|
||||
use jinux_util::{frame_ptr::InFramePtr, slot_vec::SlotVec};
|
||||
use log::debug;
|
||||
use pod::Pod;
|
||||
|
||||
use crate::{
|
||||
device::{network::config::NetworkFeatures, VirtioDeviceError},
|
||||
queue::{QueueError, VirtQueue},
|
||||
VirtioPciCommonCfg,
|
||||
};
|
||||
|
||||
use super::{
|
||||
buffer::{RxBuffer, TxBuffer},
|
||||
config::VirtioNetConfig,
|
||||
header::VirtioNetHdr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct EthernetAddr(pub [u8; 6]);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VirtioNetError {
|
||||
NotReady,
|
||||
WrongToken,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub struct NetworkDevice {
|
||||
config: VirtioNetConfig,
|
||||
mac_addr: EthernetAddr,
|
||||
send_queue: VirtQueue,
|
||||
recv_queue: VirtQueue,
|
||||
rx_buffers: SlotVec<RxBuffer>,
|
||||
}
|
||||
|
||||
impl From<QueueError> for VirtioNetError {
|
||||
fn from(value: QueueError) -> Self {
|
||||
match value {
|
||||
QueueError::NotReady => VirtioNetError::NotReady,
|
||||
QueueError::WrongToken => VirtioNetError::WrongToken,
|
||||
_ => VirtioNetError::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkDevice {
|
||||
pub(crate) fn negotiate_features(device_features: u64) -> u64 {
|
||||
let device_features = NetworkFeatures::from_bits_truncate(device_features);
|
||||
let supported_features = NetworkFeatures::support_features();
|
||||
let network_features = device_features & supported_features;
|
||||
debug!("{:?}", network_features);
|
||||
network_features.bits()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
cap: &CapabilityVirtioData,
|
||||
bars: [Option<BAR>; 6],
|
||||
common_cfg: &InFramePtr<VirtioPciCommonCfg>,
|
||||
notify_base_address: usize,
|
||||
notify_off_multiplier: u32,
|
||||
mut msix_vector_left: Vec<u16>,
|
||||
) -> Result<Self, VirtioDeviceError> {
|
||||
let virtio_net_config = VirtioNetConfig::new(cap, bars);
|
||||
let features = {
|
||||
// select low
|
||||
common_cfg.write_at(
|
||||
offset_of!(VirtioPciCommonCfg, device_feature_select),
|
||||
0 as u32,
|
||||
);
|
||||
let device_feature_low =
|
||||
common_cfg.read_at(offset_of!(VirtioPciCommonCfg, device_feature)) as u64;
|
||||
// select high
|
||||
common_cfg.write_at(
|
||||
offset_of!(VirtioPciCommonCfg, device_feature_select),
|
||||
1 as u32,
|
||||
);
|
||||
let device_feature_high =
|
||||
common_cfg.read_at(offset_of!(VirtioPciCommonCfg, device_feature)) as u64;
|
||||
let device_feature = device_feature_high << 32 | device_feature_low;
|
||||
NetworkFeatures::from_bits_truncate(Self::negotiate_features(device_feature))
|
||||
};
|
||||
debug!("virtio_net_config = {:?}", virtio_net_config);
|
||||
debug!("features = {:?}", features);
|
||||
let mac_addr = virtio_net_config.read_at(offset_of!(VirtioNetConfig, mac));
|
||||
let status = virtio_net_config.read_at(offset_of!(VirtioNetConfig, status));
|
||||
debug!("mac addr = {:x?}, status = {:?}", mac_addr, status);
|
||||
let (recv_msix_vec, send_msix_vec) = {
|
||||
if msix_vector_left.len() >= 2 {
|
||||
let vector1 = msix_vector_left.pop().unwrap();
|
||||
let vector2 = msix_vector_left.pop().unwrap();
|
||||
(vector1, vector2)
|
||||
} else {
|
||||
let vector = msix_vector_left.pop().unwrap();
|
||||
(vector, vector)
|
||||
}
|
||||
};
|
||||
let mut recv_queue = VirtQueue::new(
|
||||
&common_cfg,
|
||||
QUEUE_RECV as usize,
|
||||
QUEUE_SIZE,
|
||||
notify_base_address,
|
||||
notify_off_multiplier,
|
||||
recv_msix_vec,
|
||||
)
|
||||
.expect("creating recv queue fails");
|
||||
let send_queue = VirtQueue::new(
|
||||
&common_cfg,
|
||||
QUEUE_SEND as usize,
|
||||
QUEUE_SIZE,
|
||||
notify_base_address,
|
||||
notify_off_multiplier,
|
||||
send_msix_vec,
|
||||
)
|
||||
.expect("create send queue fails");
|
||||
|
||||
let mut rx_buffers = SlotVec::new();
|
||||
for i in 0..QUEUE_SIZE {
|
||||
let mut rx_buffer = RxBuffer::new(RX_BUFFER_LEN);
|
||||
let token = recv_queue.add(&[], &mut [rx_buffer.buf_mut()])?;
|
||||
assert_eq!(i, token);
|
||||
assert_eq!(rx_buffers.put(rx_buffer) as u16, i);
|
||||
}
|
||||
|
||||
if recv_queue.should_notify() {
|
||||
debug!("notify receive queue");
|
||||
recv_queue.notify();
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
config: virtio_net_config.read(),
|
||||
mac_addr,
|
||||
send_queue,
|
||||
recv_queue,
|
||||
rx_buffers,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a rx buffer to recv queue
|
||||
fn add_rx_buffer(&mut self, mut rx_buffer: RxBuffer) -> Result<(), VirtioNetError> {
|
||||
let token = self.recv_queue.add(&[], &mut [rx_buffer.buf_mut()])?;
|
||||
assert!(self.rx_buffers.put_at(token as usize, rx_buffer).is_none());
|
||||
if self.recv_queue.should_notify() {
|
||||
self.recv_queue.notify();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Acknowledge interrupt
|
||||
pub fn ack_interrupt(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// The mac address
|
||||
pub fn mac_addr(&self) -> EthernetAddr {
|
||||
self.mac_addr
|
||||
}
|
||||
|
||||
/// Send queue is ready
|
||||
pub fn can_send(&self) -> bool {
|
||||
self.send_queue.available_desc() >= 2
|
||||
}
|
||||
|
||||
/// Receive queue is ready
|
||||
pub fn can_receive(&self) -> bool {
|
||||
self.recv_queue.can_pop()
|
||||
}
|
||||
|
||||
/// Receive a packet from network. If packet is ready, returns a RxBuffer containing the packet.
|
||||
/// Otherwise, return NotReady error.
|
||||
pub fn receive(&mut self) -> Result<RxBuffer, VirtioNetError> {
|
||||
let (token, len) = self.recv_queue.pop_used()?;
|
||||
debug!("receive packet: token = {}, len = {}", token, len);
|
||||
let mut rx_buffer = self
|
||||
.rx_buffers
|
||||
.remove(token as usize)
|
||||
.ok_or(VirtioNetError::WrongToken)?;
|
||||
rx_buffer.set_packet_len(len as usize);
|
||||
// FIXME: Ideally, we can reuse the returned buffer without creating new buffer.
|
||||
// But this requires locking device to be compatible with smoltcp interface.
|
||||
let new_rx_buffer = RxBuffer::new(RX_BUFFER_LEN);
|
||||
self.add_rx_buffer(new_rx_buffer)?;
|
||||
Ok(rx_buffer)
|
||||
}
|
||||
|
||||
/// Send a packet to network. Return until the request completes.
|
||||
pub fn send(&mut self, tx_buffer: TxBuffer) -> Result<(), VirtioNetError> {
|
||||
let header = VirtioNetHdr::default();
|
||||
let token = self
|
||||
.send_queue
|
||||
.add(&[header.as_bytes(), tx_buffer.buf()], &mut [])?;
|
||||
|
||||
if self.send_queue.should_notify() {
|
||||
self.send_queue.notify();
|
||||
}
|
||||
// Wait until the buffer is used
|
||||
while !self.send_queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
// Pop out the buffer, so we can reuse the send queue further
|
||||
let (pop_token, _) = self.send_queue.pop_used()?;
|
||||
debug_assert!(pop_token == token);
|
||||
if pop_token != token {
|
||||
return Err(VirtioNetError::WrongToken);
|
||||
}
|
||||
debug!("send packet succeeds");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const QUEUE_RECV: u16 = 0;
|
||||
const QUEUE_SEND: u16 = 1;
|
||||
|
||||
const QUEUE_SIZE: u16 = 64;
|
||||
const RX_BUFFER_LEN: usize = 4096;
|
44
services/comps/virtio/src/device/network/header.rs
Normal file
44
services/comps/virtio/src/device/network/header.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use bitflags::bitflags;
|
||||
use int_to_c_enum::TryFromInt;
|
||||
use pod::Pod;
|
||||
|
||||
pub const VIRTIO_NET_HDR_LEN: usize = core::mem::size_of::<VirtioNetHdr>();
|
||||
|
||||
/// VirtioNet header precedes each packet
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone, Copy, Pod)]
|
||||
pub struct VirtioNetHdr {
|
||||
flags: Flags,
|
||||
gso_type: u8,
|
||||
hdr_len: u16,
|
||||
gso_size: u16,
|
||||
csum_start: u16,
|
||||
csum_offset: u16,
|
||||
num_buffers: u16, // Only if PCI is modern or VIRTIO_NET_F_MRG_RXBUF negotiated
|
||||
// hash_value: u32, // Only if VIRTIO_NET_F_HASH_REPORT negotiated
|
||||
// hash_report: u16, // Only if VIRTIO_NET_F_HASH_REPORT negotiated
|
||||
// padding_reserved: u16, // Only if VIRTIO_NET_F_HASH_REPORT negotiated
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Default, Pod)]
|
||||
pub struct Flags: u8 {
|
||||
const VIRTIO_NET_HDR_F_NEEDS_CSUM = 1;
|
||||
const VIRTIO_NET_HDR_F_DATA_VALID = 2;
|
||||
const VIRTIO_NET_HDR_F_RSC_INFO = 4;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Default, Debug, Clone, Copy, TryFromInt)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum GsoType {
|
||||
#[default]
|
||||
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_UDP_L4 = 5,
|
||||
VIRTIO_NET_HDR_GSO_ECN = 0x80,
|
||||
}
|
4
services/comps/virtio/src/device/network/mod.rs
Normal file
4
services/comps/virtio/src/device/network/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod buffer;
|
||||
pub mod config;
|
||||
pub mod device;
|
||||
pub mod header;
|
@ -202,7 +202,7 @@ pub enum VirtioDeviceType {
|
||||
impl VirtioDeviceType {
|
||||
pub fn from_virtio_device(device: &VirtioDevice) -> Self {
|
||||
match device {
|
||||
VirtioDevice::Network => VirtioDeviceType::Network,
|
||||
VirtioDevice::Network(_) => VirtioDeviceType::Network,
|
||||
VirtioDevice::Block(_) => VirtioDeviceType::Block,
|
||||
VirtioDevice::Console => VirtioDeviceType::Console,
|
||||
VirtioDevice::Entropy => VirtioDeviceType::Entropy,
|
||||
|
Reference in New Issue
Block a user