diff --git a/kernel/comps/virtio/src/device/block/device.rs b/kernel/comps/virtio/src/device/block/device.rs index 4d842dccb..bffe95535 100644 --- a/kernel/comps/virtio/src/device/block/device.rs +++ b/kernel/comps/virtio/src/device/block/device.rs @@ -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) -> 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, + config_manager: ConfigManager, features: VirtioBlockFeature, queue: SpinLock, transport: SpinLock>, @@ -106,9 +117,10 @@ impl DeviceInner { /// Creates and inits the device. pub fn init(mut transport: Box) -> Result, 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), diff --git a/kernel/comps/virtio/src/device/block/mod.rs b/kernel/comps/virtio/src/device/block/mod.rs index 36b157af3..16dc4ed84 100644 --- a/kernel/comps/virtio/src/device/block/mod.rs +++ b/kernel/comps/virtio/src/device/block/mod.rs @@ -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 { - let memory = transport.device_config_memory(); - SafePtr::new(memory, 0) + pub(self) fn new_manager(transport: &dyn VirtioTransport) -> ConfigManager { + 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) -> ostd::prelude::Result { - field_ptr!(this, Self, blk_size) - .read_once() - .map(|val| val as usize) +impl ConfigManager { + 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::(offset_of!(VirtioBlockConfig, capacity)) + .unwrap() as u64; + let cap_high = self + .read_once::(offset_of!(VirtioBlockConfig, capacity) + 4) + .unwrap() as u64; + blk_config.capacity = cap_high << 32 | cap_low; + blk_config.size_max = self + .read_once::(offset_of!(VirtioBlockConfig, size_max)) + .unwrap(); + blk_config.seg_max = self + .read_once::(offset_of!(VirtioBlockConfig, seg_max)) + .unwrap(); + blk_config.geometry.cylinders = self + .read_once::( + offset_of!(VirtioBlockConfig, geometry) + + offset_of!(VirtioBlockGeometry, cylinders), + ) + .unwrap(); + blk_config.geometry.heads = self + .read_once::( + offset_of!(VirtioBlockConfig, geometry) + offset_of!(VirtioBlockGeometry, heads), + ) + .unwrap(); + blk_config.geometry.sectors = self + .read_once::( + offset_of!(VirtioBlockConfig, geometry) + offset_of!(VirtioBlockGeometry, sectors), + ) + .unwrap(); + blk_config.blk_size = self + .read_once::(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, - ) -> ostd::prelude::Result { - field_ptr!(this, Self, capacity) - .read_once() - .map(|val| val as usize) + pub(self) fn block_size(&self) -> usize { + self.read_once::(offset_of!(VirtioBlockConfig, blk_size)) + .unwrap() as usize + } + + pub(self) fn capacity_sectors(&self) -> usize { + let cap_low = self + .read_once::(offset_of!(VirtioBlockConfig, capacity)) + .unwrap() as usize; + let cap_high = self + .read_once::(offset_of!(VirtioBlockConfig, capacity) + 4) + .unwrap() as usize; + + cap_high << 32 | cap_low } } diff --git a/kernel/comps/virtio/src/device/console/config.rs b/kernel/comps/virtio/src/device/console/config.rs index 49079e274..c1809a768 100644 --- a/kernel/comps/virtio/src/device/console/config.rs +++ b/kernel/comps/virtio/src/device/console/config.rs @@ -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 { - let memory = transport.device_config_memory(); - SafePtr::new(memory, 0) + pub(super) fn new_manager(transport: &dyn VirtioTransport) -> ConfigManager { + 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 { + 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::(offset_of!(VirtioConsoleConfig, cols)) + .unwrap(); + console_config.rows = self + .read_once::(offset_of!(VirtioConsoleConfig, rows)) + .unwrap(); + console_config.max_nr_ports = self + .read_once::(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(); + } } } diff --git a/kernel/comps/virtio/src/device/console/device.rs b/kernel/comps/virtio/src/device/console/device.rs index 4e945cc71..efa9aa312 100644 --- a/kernel/comps/virtio/src/device/console/device.rs +++ b/kernel/comps/virtio/src/device/console/device.rs @@ -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, + config_manager: ConfigManager, transport: SpinLock>, receive_queue: SpinLock, transmit_queue: SpinLock, @@ -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) -> 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, diff --git a/kernel/comps/virtio/src/device/input/mod.rs b/kernel/comps/virtio/src/device/input/mod.rs index c3d005d50..2ae3a88fd 100644 --- a/kernel/comps/virtio/src/device/input/mod.rs +++ b/kernel/comps/virtio/src/device/input/mod.rs @@ -77,7 +77,7 @@ pub struct VirtioInputConfig { impl VirtioInputConfig { pub(self) fn new(transport: &dyn VirtioTransport) -> SafePtr { - let memory = transport.device_config_memory(); + let memory = transport.device_config_mem().unwrap(); SafePtr::new(memory, 0) } } diff --git a/kernel/comps/virtio/src/device/network/config.rs b/kernel/comps/virtio/src/device/network/config.rs index 9c0c9e2b8..e87c1c2ff 100644 --- a/kernel/comps/virtio/src/device/network/config.rs +++ b/kernel/comps/virtio/src/device/network/config.rs @@ -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 { - let memory = transport.device_config_memory(); - SafePtr::new(memory, 0) - } - - pub(super) fn read(this: &SafePtr) -> ostd::prelude::Result { - // 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::(); - 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 { + 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 { + 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::(offset_of!(VirtioNetConfig, mac) + i) + .unwrap(); + } + net_config.status.bits = self + .read_once::(offset_of!(VirtioNetConfig, status)) + .unwrap(); + + if self.is_modern() { + net_config.max_virtqueue_pairs = self + .read_once::(offset_of!(VirtioNetConfig, max_virtqueue_pairs)) + .unwrap(); + net_config.mtu = self + .read_once::(offset_of!(VirtioNetConfig, mtu)) + .unwrap(); + net_config.speed = self + .read_once::(offset_of!(VirtioNetConfig, speed)) + .unwrap(); + net_config.duplex = self + .read_once::(offset_of!(VirtioNetConfig, duplex)) + .unwrap(); + net_config.rss_max_key_size = self + .read_once::(offset_of!(VirtioNetConfig, rss_max_key_size)) + .unwrap(); + net_config.rss_max_indirection_table_length = self + .read_once::(offset_of!( + VirtioNetConfig, + rss_max_indirection_table_length + )) + .unwrap(); + net_config.supported_hash_types = self + .read_once::(offset_of!(VirtioNetConfig, supported_hash_types)) + .unwrap(); + } + + net_config } } diff --git a/kernel/comps/virtio/src/device/network/device.rs b/kernel/comps/virtio/src/device/network/device.rs index 2b09af894..38ea17a03 100644 --- a/kernel/comps/virtio/src/device/network/device.rs +++ b/kernel/comps/virtio/src/device/network/device.rs @@ -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, // For smoltcp use caps: DeviceCapabilities, mac_addr: EthernetAddr, @@ -57,16 +57,15 @@ impl NetworkDevice { } pub fn init(mut transport: Box) -> 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) diff --git a/kernel/comps/virtio/src/device/socket/config.rs b/kernel/comps/virtio/src/device/socket/config.rs index f2ce56f1d..08f2c169f 100644 --- a/kernel/comps/virtio/src/device/socket/config.rs +++ b/kernel/comps/virtio/src/device/socket/config.rs @@ -34,7 +34,7 @@ pub struct VirtioVsockConfig { impl VirtioVsockConfig { pub(crate) fn new(transport: &dyn VirtioTransport) -> SafePtr { - let memory = transport.device_config_memory(); + let memory = transport.device_config_mem().unwrap(); SafePtr::new(memory, 0) } } diff --git a/kernel/comps/virtio/src/lib.rs b/kernel/comps/virtio/src/lib.rs index dda790428..a9d2c9079 100644 --- a/kernel/comps/virtio/src/lib.rs +++ b/kernel/comps/virtio/src/lib.rs @@ -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> { 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)); diff --git a/kernel/comps/virtio/src/queue.rs b/kernel/comps/virtio/src/queue.rs index 9a5b60929..6ff136635 100644 --- a/kernel/comps/virtio/src/queue.rs +++ b/kernel/comps/virtio/src/queue.rs @@ -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, /// Used ring used: SafePtr, - /// point to notify address - notify: SafePtr, + /// Notify configuration manager + notify_config: ConfigManager, /// 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 { 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::() * size as usize; + let queue_size = transport.max_queue_size(idx).unwrap() as usize; + let desc_size = size_of::() * 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::() * (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 = 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::(0, self.queue_idx) + .unwrap(); + } else { + self.notify_config + .write_once::(0, self.queue_idx as u16) + .unwrap(); + } } /// Disables registered callbacks. diff --git a/kernel/comps/virtio/src/transport/mmio/device.rs b/kernel/comps/virtio/src/transport/mmio/device.rs index 854ed6afd..a833bf45b 100644 --- a/kernel/comps/virtio/src/transport/mmio/device.rs +++ b/kernel/comps/virtio/src/transport/mmio/device.rs @@ -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, VirtioTransportError> { + fn notify_config(&self, _idx: usize) -> ConfigManager { 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 { // 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 { diff --git a/kernel/comps/virtio/src/transport/mod.rs b/kernel/comps/virtio/src/transport/mod.rs index 8aa71234c..f75a8f3c2 100644 --- a/kernel/comps/virtio/src/transport/mod.rs +++ b/kernel/comps/virtio/src/transport/mod.rs @@ -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; + + /// 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; - /// 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, VirtioTransportError>; + fn notify_config(&self, idx: usize) -> ConfigManager; 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 { + modern_space: Option>, + legacy_space: Option<(Bar, usize)>, +} + +impl ConfigManager { + pub(super) fn new( + modern_space: Option>, + 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(&self, offset: usize) -> Result { + let Some(safe_ptr) = self.modern_space.as_ref() else { + return Err(VirtioTransportError::InvalidArgs); + }; + + let field_ptr: SafePtr = { + let mut ptr = safe_ptr.borrow_vm(); + ptr.byte_add(offset); + ptr.cast() + }; + + field_ptr + .read_once() + .map_err(|_| VirtioTransportError::DeviceStatusError) + } + + fn read_legacy(&self, offset: usize) -> Result { + 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( + &self, + offset: usize, + ) -> Result { + debug_assert!(offset + size_of::() <= size_of::()); + if self.is_modern() { + self.read_modern(offset) + } else { + self.read_legacy(offset) + } + } + + fn write_modern( + &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 = { + 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( + &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( + &self, + offset: usize, + value: V, + ) -> Result<(), VirtioTransportError> { + debug_assert!(offset + size_of::() <= size_of::()); + if self.is_modern() { + self.write_modern(offset, value) + } else { + self.write_legacy(offset, value) + } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum VirtioTransportError { DeviceStatusError, diff --git a/kernel/comps/virtio/src/transport/pci/device.rs b/kernel/comps/virtio/src/transport/pci/device.rs index 34cb98314..05798d84b 100644 --- a/kernel/comps/virtio/src/transport/pci/device.rs +++ b/kernel/comps/virtio/src/transport/pci/device.rs @@ -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, - device_cfg: VirtioPciCapabilityData, - notify: VirtioPciNotify, - msix_manager: VirtioMsixManager, - device: Arc, +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, + 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, VirtioTransportError> { - if idx >= self.num_queues() { - return Err(VirtioTransportError::InvalidArgs); - } - Ok(SafePtr::new( + fn notify_config(&self, idx: usize) -> ConfigManager { + 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 { 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 { - &self.device - } - +impl VirtioPciModernTransport { #[allow(clippy::result_large_err)] pub(super) fn new( common_device: PciCommonDevice, ) -> Result { - 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 }), }) } } diff --git a/kernel/comps/virtio/src/transport/pci/driver.rs b/kernel/comps/virtio/src/transport/pci/driver.rs index 965834227..2fc1a742e 100644 --- a/kernel/comps/virtio/src/transport/pci/driver.rs +++ b/kernel/comps/virtio/src/transport/pci/driver.rs @@ -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>, + devices: SpinLock>>, } impl VirtioPciDriver { @@ -25,7 +29,7 @@ impl VirtioPciDriver { self.devices.lock().len() } - pub fn pop_device_transport(&self) -> Option { + pub fn pop_device_transport(&self) -> Option> { 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 = 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))) } } diff --git a/kernel/comps/virtio/src/transport/pci/legacy.rs b/kernel/comps/virtio/src/transport/pci/legacy.rs new file mode 100644 index 000000000..0faf81a60 --- /dev/null +++ b/kernel/comps/virtio/src/transport/pci/legacy.rs @@ -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 { + 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::(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 . + pub(crate) fn calc_virtqueue_size_aligned(queue_size: usize) -> usize { + let align_mask = Self::QUEUE_ALIGN_SIZE - 1; + + ((size_of::() * queue_size + size_of::() * (3 + queue_size) + align_mask) + & !align_mask) + + ((size_of::() * 3 + size_of::() * 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, + _avail_ring_ptr: &SafePtr, + _used_ring_ptr: &SafePtr, + ) -> 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::(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 { + 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 { + 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::(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::(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 { + self.config_bar + .write_once(QUEUE_SELECT_OFFSET, idx) + .unwrap(); + debug_assert_eq!( + self.config_bar + .read_once::(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, + 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::(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, + ) -> 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() + } +} diff --git a/kernel/comps/virtio/src/transport/pci/mod.rs b/kernel/comps/virtio/src/transport/pci/mod.rs index d894f70a4..abe220280 100644 --- a/kernel/comps/virtio/src/transport/pci/mod.rs +++ b/kernel/comps/virtio/src/transport/pci/mod.rs @@ -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; diff --git a/kernel/comps/virtio/src/transport/pci/msix.rs b/kernel/comps/virtio/src/transport/pci/msix.rs index 165147330..c732fb994 100644 --- a/kernel/comps/virtio/src/transport/pci/msix.rs +++ b/kernel/comps/virtio/src/transport/pci/msix.rs @@ -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() + } } diff --git a/ostd/src/bus/pci/capability/msix.rs b/ostd/src/bus/pci/capability/msix.rs index 821322f58..235df1be2 100644 --- a/ostd/src/bus/pci/capability/msix.rs +++ b/ostd/src/bus/pci/capability/msix.rs @@ -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 { diff --git a/ostd/src/bus/pci/cfg_space.rs b/ostd/src/bus/pci/cfg_space.rs index a8f2dcd54..df9e14029 100644 --- a/ostd/src/bus/pci/cfg_space.rs +++ b/ostd/src/bus/pci/cfg_space.rs @@ -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(&self, offset: usize) -> Result { + 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(&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