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)
}
}

View File

@ -11,6 +11,7 @@
extern crate alloc;
use alloc::boxed::Box;
use core::hint::spin_loop;
use bitflags::bitflags;
use component::{init_component, ComponentInitError};
@ -43,6 +44,10 @@ fn virtio_component_init() -> Result<(), ComponentInitError> {
transport
.write_device_status(DeviceStatus::empty())
.unwrap();
while transport.read_device_status() != DeviceStatus::empty() {
spin_loop();
}
// Set to acknowledge
transport
.write_device_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER)
@ -50,12 +55,13 @@ fn virtio_component_init() -> Result<(), ComponentInitError> {
// negotiate features
negotiate_features(&mut transport);
// change to features ok status
transport
.write_device_status(
DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK,
)
.unwrap();
if !transport.is_legacy_version() {
// change to features ok status
let status =
DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK;
transport.write_device_status(status).unwrap();
}
let device_type = transport.device_type();
let res = match transport.device_type() {
VirtioDeviceType::Block => BlockDevice::init(transport),
@ -80,7 +86,7 @@ fn virtio_component_init() -> Result<(), ComponentInitError> {
fn pop_device_transport() -> Option<Box<dyn VirtioTransport>> {
if let Some(device) = VIRTIO_PCI_DRIVER.get().unwrap().pop_device_transport() {
return Some(Box::new(device));
return Some(device);
}
if let Some(device) = VIRTIO_MMIO_DRIVER.get().unwrap().pop_device_transport() {
return Some(Box::new(device));

View File

@ -13,12 +13,14 @@ use aster_util::{field_ptr, safe_ptr::SafePtr};
use bitflags::bitflags;
use log::debug;
use ostd::{
io_mem::IoMem,
mm::{DmaCoherent, FrameAllocOptions},
offset_of, Pod,
};
use crate::{dma_buf::DmaBuf, transport::VirtioTransport};
use crate::{
dma_buf::DmaBuf,
transport::{pci::legacy::VirtioPciLegacyTransport, ConfigManager, VirtioTransport},
};
#[derive(Debug)]
pub enum QueueError {
@ -40,8 +42,8 @@ pub struct VirtQueue {
avail: SafePtr<AvailRing, DmaCoherent>,
/// Used ring
used: SafePtr<UsedRing, DmaCoherent>,
/// point to notify address
notify: SafePtr<u32, IoMem>,
/// Notify configuration manager
notify_config: ConfigManager<u32>,
/// The index of queue
queue_idx: u32,
@ -66,7 +68,7 @@ impl VirtQueue {
/// Create a new VirtQueue.
pub(crate) fn new(
idx: u16,
size: u16,
mut size: u16,
transport: &mut dyn VirtioTransport,
) -> Result<Self, QueueError> {
if !size.is_power_of_two() {
@ -74,17 +76,27 @@ impl VirtQueue {
}
let (descriptor_ptr, avail_ring_ptr, used_ring_ptr) = if transport.is_legacy_version() {
// FIXME: How about pci legacy?
// Currently, we use one Frame to place the descriptors and available rings, one Frame to place used rings
// because the virtio-mmio legacy required the address to be continuous. The max queue size is 128.
if size > 128 {
return Err(QueueError::InvalidArgs);
}
let desc_size = size_of::<Descriptor>() * size as usize;
let queue_size = transport.max_queue_size(idx).unwrap() as usize;
let desc_size = size_of::<Descriptor>() * queue_size;
size = queue_size as u16;
let (seg1, seg2) = {
let segment = FrameAllocOptions::new(2).alloc_contiguous().unwrap();
segment.split(ostd::mm::PAGE_SIZE)
let align_size = VirtioPciLegacyTransport::QUEUE_ALIGN_SIZE;
let total_frames =
VirtioPciLegacyTransport::calc_virtqueue_size_aligned(queue_size) / align_size;
let continue_segment = FrameAllocOptions::new(total_frames)
.alloc_contiguous()
.unwrap();
let avial_size = size_of::<u16>() * (3 + queue_size);
let seg1_frames = (desc_size + avial_size).div_ceil(align_size);
continue_segment.split(seg1_frames * align_size)
};
let desc_frame_ptr: SafePtr<Descriptor, DmaCoherent> =
SafePtr::new(DmaCoherent::map(seg1, true).unwrap(), 0);
@ -141,7 +153,7 @@ impl VirtQueue {
}
}
let notify = transport.get_notify_ptr(idx).unwrap();
let notify_config = transport.notify_config(idx as usize);
field_ptr!(&avail_ring_ptr, AvailRing, flags)
.write_once(&AvailFlags::empty())
.unwrap();
@ -149,7 +161,7 @@ impl VirtQueue {
descs,
avail: avail_ring_ptr,
used: used_ring_ptr,
notify,
notify_config,
queue_size: size,
queue_idx: idx as u32,
num_used: 0,
@ -343,7 +355,15 @@ impl VirtQueue {
/// notify that there are available rings
pub fn notify(&mut self) {
self.notify.write_once(&self.queue_idx).unwrap();
if self.notify_config.is_modern() {
self.notify_config
.write_once::<u32>(0, self.queue_idx)
.unwrap();
} else {
self.notify_config
.write_once::<u16>(0, self.queue_idx as u16)
.unwrap();
}
}
/// Disables registered callbacks.

View File

@ -7,9 +7,12 @@ use aster_rights::{ReadOp, WriteOp};
use aster_util::{field_ptr, safe_ptr::SafePtr};
use log::warn;
use ostd::{
bus::mmio::{
bus::MmioDevice,
common_device::{MmioCommonDevice, VirtioMmioVersion},
bus::{
mmio::{
bus::MmioDevice,
common_device::{MmioCommonDevice, VirtioMmioVersion},
},
pci::cfg_space::Bar,
},
io_mem::IoMem,
mm::{DmaCoherent, PAGE_SIZE},
@ -21,7 +24,7 @@ use ostd::{
use super::{layout::VirtioMmioLayout, multiplex::MultiplexIrq};
use crate::{
queue::{AvailRing, Descriptor, UsedRing},
transport::{DeviceStatus, VirtioTransport, VirtioTransportError},
transport::{ConfigManager, DeviceStatus, VirtioTransport, VirtioTransportError},
VirtioDeviceType,
};
@ -170,9 +173,11 @@ impl VirtioTransport for VirtioMmioTransport {
Ok(())
}
fn get_notify_ptr(&self, _idx: u16) -> Result<SafePtr<u32, IoMem>, VirtioTransportError> {
fn notify_config(&self, _idx: usize) -> ConfigManager<u32> {
let offset = offset_of!(VirtioMmioLayout, queue_notify) as usize;
Ok(SafePtr::new(self.common_device.io_mem().clone(), offset))
let safe_ptr = Some(SafePtr::new(self.common_device.io_mem().clone(), offset));
ConfigManager::new(safe_ptr, None)
}
fn num_queues(&self) -> u16 {
@ -196,9 +201,13 @@ impl VirtioTransport for VirtioMmioTransport {
todo!()
}
fn device_config_memory(&self) -> IoMem {
fn device_config_mem(&self) -> Option<IoMem> {
// offset: 0x100~0x200
self.common_device.io_mem().slice(0x100..0x200)
Some(self.common_device.io_mem().slice(0x100..0x200))
}
fn device_config_bar(&self) -> Option<(Bar, usize)> {
None
}
fn read_device_features(&self) -> u64 {

View File

@ -4,7 +4,14 @@ use alloc::boxed::Box;
use core::fmt::Debug;
use aster_util::safe_ptr::SafePtr;
use ostd::{io_mem::IoMem, mm::DmaCoherent, trap::IrqCallbackFunction};
use ostd::{
arch::device::io_port::{PortRead, PortWrite},
bus::pci::cfg_space::Bar,
io_mem::IoMem,
mm::{DmaCoherent, PodOnce},
trap::IrqCallbackFunction,
Pod,
};
use self::{mmio::virtio_mmio_init, pci::virtio_pci_init};
use crate::{
@ -51,7 +58,10 @@ pub trait VirtioTransport: Sync + Send + Debug {
}
/// Get access to the device config memory.
fn device_config_memory(&self) -> IoMem;
fn device_config_mem(&self) -> Option<IoMem>;
/// Get access to the device config BAR space.
fn device_config_bar(&self) -> Option<(Bar, usize)>;
// ====================Virtqueue related APIs====================
@ -71,9 +81,9 @@ pub trait VirtioTransport: Sync + Send + Debug {
/// The max queue size of one virtqueue.
fn max_queue_size(&self, idx: u16) -> Result<u16, VirtioTransportError>;
/// Get notify pointer of a virtqueue. User should send notification (e.g. write 0 to the pointer)
/// Get notify manager of a virtqueue. User should send notification (e.g. write 0 to the pointer)
/// after it add buffers into the corresponding virtqueue.
fn get_notify_ptr(&self, idx: u16) -> Result<SafePtr<u32, IoMem>, VirtioTransportError>;
fn notify_config(&self, idx: usize) -> ConfigManager<u32>;
fn is_legacy_version(&self) -> bool;
@ -99,6 +109,115 @@ pub trait VirtioTransport: Sync + Send + Debug {
) -> Result<(), VirtioTransportError>;
}
/// Manage PCI device/notify configuration space (legacy/modern).
#[derive(Debug)]
pub struct ConfigManager<T: Pod> {
modern_space: Option<SafePtr<T, IoMem>>,
legacy_space: Option<(Bar, usize)>,
}
impl<T: Pod> ConfigManager<T> {
pub(super) fn new(
modern_space: Option<SafePtr<T, IoMem>>,
legacy_space: Option<(Bar, usize)>,
) -> Self {
Self {
modern_space,
legacy_space,
}
}
/// Return if the modern configuration space exists.
pub(super) fn is_modern(&self) -> bool {
self.modern_space.is_some()
}
fn read_modern<V: PodOnce + PortRead>(&self, offset: usize) -> Result<V, VirtioTransportError> {
let Some(safe_ptr) = self.modern_space.as_ref() else {
return Err(VirtioTransportError::InvalidArgs);
};
let field_ptr: SafePtr<V, &IoMem> = {
let mut ptr = safe_ptr.borrow_vm();
ptr.byte_add(offset);
ptr.cast()
};
field_ptr
.read_once()
.map_err(|_| VirtioTransportError::DeviceStatusError)
}
fn read_legacy<V: PodOnce + PortRead>(&self, offset: usize) -> Result<V, VirtioTransportError> {
let Some((bar, base)) = self.legacy_space.as_ref() else {
return Err(VirtioTransportError::InvalidArgs);
};
bar.read_once(base + offset)
.map_err(|_| VirtioTransportError::DeviceStatusError)
}
/// Read a specific configuration.
pub(super) fn read_once<V: PodOnce + PortRead>(
&self,
offset: usize,
) -> Result<V, VirtioTransportError> {
debug_assert!(offset + size_of::<V>() <= size_of::<T>());
if self.is_modern() {
self.read_modern(offset)
} else {
self.read_legacy(offset)
}
}
fn write_modern<V: PodOnce + PortWrite>(
&self,
offset: usize,
value: V,
) -> Result<(), VirtioTransportError> {
let Some(safe_ptr) = self.modern_space.as_ref() else {
return Err(VirtioTransportError::InvalidArgs);
};
let field_ptr: SafePtr<V, &IoMem> = {
let mut ptr = safe_ptr.borrow_vm();
ptr.byte_add(offset);
ptr.cast()
};
field_ptr
.write_once(&value)
.map_err(|_| VirtioTransportError::DeviceStatusError)
}
fn write_legacy<V: PodOnce + PortWrite>(
&self,
offset: usize,
value: V,
) -> Result<(), VirtioTransportError> {
let Some((bar, base)) = self.legacy_space.as_ref() else {
return Err(VirtioTransportError::InvalidArgs);
};
bar.write_once(base + offset, value)
.map_err(|_| VirtioTransportError::DeviceStatusError)
}
/// Write a specific configuration.
pub(super) fn write_once<V: PodOnce + PortWrite>(
&self,
offset: usize,
value: V,
) -> Result<(), VirtioTransportError> {
debug_assert!(offset + size_of::<V>() <= size_of::<T>());
if self.is_modern() {
self.write_modern(offset, value)
} else {
self.write_legacy(offset, value)
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum VirtioTransportError {
DeviceStatusError,

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{boxed::Box, sync::Arc};
use alloc::boxed::Box;
use core::fmt::Debug;
use aster_util::{field_ptr, safe_ptr::SafePtr};
@ -8,7 +8,8 @@ use log::{info, warn};
use ostd::{
bus::{
pci::{
bus::PciDevice, capability::CapabilityData, common_device::PciCommonDevice, PciDeviceId,
bus::PciDevice, capability::CapabilityData, cfg_space::Bar,
common_device::PciCommonDevice, PciDeviceId,
},
BusProbeError,
},
@ -23,7 +24,7 @@ use crate::{
queue::{AvailRing, Descriptor, UsedRing},
transport::{
pci::capability::{VirtioPciCapabilityData, VirtioPciCpabilityType},
DeviceStatus, VirtioTransport, VirtioTransportError,
ConfigManager, DeviceStatus, VirtioTransport, VirtioTransportError,
},
VirtioDeviceType,
};
@ -39,14 +40,10 @@ pub struct VirtioPciDevice {
device_id: PciDeviceId,
}
pub struct VirtioPciTransport {
device_type: VirtioDeviceType,
common_device: PciCommonDevice,
common_cfg: SafePtr<VirtioPciCommonCfg, IoMem>,
device_cfg: VirtioPciCapabilityData,
notify: VirtioPciNotify,
msix_manager: VirtioMsixManager,
device: Arc<VirtioPciDevice>,
impl VirtioPciDevice {
pub(super) fn new(device_id: PciDeviceId) -> Self {
Self { device_id }
}
}
impl PciDevice for VirtioPciDevice {
@ -55,15 +52,24 @@ impl PciDevice for VirtioPciDevice {
}
}
impl Debug for VirtioPciTransport {
pub struct VirtioPciModernTransport {
device_type: VirtioDeviceType,
common_device: PciCommonDevice,
common_cfg: SafePtr<VirtioPciCommonCfg, IoMem>,
device_cfg: VirtioPciCapabilityData,
notify: VirtioPciNotify,
msix_manager: VirtioMsixManager,
}
impl Debug for VirtioPciModernTransport {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PCIVirtioDevice")
f.debug_struct("PCIVirtioModernDevice")
.field("common_device", &self.common_device)
.finish()
}
}
impl VirtioTransport for VirtioPciTransport {
impl VirtioTransport for VirtioPciModernTransport {
fn device_type(&self) -> VirtioDeviceType {
self.device_type
}
@ -108,14 +114,14 @@ impl VirtioTransport for VirtioPciTransport {
Ok(())
}
fn get_notify_ptr(&self, idx: u16) -> Result<SafePtr<u32, IoMem>, VirtioTransportError> {
if idx >= self.num_queues() {
return Err(VirtioTransportError::InvalidArgs);
}
Ok(SafePtr::new(
fn notify_config(&self, idx: usize) -> ConfigManager<u32> {
debug_assert!(idx < self.num_queues() as usize);
let safe_ptr = Some(SafePtr::new(
self.notify.io_memory.clone(),
(self.notify.offset + self.notify.offset_multiplier * idx as u32) as usize,
))
));
ConfigManager::new(safe_ptr, None)
}
fn num_queues(&self) -> u16 {
@ -124,12 +130,22 @@ impl VirtioTransport for VirtioPciTransport {
.unwrap()
}
fn device_config_memory(&self) -> IoMem {
let memory = self.device_cfg.memory_bar().as_ref().unwrap().io_mem();
fn device_config_mem(&self) -> Option<IoMem> {
let offset = self.device_cfg.offset() as usize;
let length = self.device_cfg.length() as usize;
memory.slice(offset..offset + length)
let io_mem = self
.device_cfg
.memory_bar()
.as_ref()
.unwrap()
.io_mem()
.slice(offset..offset + length);
Some(io_mem)
}
fn device_config_bar(&self) -> Option<(Bar, usize)> {
None
}
fn read_device_features(&self) -> u64 {
@ -251,42 +267,22 @@ impl VirtioTransport for VirtioPciTransport {
}
}
impl VirtioPciTransport {
pub(super) fn pci_device(&self) -> &Arc<VirtioPciDevice> {
&self.device
}
impl VirtioPciModernTransport {
#[allow(clippy::result_large_err)]
pub(super) fn new(
common_device: PciCommonDevice,
) -> Result<Self, (BusProbeError, PciCommonDevice)> {
let device_type = match common_device.device_id().device_id {
0x1000 => VirtioDeviceType::Network,
0x1001 => VirtioDeviceType::Block,
0x1002 => VirtioDeviceType::TraditionalMemoryBalloon,
0x1003 => VirtioDeviceType::Console,
0x1004 => VirtioDeviceType::ScsiHost,
0x1005 => VirtioDeviceType::Entropy,
0x1009 => VirtioDeviceType::Transport9P,
id => {
if id <= 0x1040 {
warn!(
"Unrecognized virtio-pci device id:{:x?}",
common_device.device_id().device_id
);
return Err((BusProbeError::DeviceNotMatch, common_device));
}
let id = id - 0x1040;
match VirtioDeviceType::try_from(id as u8) {
Ok(device) => device,
Err(_) => {
warn!(
"Unrecognized virtio-pci device id:{:x?}",
common_device.device_id().device_id
);
return Err((BusProbeError::DeviceNotMatch, common_device));
}
}
let device_id = common_device.device_id().device_id;
if device_id <= 0x1040 {
warn!("Unrecognized virtio-pci device id:{:x?}", device_id);
return Err((BusProbeError::DeviceNotMatch, common_device));
}
let device_type = match VirtioDeviceType::try_from((device_id - 0x1040) as u8) {
Ok(device) => device,
Err(_) => {
warn!("Unrecognized virtio-pci device id:{:x?}", device_id);
return Err((BusProbeError::DeviceNotMatch, common_device));
}
};
@ -335,7 +331,6 @@ impl VirtioPciTransport {
let common_cfg = common_cfg.unwrap();
let device_cfg = device_cfg.unwrap();
let msix_manager = VirtioMsixManager::new(msix);
let device_id = *common_device.device_id();
Ok(Self {
common_device,
common_cfg,
@ -343,7 +338,6 @@ impl VirtioPciTransport {
notify,
msix_manager,
device_type,
device: Arc::new(VirtioPciDevice { device_id }),
})
}
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{sync::Arc, vec::Vec};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use ostd::{
bus::{
@ -13,11 +13,15 @@ use ostd::{
sync::SpinLock,
};
use super::device::VirtioPciTransport;
use super::device::VirtioPciModernTransport;
use crate::transport::{
pci::{device::VirtioPciDevice, legacy::VirtioPciLegacyTransport},
VirtioTransport,
};
#[derive(Debug)]
pub struct VirtioPciDriver {
devices: SpinLock<Vec<VirtioPciTransport>>,
devices: SpinLock<Vec<Box<dyn VirtioTransport>>>,
}
impl VirtioPciDriver {
@ -25,7 +29,7 @@ impl VirtioPciDriver {
self.devices.lock().len()
}
pub fn pop_device_transport(&self) -> Option<VirtioPciTransport> {
pub fn pop_device_transport(&self) -> Option<Box<dyn VirtioTransport>> {
self.devices.lock().pop()
}
@ -45,9 +49,22 @@ impl PciDriver for VirtioPciDriver {
if device.device_id().vendor_id != VIRTIO_DEVICE_VENDOR_ID {
return Err((BusProbeError::DeviceNotMatch, device));
}
let transport = VirtioPciTransport::new(device)?;
let device = transport.pci_device().clone();
let device_id = *device.device_id();
let transport: Box<dyn VirtioTransport> = match device_id.device_id {
0x1000..0x1040 if (device.device_id().revision_id == 0) => {
// Transitional PCI Device ID in the range 0x1000 to 0x103f.
let legacy = VirtioPciLegacyTransport::new(device)?;
Box::new(legacy)
}
0x1040..0x107f => {
let modern = VirtioPciModernTransport::new(device)?;
Box::new(modern)
}
_ => return Err((BusProbeError::DeviceNotMatch, device)),
};
self.devices.lock().push(transport);
Ok(device)
Ok(Arc::new(VirtioPciDevice::new(device_id)))
}
}

View File

@ -0,0 +1,335 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::boxed::Box;
use core::fmt::Debug;
use aster_util::safe_ptr::SafePtr;
use log::{info, warn};
use ostd::{
bus::{
pci::{capability::CapabilityData, cfg_space::Bar, common_device::PciCommonDevice},
BusProbeError,
},
io_mem::IoMem,
mm::{DmaCoherent, HasDaddr, PAGE_SIZE},
trap::IrqCallbackFunction,
};
use crate::{
queue::UsedElem,
transport::{
pci::msix::VirtioMsixManager, AvailRing, ConfigManager, Descriptor, UsedRing,
VirtioTransport, VirtioTransportError,
},
DeviceStatus, VirtioDeviceType,
};
// When used through the legacy interface, the virtio common configuration structure looks as follows:
//
// virtio legacy ==> Mapped into PCI BAR0
// +---------------------------------------------------------------------------------------+
// | Device Features Bits[0:31] (Read-only) |
// +---------------------------------------------------------------------------------------+
// | Driver Features Bits[0:31] (Read & Write) |
// +---------------------------------------------------------------------------------------+
// | Virtqueue Address PFN (R/W) |
// +-------------------------------------------+-------------------------------------------+
// | Queue Select (R/W) | Queue Size (R) |
// +---------------------+---------------------+-------------------------------------------+
// | ISR Status (R) | Device Status (R/W) | Queue Notify (R/W) |
// +---------------------+---------------------+-------------------------------------------+
// | MSIX Queue Vector (R/W) | MSIX Config Vector (R/W) |
// +-------------------------------------------+-------------------------------------------+
// | Device Specific Configurations (R/W) |
// +---------------------------------------------------------------------------------------+
//
// When MSI-X capability is enabled, device-specific configuration starts at byte offset 24 in
// virtio common configuration structure. When MSI-X capability is not enabled, device-specific
// configuration starts at byte offset 20 in virtio header.
const DEVICE_FEATURES_OFFSET: usize = 0x00;
const DRIVER_FEATURES_OFFSET: usize = 0x04;
const QUEUE_ADDR_PFN_OFFSET: usize = 0x08;
const QUEUE_SIZE_OFFSET: usize = 0x0c;
const QUEUE_SELECT_OFFSET: usize = 0x0e;
const QUEUE_NOTIFY_OFFSET: usize = 0x10;
const DEVICE_STATUS_OFFSET: usize = 0x12;
const ISR_STATUS_OFFSET: usize = 0x13;
// If MSI-X is enabled for the device, there are two additional fields.
const CONFIG_MSIX_VECTOR_OFFSET: usize = 0x14;
const QUEUE_MSIX_VECTOR_OFFSET: usize = 0x16;
const DEVICE_CONFIG_OFFSET: usize = 0x14;
const DEVICE_CONFIG_OFFSET_WITH_MSIX: usize = 0x18;
pub struct VirtioPciLegacyTransport {
device_type: VirtioDeviceType,
common_device: PciCommonDevice,
config_bar: Bar,
num_queues: u16,
msix_manager: VirtioMsixManager,
}
impl VirtioPciLegacyTransport {
pub const QUEUE_ALIGN_SIZE: usize = 4096;
#[allow(clippy::result_large_err)]
pub(super) fn new(
common_device: PciCommonDevice,
) -> Result<Self, (BusProbeError, PciCommonDevice)> {
let device_type = match common_device.device_id().device_id {
0x1000 => VirtioDeviceType::Network,
0x1001 => VirtioDeviceType::Block,
0x1002 => VirtioDeviceType::TraditionalMemoryBalloon,
0x1003 => VirtioDeviceType::Console,
0x1004 => VirtioDeviceType::ScsiHost,
0x1005 => VirtioDeviceType::Entropy,
0x1009 => VirtioDeviceType::Transport9P,
_ => {
warn!(
"Unrecognized virtio-pci device id:{:x?}",
common_device.device_id().device_id
);
return Err((BusProbeError::ConfigurationSpaceError, common_device));
}
};
info!("[Virtio]: Found device:{:?}", device_type);
let config_bar = common_device.bar_manager().bar(0).unwrap();
let mut num_queues = 0u16;
while num_queues < u16::MAX {
config_bar
.write_once(QUEUE_SELECT_OFFSET, num_queues)
.unwrap();
let queue_size = config_bar.read_once::<u16>(QUEUE_SIZE_OFFSET).unwrap();
if queue_size == 0 {
break;
}
num_queues += 1;
}
// TODO: Support interrupt without MSI-X
let mut msix = None;
for cap in common_device.capabilities().iter() {
match cap.capability_data() {
CapabilityData::Msix(data) => {
msix = Some(data.clone());
}
_ => continue,
}
}
let Some(msix) = msix else {
return Err((BusProbeError::ConfigurationSpaceError, common_device));
};
let msix_manager = VirtioMsixManager::new(msix);
Ok(Self {
device_type,
common_device,
config_bar,
num_queues,
msix_manager,
})
}
/// Calculate the aligned virtqueue size.
///
/// According to the VirtIO spec v0.9.5:
///
/// Each virtqueue occupies two or more physically-contiguous pages (defined, for
/// the purposes of this specification, as 4096 bytes), and consists of three parts:
/// +------------------+------------------------------------------------+-----------+
/// | Descriptor Table | Available Ring (padding to next 4096 boundary) | Used Ring |
/// +------------------+------------------------------------------------+-----------+
///
/// More details can be found at <http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf>.
pub(crate) fn calc_virtqueue_size_aligned(queue_size: usize) -> usize {
let align_mask = Self::QUEUE_ALIGN_SIZE - 1;
((size_of::<Descriptor>() * queue_size + size_of::<u16>() * (3 + queue_size) + align_mask)
& !align_mask)
+ ((size_of::<u16>() * 3 + size_of::<UsedElem>() * queue_size + align_mask)
& !align_mask)
}
}
impl VirtioTransport for VirtioPciLegacyTransport {
fn device_type(&self) -> VirtioDeviceType {
self.device_type
}
fn set_queue(
&mut self,
idx: u16,
_queue_size: u16,
descriptor_ptr: &SafePtr<Descriptor, DmaCoherent>,
_avail_ring_ptr: &SafePtr<AvailRing, DmaCoherent>,
_used_ring_ptr: &SafePtr<UsedRing, DmaCoherent>,
) -> Result<(), VirtioTransportError> {
// When using the legacy interface, there was no mechanism to negotiate
// the queue size! The transitional driver MUST retrieve the `Queue Size`
// field from the device and MUST allocate the total number of bytes
// (including descriptor, avail_ring and used_ring) for the virtqueue
// according to the specific formula, see `calc_virtqueue_size_aligned`.
let queue_addr = descriptor_ptr.daddr();
let page_frame_number = (queue_addr / PAGE_SIZE) as u32;
self.config_bar
.write_once(QUEUE_SELECT_OFFSET, idx)
.unwrap();
debug_assert_eq!(
self.config_bar
.read_once::<u16>(QUEUE_SELECT_OFFSET)
.unwrap(),
idx
);
self.config_bar
.write_once(QUEUE_ADDR_PFN_OFFSET, page_frame_number)
.unwrap();
Ok(())
}
fn notify_config(&self, _idx: usize) -> ConfigManager<u32> {
let bar_space = Some((self.config_bar.clone(), QUEUE_NOTIFY_OFFSET));
ConfigManager::new(None, bar_space)
}
fn num_queues(&self) -> u16 {
self.num_queues
}
fn device_config_mem(&self) -> Option<IoMem> {
None
}
fn device_config_bar(&self) -> Option<(Bar, usize)> {
let bar = self.config_bar.clone();
let base = if self.msix_manager.is_enabled() {
DEVICE_CONFIG_OFFSET_WITH_MSIX
} else {
DEVICE_CONFIG_OFFSET
};
Some((bar, base))
}
fn read_device_features(&self) -> u64 {
// Only Feature Bits 0 to 31 are accessible through the Legacy Interface.
let features = self
.config_bar
.read_once::<u32>(DEVICE_FEATURES_OFFSET)
.unwrap();
features as u64
}
fn write_driver_features(&mut self, features: u64) -> Result<(), VirtioTransportError> {
// When used through the Legacy Interface, Transitional Devices MUST assume that
// Feature Bits 32 to 63 are not acknowledged by Driver.
self.config_bar
.write_once(DRIVER_FEATURES_OFFSET, features as u32)
.unwrap();
Ok(())
}
fn read_device_status(&self) -> DeviceStatus {
let status = self
.config_bar
.read_once::<u8>(DEVICE_STATUS_OFFSET)
.unwrap();
DeviceStatus::from_bits_truncate(status)
}
fn write_device_status(&mut self, status: DeviceStatus) -> Result<(), VirtioTransportError> {
let status = status.bits();
self.config_bar
.write_once(DEVICE_STATUS_OFFSET, status)
.unwrap();
Ok(())
}
// Set to driver ok status
fn finish_init(&mut self) {
self.write_device_status(
DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::DRIVER_OK,
)
.unwrap();
}
fn max_queue_size(&self, idx: u16) -> Result<u16, VirtioTransportError> {
self.config_bar
.write_once(QUEUE_SELECT_OFFSET, idx)
.unwrap();
debug_assert_eq!(
self.config_bar
.read_once::<u16>(QUEUE_SELECT_OFFSET)
.unwrap(),
idx
);
Ok(self.config_bar.read_once(QUEUE_SIZE_OFFSET).unwrap())
}
fn register_queue_callback(
&mut self,
index: u16,
func: Box<IrqCallbackFunction>,
single_interrupt: bool,
) -> Result<(), VirtioTransportError> {
if index >= self.num_queues() {
return Err(VirtioTransportError::InvalidArgs);
}
let (vector, irq) = if single_interrupt {
if let Some(unused_irq) = self.msix_manager.pop_unused_irq() {
unused_irq
} else {
warn!(
"{:?}: `single_interrupt` ignored: no more IRQ lines available",
self.device_type()
);
self.msix_manager.shared_irq_line()
}
} else {
self.msix_manager.shared_irq_line()
};
irq.on_active(func);
self.config_bar
.write_once(QUEUE_SELECT_OFFSET, index)
.unwrap();
debug_assert_eq!(
self.config_bar
.read_once::<u16>(QUEUE_SELECT_OFFSET)
.unwrap(),
index
);
self.config_bar
.write_once(QUEUE_MSIX_VECTOR_OFFSET, vector)
.unwrap();
Ok(())
}
fn register_cfg_callback(
&mut self,
func: Box<IrqCallbackFunction>,
) -> Result<(), VirtioTransportError> {
let (vector, irq) = self.msix_manager.config_msix_irq();
irq.on_active(func);
self.config_bar
.write_once(CONFIG_MSIX_VECTOR_OFFSET, vector)
.unwrap();
Ok(())
}
fn is_legacy_version(&self) -> bool {
true
}
}
impl Debug for VirtioPciLegacyTransport {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PCIVirtioLegacyDevice")
.field("common_device", &self.common_device)
.finish()
}
}

View File

@ -4,6 +4,7 @@ pub mod capability;
pub mod common_cfg;
pub mod device;
pub mod driver;
pub mod legacy;
pub(super) mod msix;
use alloc::sync::Arc;

View File

@ -62,4 +62,9 @@ impl VirtioMsixManager {
self.used_msix_vectors.push(vector);
Some((vector, self.msix.irq_mut(vector as usize).unwrap()))
}
/// Returns true if MSI-X is enabled.
pub fn is_enabled(&self) -> bool {
self.msix.is_enabled()
}
}

View File

@ -217,6 +217,12 @@ impl CapabilityMsixData {
pub fn irq_mut(&mut self, index: usize) -> Option<&mut IrqLine> {
self.irqs[index].as_mut()
}
/// Returns true if MSI-X Enable bit is set.
pub fn is_enabled(&self) -> bool {
let msg_ctrl = self.loc.read16(self.ptr + 2);
msg_ctrl & 0x8000 != 0
}
}
fn set_bit(origin_value: u16, offset: usize, set: bool) -> u16 {

View File

@ -13,7 +13,10 @@ use super::PciDeviceLocation;
use crate::{
arch::device::io_port::{PortRead, PortWrite},
io_mem::IoMem,
mm::page_prop::{CachePolicy, PageFlags},
mm::{
page_prop::{CachePolicy, PageFlags},
PodOnce, VmIoOnce,
},
Error, Result,
};
@ -175,6 +178,22 @@ impl Bar {
Self::Io(Arc::new(IoBar::new(&location, index)?))
})
}
/// Reads a value of a specified type at a specified offset.
pub fn read_once<T: PodOnce + PortRead>(&self, offset: usize) -> Result<T> {
match self {
Bar::Memory(mem_bar) => mem_bar.io_mem().read_once(offset),
Bar::Io(io_bar) => io_bar.read(offset as u32),
}
}
/// Writes a value of a specified type at a specified offset.
pub fn write_once<T: PodOnce + PortWrite>(&self, offset: usize, value: T) -> Result<()> {
match self {
Bar::Memory(mem_bar) => mem_bar.io_mem().write_once(offset, &value),
Bar::Io(io_bar) => io_bar.write(offset as u32, value),
}
}
}
/// Memory BAR