Add virtio legacy interface

This commit is contained in:
Qingsong Chen
2024-11-21 09:23:10 +00:00
committed by Tate, Hongliang Tian
parent 495c93c2ad
commit 81898362b6
19 changed files with 844 additions and 191 deletions

View File

@ -1,6 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec, vec::Vec};
use alloc::{
boxed::Box,
collections::BTreeMap,
string::{String, ToString},
sync::Arc,
vec,
vec::Vec,
};
use core::{fmt::Debug, hint::spin_loop, mem::size_of};
use aster_block::{
@ -8,11 +15,9 @@ use aster_block::{
request_queue::{BioRequest, BioRequestSingleQueue},
BlockDeviceMeta,
};
use aster_util::safe_ptr::SafePtr;
use id_alloc::IdAlloc;
use log::info;
use log::{debug, info};
use ostd::{
io_mem::IoMem,
mm::{DmaDirection, DmaStream, DmaStreamSlice, FrameAllocOptions, VmIo},
sync::SpinLock,
trap::TrapFrame,
@ -26,7 +31,7 @@ use crate::{
VirtioDeviceError,
},
queue::VirtQueue,
transport::VirtioTransport,
transport::{ConfigManager, VirtioTransport},
};
#[derive(Debug)]
@ -39,8 +44,14 @@ pub struct BlockDevice {
impl BlockDevice {
/// Creates a new VirtIO-Block driver and registers it.
pub(crate) fn init(transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
let is_legacy = transport.is_legacy_version();
let device = DeviceInner::init(transport)?;
let device_id = device.request_device_id();
let device_id = if is_legacy {
// FIXME: legacy device do not support `GetId` request.
"legacy_blk".to_string()
} else {
device.request_device_id()
};
let block_device = Arc::new(Self {
device,
@ -70,9 +81,9 @@ impl BlockDevice {
/// Negotiate features for the device specified bits 0~23
pub(crate) fn negotiate_features(features: u64) -> u64 {
let feature = BlockFeatures::from_bits(features).unwrap();
let support_features = BlockFeatures::from_bits(features).unwrap();
(feature & support_features).bits
let mut support_features = BlockFeatures::from_bits_truncate(features);
support_features.remove(BlockFeatures::MQ);
support_features.bits
}
}
@ -84,14 +95,14 @@ impl aster_block::BlockDevice for BlockDevice {
fn metadata(&self) -> BlockDeviceMeta {
BlockDeviceMeta {
max_nr_segments_per_bio: self.queue.max_nr_segments_per_bio(),
nr_sectors: VirtioBlockConfig::read_capacity_sectors(&self.device.config).unwrap(),
nr_sectors: self.device.config_manager.capacity_sectors(),
}
}
}
#[derive(Debug)]
struct DeviceInner {
config: SafePtr<VirtioBlockConfig, IoMem>,
config_manager: ConfigManager<VirtioBlockConfig>,
features: VirtioBlockFeature,
queue: SpinLock<VirtQueue>,
transport: SpinLock<Box<dyn VirtioTransport>>,
@ -106,9 +117,10 @@ impl DeviceInner {
/// Creates and inits the device.
pub fn init(mut transport: Box<dyn VirtioTransport>) -> Result<Arc<Self>, VirtioDeviceError> {
let config = VirtioBlockConfig::new(transport.as_mut());
let config_manager = VirtioBlockConfig::new_manager(transport.as_ref());
debug!("virio_blk_config = {:?}", config_manager.read_config());
assert_eq!(
VirtioBlockConfig::read_block_size(&config).unwrap(),
config_manager.block_size(),
VirtioBlockConfig::sector_size(),
"currently not support customized device logical block size"
);
@ -138,7 +150,7 @@ impl DeviceInner {
assert!(Self::QUEUE_SIZE as usize * RESP_SIZE <= block_responses.nbytes());
let device = Arc::new(Self {
config,
config_manager,
features,
queue: SpinLock::new(queue),
transport: SpinLock::new(transport),

View File

@ -2,13 +2,15 @@
pub mod device;
use core::mem::offset_of;
use aster_block::SECTOR_SIZE;
use aster_util::{field_ptr, safe_ptr::SafePtr};
use aster_util::safe_ptr::SafePtr;
use bitflags::bitflags;
use int_to_c_enum::TryFromInt;
use ostd::{io_mem::IoMem, Pod};
use ostd::Pod;
use crate::transport::VirtioTransport;
use crate::transport::{ConfigManager, VirtioTransport};
pub static DEVICE_NAME: &str = "Virtio-Block";
@ -73,7 +75,9 @@ pub struct VirtioBlockConfig {
topology: VirtioBlockTopology,
/// Writeback mode.
writeback: u8,
unused0: [u8; 3],
unused0: u8,
/// The number of virtqueues.
num_queues: u16,
/// The maximum discard sectors for one segment.
max_discard_sectors: u32,
/// The maximum number of discard segments in a discard command.
@ -118,27 +122,78 @@ pub struct VirtioBlockFeature {
}
impl VirtioBlockConfig {
pub(self) fn new(transport: &dyn VirtioTransport) -> SafePtr<Self, IoMem> {
let memory = transport.device_config_memory();
SafePtr::new(memory, 0)
pub(self) fn new_manager(transport: &dyn VirtioTransport) -> ConfigManager<Self> {
let safe_ptr = transport
.device_config_mem()
.map(|mem| SafePtr::new(mem, 0));
let bar_space = transport.device_config_bar();
ConfigManager::new(safe_ptr, bar_space)
}
pub(self) const fn sector_size() -> usize {
SECTOR_SIZE
}
}
pub(self) fn read_block_size(this: &SafePtr<Self, IoMem>) -> ostd::prelude::Result<usize> {
field_ptr!(this, Self, blk_size)
.read_once()
.map(|val| val as usize)
impl ConfigManager<VirtioBlockConfig> {
pub(super) fn read_config(&self) -> VirtioBlockConfig {
let mut blk_config = VirtioBlockConfig::new_uninit();
// Only following fields are defined in legacy interface.
let cap_low = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, capacity))
.unwrap() as u64;
let cap_high = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, capacity) + 4)
.unwrap() as u64;
blk_config.capacity = cap_high << 32 | cap_low;
blk_config.size_max = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, size_max))
.unwrap();
blk_config.seg_max = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, seg_max))
.unwrap();
blk_config.geometry.cylinders = self
.read_once::<u16>(
offset_of!(VirtioBlockConfig, geometry)
+ offset_of!(VirtioBlockGeometry, cylinders),
)
.unwrap();
blk_config.geometry.heads = self
.read_once::<u8>(
offset_of!(VirtioBlockConfig, geometry) + offset_of!(VirtioBlockGeometry, heads),
)
.unwrap();
blk_config.geometry.sectors = self
.read_once::<u8>(
offset_of!(VirtioBlockConfig, geometry) + offset_of!(VirtioBlockGeometry, sectors),
)
.unwrap();
blk_config.blk_size = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, blk_size))
.unwrap();
if self.is_modern() {
// TODO: read more field if modern interface exists.
}
blk_config
}
pub(self) fn read_capacity_sectors(
this: &SafePtr<Self, IoMem>,
) -> ostd::prelude::Result<usize> {
field_ptr!(this, Self, capacity)
.read_once()
.map(|val| val as usize)
pub(self) fn block_size(&self) -> usize {
self.read_once::<u32>(offset_of!(VirtioBlockConfig, blk_size))
.unwrap() as usize
}
pub(self) fn capacity_sectors(&self) -> usize {
let cap_low = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, capacity))
.unwrap() as usize;
let cap_high = self
.read_once::<u32>(offset_of!(VirtioBlockConfig, capacity) + 4)
.unwrap() as usize;
cap_high << 32 | cap_low
}
}

View File

@ -1,9 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
use aster_util::safe_ptr::SafePtr;
use ostd::{io_mem::IoMem, Pod};
use core::mem::offset_of;
use crate::transport::VirtioTransport;
use aster_util::safe_ptr::SafePtr;
use ostd::Pod;
use crate::transport::{ConfigManager, VirtioTransport};
bitflags::bitflags! {
pub struct ConsoleFeatures: u64{
@ -22,14 +24,49 @@ bitflags::bitflags! {
#[repr(C)]
pub struct VirtioConsoleConfig {
pub cols: u16,
pub row: u16,
pub rows: u16,
pub max_nr_ports: u32,
pub emerg_wr: u32,
}
impl VirtioConsoleConfig {
pub(super) fn new(transport: &dyn VirtioTransport) -> SafePtr<Self, IoMem> {
let memory = transport.device_config_memory();
SafePtr::new(memory, 0)
pub(super) fn new_manager(transport: &dyn VirtioTransport) -> ConfigManager<Self> {
let safe_ptr = transport
.device_config_mem()
.map(|mem| SafePtr::new(mem, 0));
let bar_space = transport.device_config_bar();
ConfigManager::new(safe_ptr, bar_space)
}
}
impl ConfigManager<VirtioConsoleConfig> {
pub(super) fn read_config(&self) -> VirtioConsoleConfig {
let mut console_config = VirtioConsoleConfig::new_uninit();
// Only following fields are defined in legacy interface.
console_config.cols = self
.read_once::<u16>(offset_of!(VirtioConsoleConfig, cols))
.unwrap();
console_config.rows = self
.read_once::<u16>(offset_of!(VirtioConsoleConfig, rows))
.unwrap();
console_config.max_nr_ports = self
.read_once::<u32>(offset_of!(VirtioConsoleConfig, max_nr_ports))
.unwrap();
console_config
}
/// Performs an emergency write.
///
/// According to the VirtIO spec 5.3.4:
///
/// If `VIRTIO_CONSOLE_F_EMERG_WRITE` is supported then the driver can
/// use emergency write to output a single character without initializing
/// virtio queues, or even acknowledging the feature.
pub(super) fn emerg_write(&self, value: u32) {
if self.is_modern() {
self.write_once(offset_of!(VirtioConsoleConfig, emerg_wr), value)
.unwrap();
}
}
}

View File

@ -4,10 +4,8 @@ use alloc::{boxed::Box, fmt::Debug, string::ToString, sync::Arc, vec::Vec};
use core::hint::spin_loop;
use aster_console::{AnyConsoleDevice, ConsoleCallback};
use aster_util::safe_ptr::SafePtr;
use log::debug;
use ostd::{
io_mem::IoMem,
mm::{DmaDirection, DmaStream, DmaStreamSlice, FrameAllocOptions, VmReader},
sync::{LocalIrqDisabled, RwLock, SpinLock},
trap::TrapFrame,
@ -17,11 +15,11 @@ use super::{config::VirtioConsoleConfig, DEVICE_NAME};
use crate::{
device::{console::config::ConsoleFeatures, VirtioDeviceError},
queue::VirtQueue,
transport::VirtioTransport,
transport::{ConfigManager, VirtioTransport},
};
pub struct ConsoleDevice {
config: SafePtr<VirtioConsoleConfig, IoMem>,
config_manager: ConfigManager<VirtioConsoleConfig>,
transport: SpinLock<Box<dyn VirtioTransport>>,
receive_queue: SpinLock<VirtQueue>,
transmit_queue: SpinLock<VirtQueue>,
@ -61,7 +59,7 @@ impl AnyConsoleDevice for ConsoleDevice {
impl Debug for ConsoleDevice {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ConsoleDevice")
.field("config", &self.config)
.field("config", &self.config_manager.read_config())
.field("transport", &self.transport)
.field("receive_queue", &self.receive_queue)
.field("transmit_queue", &self.transmit_queue)
@ -78,7 +76,9 @@ impl ConsoleDevice {
}
pub fn init(mut transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
let config = VirtioConsoleConfig::new(transport.as_ref());
let config_manager = VirtioConsoleConfig::new_manager(transport.as_ref());
debug!("virtio_console_config = {:?}", config_manager.read_config());
const RECV0_QUEUE_INDEX: u16 = 0;
const TRANSMIT0_QUEUE_INDEX: u16 = 1;
let receive_queue =
@ -97,7 +97,7 @@ impl ConsoleDevice {
};
let device = Arc::new(Self {
config,
config_manager,
transport: SpinLock::new(transport),
receive_queue,
transmit_queue,

View File

@ -77,7 +77,7 @@ pub struct VirtioInputConfig {
impl VirtioInputConfig {
pub(self) fn new(transport: &dyn VirtioTransport) -> SafePtr<Self, IoMem> {
let memory = transport.device_config_memory();
let memory = transport.device_config_mem().unwrap();
SafePtr::new(memory, 0)
}
}

View File

@ -1,11 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
use aster_network::EthernetAddr;
use aster_util::{field_ptr, safe_ptr::SafePtr};
use bitflags::bitflags;
use ostd::{io_mem::IoMem, offset_of, Pod};
use core::mem::offset_of;
use crate::transport::VirtioTransport;
use aster_network::EthernetAddr;
use aster_util::safe_ptr::SafePtr;
use bitflags::bitflags;
use ostd::Pod;
use crate::transport::{ConfigManager, VirtioTransport};
bitflags! {
/// Virtio Net Feature bits.
@ -77,38 +79,55 @@ pub struct VirtioNetConfig {
}
impl VirtioNetConfig {
pub(super) fn new(transport: &dyn VirtioTransport) -> SafePtr<Self, IoMem> {
let memory = transport.device_config_memory();
SafePtr::new(memory, 0)
}
pub(super) fn read(this: &SafePtr<Self, IoMem>) -> ostd::prelude::Result<Self> {
// TODO: Add a general API to read this byte-by-byte.
let mac_data = {
let mut mac_data: [u8; 6] = [0; 6];
let mut mac_ptr = field_ptr!(this, Self, mac).cast::<u8>();
for byte in &mut mac_data {
*byte = mac_ptr.read_once().unwrap();
mac_ptr.byte_add(1);
}
mac_data
};
Ok(Self {
mac: aster_network::EthernetAddr(mac_data),
status: field_ptr!(this, Self, status).read_once()?,
max_virtqueue_pairs: field_ptr!(this, Self, max_virtqueue_pairs).read_once()?,
mtu: field_ptr!(this, Self, mtu).read_once()?,
speed: field_ptr!(this, Self, speed).read_once()?,
duplex: field_ptr!(this, Self, duplex).read_once()?,
rss_max_key_size: field_ptr!(this, Self, rss_max_key_size).read_once()?,
rss_max_indirection_table_length: field_ptr!(
this,
Self,
rss_max_indirection_table_length
)
.read_once()?,
supported_hash_types: field_ptr!(this, Self, supported_hash_types).read_once()?,
})
pub(super) fn new_manager(transport: &dyn VirtioTransport) -> ConfigManager<Self> {
let safe_ptr = transport
.device_config_mem()
.map(|mem| SafePtr::new(mem, 0));
let bar_space = transport.device_config_bar();
ConfigManager::new(safe_ptr, bar_space)
}
}
impl ConfigManager<VirtioNetConfig> {
pub(super) fn read_config(&self) -> VirtioNetConfig {
let mut net_config = VirtioNetConfig::new_uninit();
// Only following fields are defined in legacy interface.
for i in 0..6 {
net_config.mac.0[i] = self
.read_once::<u8>(offset_of!(VirtioNetConfig, mac) + i)
.unwrap();
}
net_config.status.bits = self
.read_once::<u16>(offset_of!(VirtioNetConfig, status))
.unwrap();
if self.is_modern() {
net_config.max_virtqueue_pairs = self
.read_once::<u16>(offset_of!(VirtioNetConfig, max_virtqueue_pairs))
.unwrap();
net_config.mtu = self
.read_once::<u16>(offset_of!(VirtioNetConfig, mtu))
.unwrap();
net_config.speed = self
.read_once::<u32>(offset_of!(VirtioNetConfig, speed))
.unwrap();
net_config.duplex = self
.read_once::<u8>(offset_of!(VirtioNetConfig, duplex))
.unwrap();
net_config.rss_max_key_size = self
.read_once::<u8>(offset_of!(VirtioNetConfig, rss_max_key_size))
.unwrap();
net_config.rss_max_indirection_table_length = self
.read_once::<u16>(offset_of!(
VirtioNetConfig,
rss_max_indirection_table_length
))
.unwrap();
net_config.supported_hash_types = self
.read_once::<u32>(offset_of!(VirtioNetConfig, supported_hash_types))
.unwrap();
}
net_config
}
}

View File

@ -21,11 +21,11 @@ use super::{config::VirtioNetConfig, header::VirtioNetHdr};
use crate::{
device::{network::config::NetworkFeatures, VirtioDeviceError},
queue::{QueueError, VirtQueue},
transport::VirtioTransport,
transport::{ConfigManager, VirtioTransport},
};
pub struct NetworkDevice {
config: VirtioNetConfig,
config_manager: ConfigManager<VirtioNetConfig>,
// For smoltcp use
caps: DeviceCapabilities,
mac_addr: EthernetAddr,
@ -57,16 +57,15 @@ impl NetworkDevice {
}
pub fn init(mut transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
let virtio_net_config = VirtioNetConfig::new(transport.as_mut());
let config_manager = VirtioNetConfig::new_manager(transport.as_ref());
let config = config_manager.read_config();
debug!("virtio_net_config = {:?}", config);
let mac_addr = config.mac;
let features = NetworkFeatures::from_bits_truncate(Self::negotiate_features(
transport.read_device_features(),
));
debug!("virtio_net_config = {:?}", virtio_net_config);
debug!("features = {:?}", features);
let config = VirtioNetConfig::read(&virtio_net_config).unwrap();
let mac_addr = config.mac;
debug!("mac addr = {:x?}, status = {:?}", mac_addr, config.status);
let caps = init_caps(&features, &config);
let mut send_queue = VirtQueue::new(QUEUE_SEND, QUEUE_SIZE, transport.as_mut())
@ -94,7 +93,7 @@ impl NetworkDevice {
}
let mut device = Self {
config,
config_manager,
caps,
mac_addr,
send_queue,
@ -294,7 +293,7 @@ impl AnyNetworkDevice for NetworkDevice {
impl Debug for NetworkDevice {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("NetworkDevice")
.field("config", &self.config)
.field("config", &self.config_manager.read_config())
.field("mac_addr", &self.mac_addr)
.field("send_queue", &self.send_queue)
.field("recv_queue", &self.recv_queue)

View File

@ -34,7 +34,7 @@ pub struct VirtioVsockConfig {
impl VirtioVsockConfig {
pub(crate) fn new(transport: &dyn VirtioTransport) -> SafePtr<Self, IoMem> {
let memory = transport.device_config_memory();
let memory = transport.device_config_mem().unwrap();
SafePtr::new(memory, 0)
}
}