diff --git a/virtio-core/src/arch/aarch64.rs b/virtio-core/src/arch/aarch64.rs new file mode 100644 index 0000000000..e8f340580a --- /dev/null +++ b/virtio-core/src/arch/aarch64.rs @@ -0,0 +1,16 @@ +use std::fs::File; + +use pcid_interface::*; + +use crate::{transport::Error, Device}; + +pub fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result { + unimplemented!("virtio_core: aarch64 enable_msix") +} + +pub fn probe_legacy_port_transport<'a>( + pci_header: &PciHeader, + pcid_handle: &mut PcidServerHandle, +) -> Result, Error> { + panic!("virtio-core: aarch64 doesn't support legacy port I/O") +} \ No newline at end of file diff --git a/virtio-core/src/arch/x86.rs b/virtio-core/src/arch/x86.rs new file mode 100644 index 0000000000..9806acea4f --- /dev/null +++ b/virtio-core/src/arch/x86.rs @@ -0,0 +1,10 @@ +pub fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result { + panic!("virtio-core: x86 doesn't support enable_msix") +} + +pub fn probe_legacy_port_transport<'a>( + pci_header: &PciHeader, + pcid_handle: &mut PcidServerHandle, +) -> Result, Error> { + crate::x86_64::probe_legacy_port_transport(pci_header, pcid_handle) +} \ No newline at end of file diff --git a/virtio-core/src/arch/x86_64.rs b/virtio-core/src/arch/x86_64.rs new file mode 100644 index 0000000000..1909c20aec --- /dev/null +++ b/virtio-core/src/arch/x86_64.rs @@ -0,0 +1,137 @@ +use crate::{ + reinit, + transport::{Error}, + utils::VolatileCell, + Device, legacy_transport::LegacyTransport, +}; + +use pcid_interface::msi::{self, MsixTableEntry}; +use pcid_interface::irq_helpers::{allocate_single_interrupt_vector, read_bsp_apic_id}; +use std::{ptr::NonNull, fs::File}; + +use syscall::Io; + +use crate::{probe::MsixInfo, MSIX_PRIMARY_VECTOR}; + +use pcid_interface::*; + +pub fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result { + + let pci_config = pcid_handle.fetch_config()?; + + // Extended message signaled interrupts. + let capability = match pcid_handle.feature_info(PciFeature::MsiX)? { + PciFeatureInfo::MsiX(capability) => capability, + _ => unreachable!(), + }; + + let table_size = capability.table_size(); + let table_base = capability.table_base_pointer(pci_config.func.bars); + let table_min_length = table_size * 16; + let pba_min_length = table_size.div_ceil(8); + + let pba_base = capability.pba_base_pointer(pci_config.func.bars); + + let bir = capability.table_bir() as usize; + let bar = pci_config.func.bars[bir]; + let bar_size = pci_config.func.bar_sizes[bir] as u64; + + let bar_ptr = match bar { + PciBar::Memory32(ptr) => ptr.into(), + PciBar::Memory64(ptr) => ptr, + _ => unreachable!(), + }; + + let address = unsafe { + common::physmap( + bar_ptr as usize, + bar_size as usize, + common::Prot::RW, + common::MemoryType::Uncacheable, + )? as usize + }; + + // Ensure that the table and PBA are be within the BAR. + { + let bar_range = bar_ptr..bar_ptr + bar_size; + assert!(bar_range.contains(&(table_base as u64 + table_min_length as u64))); + assert!(bar_range.contains(&(pba_base as u64 + pba_min_length as u64))); + } + + let virt_table_base = ((table_base - bar_ptr as usize) + address) as *mut MsixTableEntry; + + let mut info = MsixInfo { + virt_table_base: NonNull::new(virt_table_base).unwrap(), + capability, + }; + + // Allocate the primary MSI vector. + let interrupt_handle = { + let table_entry_pointer = info.table_entry_pointer(MSIX_PRIMARY_VECTOR as usize); + + let destination_id = read_bsp_apic_id().expect("virtio_core: `read_bsp_apic_id()` failed"); + let lapic_id = u8::try_from(destination_id).unwrap(); + + let rh = false; + let dm = false; + let addr = msi::x86_64::message_address(lapic_id, rh, dm); + + let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id) + .unwrap() + .expect("virtio_core: interrupt vector exhaustion"); + + let msg_data = msi::x86_64::message_data_edge_triggered(msi::x86_64::DeliveryMode::Fixed, vector); + + table_entry_pointer.addr_lo.write(addr); + table_entry_pointer.addr_hi.write(0); + table_entry_pointer.msg_data.write(msg_data); + table_entry_pointer + .vec_ctl + .writef(MsixTableEntry::VEC_CTL_MASK_BIT, false); + + interrupt_handle + }; + + pcid_handle.enable_feature(PciFeature::MsiX)?; + + log::info!("virtio: using MSI-X (interrupt_handle={interrupt_handle:?})"); + Ok(interrupt_handle) +} + +pub fn probe_legacy_port_transport<'a>( + pci_header: &PciHeader, + pcid_handle: &mut PcidServerHandle, +) -> Result, Error> { + if let PciBar::Port(port) = pci_header.get_bar(0) { + unsafe { syscall::iopl(3).expect("virtio: failed to set I/O privilege level") }; + log::warn!("virtio: using legacy transport"); + + static SHIM: VolatileCell = VolatileCell::new(0); + + let transport = LegacyTransport::new(port); + + // Setup interrupts. + let all_pci_features = pcid_handle.fetch_all_features()?; + let has_msix = all_pci_features + .iter() + .any(|(feature, _)| feature.is_msix()); + + // According to the virtio specification, the device REQUIRED to support MSI-X. + assert!(has_msix, "virtio: device does not support MSI-X"); + let irq_handle = enable_msix(pcid_handle)?; + + let device = Device { + transport, + irq_handle, + isr: &SHIM, + device_space: core::ptr::null_mut(), + }; + + device.transport.reset(); + reinit(&device)?; + + Ok(device) + } else { + unreachable!("virtio: legacy transport with non-port IO?") + } +} diff --git a/virtio-core/src/legacy_transport.rs b/virtio-core/src/legacy_transport.rs new file mode 100644 index 0000000000..7769d597ac --- /dev/null +++ b/virtio-core/src/legacy_transport.rs @@ -0,0 +1,191 @@ +use std::{sync::{Weak, atomic::{AtomicU16, Ordering}, Arc}, mem::size_of, fs::File}; + +use common::dma::{PhysBox, Dma}; +use syscall::{Pio, Io}; + +use crate::{transport::{NotifyBell, Transport, Queue, Error, Available, Used, queue_part_sizes, spawn_irq_thread}, spec::{Descriptor, DeviceStatusFlags}}; + + +pub enum LegacyRegister { + DeviceFeatures = 0, // u32 + + QueueAddress = 8, // u32 + QueueSize = 12, // u16 + QueueSelect = 14, // u16 + QueueNotify = 16, // u16 + + DeviceStatus = 18, // u8 + + ConfigMsixVector = 20, // u16 + QueueMsixVector = 22, // u16 +} + +struct LegacyBell(Weak); + +impl NotifyBell for LegacyBell { + #[inline] + fn ring(&self, queue_index: u16) { + let transport = self.0.upgrade().expect("bell: transport dropped"); + transport.write::(LegacyRegister::QueueNotify, queue_index) + } +} + +pub struct LegacyTransport(u16, AtomicU16, Weak); + +impl LegacyTransport { + pub(super) fn new(port: u16) -> Arc { + Arc::new_cyclic(|sref| Self(port, AtomicU16::new(0), sref.clone())) + } + + unsafe fn read_raw(&self, offset: usize) -> V + where + V: Sized + TryFrom, + >::Error: std::fmt::Debug, + { + let port = self.0 + offset as u16; + + if size_of::() == size_of::() { + V::try_from(Pio::::new(port).read() as u64).unwrap() + } else if size_of::() == size_of::() { + V::try_from(Pio::::new(port).read() as u64).unwrap() + } else if size_of::() == size_of::() { + V::try_from(Pio::::new(port).read() as u64).unwrap() + } else if size_of::() == size_of::() { + let lower = Pio::::new(port).read() as u64; + let upper = Pio::::new(port + size_of::() as u16).read() as u64; + + V::try_from(lower | (upper << 32)).unwrap() + } else { + unreachable!() + } + } + + fn read(&self, register: LegacyRegister) -> V + where + V: Sized + TryFrom, + >::Error: std::fmt::Debug, + { + unsafe { self.read_raw(register as usize) } + } + + fn write(&self, register: LegacyRegister, value: V) + where + V: Sized + TryInto, + >::Error: std::fmt::Debug, + { + if size_of::() == size_of::() { + Pio::::new(self.0 + register as u16).write(value.try_into().unwrap() as u8); + } else if size_of::() == size_of::() { + Pio::::new(self.0 + register as u16).write(value.try_into().unwrap() as u16); + } else if size_of::() == size_of::() { + Pio::::new(self.0 + register as u16).write(value.try_into().unwrap() as u32); + } else { + unreachable!() + } + } +} + +impl Transport for LegacyTransport { + fn reset(&self) { + self.write(LegacyRegister::DeviceStatus, 0u8); + + let status = self.read::(LegacyRegister::DeviceStatus); + assert_eq!(status, 0); + } + + fn check_device_feature(&self, feature: u32) -> bool { + assert!( + feature < 32, + "virtio: cannot query feature {feature} on a legacy device" + ); + self.read::(LegacyRegister::DeviceFeatures) & (1 << feature) == (1 << feature) + } + + fn ack_driver_feature(&self, feature: u32) { + assert!( + feature < 32, + "virtio: cannot ack feature {feature} on a legacy device" + ); + + let current = self.read::(LegacyRegister::DeviceFeatures); + self.write::(LegacyRegister::DeviceFeatures, current | (1 << feature)); + } + + fn setup_queue(&self, vector: u16, irq_handle: &File) -> Result, Error> { + let queue_index = self.1.fetch_add(1, Ordering::SeqCst); + self.write(LegacyRegister::QueueSelect, queue_index); + + let queue_size = self.read::(LegacyRegister::QueueSize) as usize; + let (desc_size, avail_size, used_size) = queue_part_sizes(queue_size); + + let size_bytes = desc_size + avail_size + used_size; + let addr = unsafe { syscall::physalloc(size_bytes).map_err(Error::SyscallError)? }; + + let descriptor = unsafe { + let physbox = PhysBox::from_raw_parts(addr, desc_size); + let table = Dma::<[Descriptor]>::from_physbox_uninit_unsized(physbox, queue_size)?; + + table.assume_init() + }; + + let avail_addr = addr + desc_size; + let avail = unsafe { Available::from_raw(avail_addr, avail_size, queue_size)? }; + + let used_addr = avail_addr + avail_size; + let used = unsafe { Used::from_raw(used_addr, used_size, queue_size)? }; + + self.write::(LegacyRegister::QueueMsixVector, vector); + self.write::(LegacyRegister::QueueAddress, (addr as u32) >> 12); + + log::info!("virtio-core: enabled queue #{queue_index} (size={queue_size})"); + + let queue = Queue::new( + descriptor, + avail, + used, + LegacyBell(self.2.clone()), + queue_index, + vector, + ); + + spawn_irq_thread(irq_handle, &queue)?; + Ok(queue) + } + + fn load_config(&self, offset: u8, size: u8) -> u64 { + // We always enable MSI-X. So, the device configuration space offset will + // always be 0x18. + // + // Checkout 4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout + const DEVICE_SPACE_OFFSET: usize = 0x18; + + let size = size as usize; + let offset = DEVICE_SPACE_OFFSET + offset as usize; + + unsafe { + if size == size_of::() { + self.read_raw::(offset) as u64 + } else if size == size_of::() { + self.read_raw::(offset) as u64 + } else if size == size_of::() { + self.read_raw::(offset) as u64 + } else if size == size_of::() { + self.read_raw::(offset) as u64 + } else { + unreachable!() + } + } + } + + fn insert_status(&self, status: DeviceStatusFlags) { + let old = self.read::(LegacyRegister::DeviceStatus); + self.write(LegacyRegister::DeviceStatus, old | status.bits()); + } + + fn reinit_queue(&self, _queue: Arc) { + todo!() + } + + // Legacy devices do not have the `FEATURES_OK` bit. + fn finalize_features(&self) {} +} diff --git a/virtio-core/src/lib.rs b/virtio-core/src/lib.rs index 87b737f124..c38a88ac3d 100644 --- a/virtio-core/src/lib.rs +++ b/virtio-core/src/lib.rs @@ -6,4 +6,20 @@ pub mod utils; mod probe; +#[cfg(target_arch = "aarch64")] +#[path="arch/aarch64.rs"] +mod arch; + +#[cfg(target_arch = "x86")] +#[path="arch/x86.rs"] +mod arch; + +#[cfg(target_arch = "x86_64")] +#[path="arch/x86_64.rs"] +mod arch; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod legacy_transport; + + pub use probe::{probe_device, reinit, Device, MSIX_PRIMARY_VECTOR}; diff --git a/virtio-core/src/probe.rs b/virtio-core/src/probe.rs index a94d6f1925..1509187450 100644 --- a/virtio-core/src/probe.rs +++ b/virtio-core/src/probe.rs @@ -1,15 +1,12 @@ -use std::fs::File; + use std::ptr::NonNull; +use std::fs::File; use std::sync::Arc; - -use pcid_interface::irq_helpers::{allocate_single_interrupt_vector, read_bsp_apic_id}; -use pcid_interface::msi::{self, MsixCapability, MsixTableEntry}; use pcid_interface::*; - -use syscall::Io; +use pcid_interface::msi::{self, MsixTableEntry, MsixCapability}; use crate::spec::*; -use crate::transport::{Error, LegacyTransport, StandardTransport, Transport}; +use crate::transport::{Error, StandardTransport, Transport}; use crate::utils::{align_down, VolatileCell}; pub struct Device<'a> { @@ -24,7 +21,7 @@ pub struct Device<'a> { unsafe impl Send for Device<'_> {} unsafe impl Sync for Device<'_> {} -struct MsixInfo { +pub struct MsixInfo { pub virt_table_base: NonNull, pub capability: MsixCapability, } @@ -44,89 +41,6 @@ static_assertions::const_assert_eq!(std::mem::size_of::(), 16); pub const MSIX_PRIMARY_VECTOR: u16 = 0; -#[cfg(target_arch = "x86_64")] -fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result { - let pci_config = pcid_handle.fetch_config()?; - - // Extended message signaled interrupts. - let capability = match pcid_handle.feature_info(PciFeature::MsiX)? { - PciFeatureInfo::MsiX(capability) => capability, - _ => unreachable!(), - }; - - let table_size = capability.table_size(); - let table_base = capability.table_base_pointer(pci_config.func.bars); - let table_min_length = table_size * 16; - let pba_min_length = table_size.div_ceil(8); - - let pba_base = capability.pba_base_pointer(pci_config.func.bars); - - let bir = capability.table_bir() as usize; - let bar = pci_config.func.bars[bir]; - let bar_size = pci_config.func.bar_sizes[bir] as u64; - - let bar_ptr = match bar { - PciBar::Memory32(ptr) => ptr.into(), - PciBar::Memory64(ptr) => ptr, - _ => unreachable!(), - }; - - let address = unsafe { - common::physmap( - bar_ptr as usize, - bar_size as usize, - common::Prot::RW, - common::MemoryType::Uncacheable, - )? as usize - }; - - // Ensure that the table and PBA are be within the BAR. - { - let bar_range = bar_ptr..bar_ptr + bar_size; - assert!(bar_range.contains(&(table_base as u64 + table_min_length as u64))); - assert!(bar_range.contains(&(pba_base as u64 + pba_min_length as u64))); - } - - let virt_table_base = ((table_base - bar_ptr as usize) + address) as *mut MsixTableEntry; - - let mut info = MsixInfo { - virt_table_base: NonNull::new(virt_table_base).unwrap(), - capability, - }; - - // Allocate the primary MSI vector. - let interrupt_handle = { - let table_entry_pointer = info.table_entry_pointer(MSIX_PRIMARY_VECTOR as usize); - - let destination_id = read_bsp_apic_id().expect("virtio_core: `read_bsp_apic_id()` failed"); - let lapic_id = u8::try_from(destination_id).unwrap(); - - let rh = false; - let dm = false; - let addr = msi::x86_64::message_address(lapic_id, rh, dm); - - let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id) - .unwrap() - .expect("virtio_core: interrupt vector exhaustion"); - - let msg_data = msi::x86_64::message_data_edge_triggered(msi::x86_64::DeliveryMode::Fixed, vector); - - table_entry_pointer.addr_lo.write(addr); - table_entry_pointer.addr_hi.write(0); - table_entry_pointer.msg_data.write(msg_data); - table_entry_pointer - .vec_ctl - .writef(MsixTableEntry::VEC_CTL_MASK_BIT, false); - - interrupt_handle - }; - - pcid_handle.enable_feature(PciFeature::MsiX)?; - - log::info!("virtio: using MSI-X (interrupt_handle={interrupt_handle:?})"); - Ok(interrupt_handle) -} - #[cfg(not(target_arch = "x86_64"))] fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result { panic!("Msi-X only supported on x86_64"); @@ -268,7 +182,7 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result // According to the virtio specification, the device REQUIRED to support MSI-X. assert!(has_msix, "virtio: device does not support MSI-X"); - let irq_handle = enable_msix(pcid_handle)?; + let irq_handle = crate::arch::enable_msix(pcid_handle)?; log::info!("virtio: using standard PCI transport"); @@ -284,38 +198,7 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result Ok(device) } else { - if let PciBar::Port(port) = pci_header.get_bar(0) { - unsafe { syscall::iopl(3).expect("virtio: failed to set I/O privilege level") }; - log::warn!("virtio: using legacy transport"); - - static SHIM: VolatileCell = VolatileCell::new(0); - - let transport = LegacyTransport::new(port); - - // Setup interrupts. - let all_pci_features = pcid_handle.fetch_all_features()?; - let has_msix = all_pci_features - .iter() - .any(|(feature, _)| feature.is_msix()); - - // According to the virtio specification, the device REQUIRED to support MSI-X. - assert!(has_msix, "virtio: device does not support MSI-X"); - let irq_handle = enable_msix(pcid_handle)?; - - let device = Device { - transport, - irq_handle, - isr: &SHIM, - device_space: core::ptr::null_mut(), - }; - - device.transport.reset(); - reinit(&device)?; - - Ok(device) - } else { - unreachable!("virtio: legacy transport with non-port IO?") - } + crate::arch::probe_legacy_port_transport(&pci_header, pcid_handle) } } diff --git a/virtio-core/src/transport.rs b/virtio-core/src/transport.rs index 53934b6f56..95a9001fb8 100644 --- a/virtio-core/src/transport.rs +++ b/virtio-core/src/transport.rs @@ -1,9 +1,8 @@ use crate::spec::*; use crate::utils::align; -use common::dma::{Dma, PhysBox}; +use common::dma::Dma; use event::EventQueue; -use syscall::{Io, Pio}; use core::mem::size_of; use core::sync::atomic::{AtomicU16, Ordering}; @@ -44,7 +43,7 @@ impl From for Error { /// /// ## Panics /// If `queue_size` is not a power of two or is zero. -const fn queue_part_sizes(queue_size: usize) -> (usize, usize, usize) { +pub const fn queue_part_sizes(queue_size: usize) -> (usize, usize, usize) { assert!(queue_size.is_power_of_two() && queue_size != 0); const DESCRIPTOR_ALIGN: usize = 16; @@ -69,7 +68,7 @@ const fn queue_part_sizes(queue_size: usize) -> (usize, usize, usize) { ) } -fn spawn_irq_thread(irq_handle: &File, queue: &Arc>) -> Result<(), Error> { +pub fn spawn_irq_thread(irq_handle: &File, queue: &Arc>) -> Result<(), Error> { let irq_fd = irq_handle.as_raw_fd(); let queue_copy = queue.clone(); @@ -297,7 +296,9 @@ impl<'a> Available<'a> { for i in 0..queue_size { // Setting them to `u16::MAX` helps with debugging since qemu reports them // as illegal values. - ring.get_element_at(i).table_index.store(u16::MAX, Ordering::SeqCst); + ring.get_element_at(i) + .table_index + .store(u16::MAX, Ordering::SeqCst); } Ok(ring) @@ -478,190 +479,6 @@ pub trait Transport: Sync + Send { fn insert_status(&self, status: DeviceStatusFlags); } -pub enum LegacyRegister { - DeviceFeatures = 0, // u32 - - QueueAddress = 8, // u32 - QueueSize = 12, // u16 - QueueSelect = 14, // u16 - QueueNotify = 16, // u16 - - DeviceStatus = 18, // u8 - - ConfigMsixVector = 20, // u16 - QueueMsixVector = 22, // u16 -} - -struct LegacyBell(Weak); - -impl NotifyBell for LegacyBell { - #[inline] - fn ring(&self, queue_index: u16) { - let transport = self.0.upgrade().expect("bell: transport dropped"); - transport.write::(LegacyRegister::QueueNotify, queue_index) - } -} - -pub struct LegacyTransport(u16, AtomicU16, Weak); - -impl LegacyTransport { - pub(super) fn new(port: u16) -> Arc { - Arc::new_cyclic(|sref| Self(port, AtomicU16::new(0), sref.clone())) - } - - unsafe fn read_raw(&self, offset: usize) -> V - where - V: Sized + TryFrom, - >::Error: std::fmt::Debug, - { - let port = self.0 + offset as u16; - - if size_of::() == size_of::() { - V::try_from(Pio::::new(port).read() as u64).unwrap() - } else if size_of::() == size_of::() { - V::try_from(Pio::::new(port).read() as u64).unwrap() - } else if size_of::() == size_of::() { - V::try_from(Pio::::new(port).read() as u64).unwrap() - } else if size_of::() == size_of::() { - let lower = Pio::::new(port).read() as u64; - let upper = Pio::::new(port + size_of::() as u16).read() as u64; - - V::try_from(lower | (upper << 32)).unwrap() - } else { - unreachable!() - } - } - - fn read(&self, register: LegacyRegister) -> V - where - V: Sized + TryFrom, - >::Error: std::fmt::Debug, - { - unsafe { self.read_raw(register as usize) } - } - - fn write(&self, register: LegacyRegister, value: V) - where - V: Sized + TryInto, - >::Error: std::fmt::Debug, - { - if size_of::() == size_of::() { - Pio::::new(self.0 + register as u16).write(value.try_into().unwrap() as u8); - } else if size_of::() == size_of::() { - Pio::::new(self.0 + register as u16).write(value.try_into().unwrap() as u16); - } else if size_of::() == size_of::() { - Pio::::new(self.0 + register as u16).write(value.try_into().unwrap() as u32); - } else { - unreachable!() - } - } -} - -impl Transport for LegacyTransport { - fn reset(&self) { - self.write(LegacyRegister::DeviceStatus, 0u8); - - let status = self.read::(LegacyRegister::DeviceStatus); - assert_eq!(status, 0); - } - - fn check_device_feature(&self, feature: u32) -> bool { - assert!( - feature < 32, - "virtio: cannot query feature {feature} on a legacy device" - ); - self.read::(LegacyRegister::DeviceFeatures) & (1 << feature) == (1 << feature) - } - - fn ack_driver_feature(&self, feature: u32) { - assert!( - feature < 32, - "virtio: cannot ack feature {feature} on a legacy device" - ); - - let current = self.read::(LegacyRegister::DeviceFeatures); - self.write::(LegacyRegister::DeviceFeatures, current | (1 << feature)); - } - - fn setup_queue(&self, vector: u16, irq_handle: &File) -> Result, Error> { - let queue_index = self.1.fetch_add(1, Ordering::SeqCst); - self.write(LegacyRegister::QueueSelect, queue_index); - - let queue_size = self.read::(LegacyRegister::QueueSize) as usize; - let (desc_size, avail_size, used_size) = queue_part_sizes(queue_size); - - let size_bytes = desc_size + avail_size + used_size; - let addr = unsafe { syscall::physalloc(size_bytes).map_err(Error::SyscallError)? }; - - let descriptor = unsafe { - let physbox = PhysBox::from_raw_parts(addr, desc_size); - let table = Dma::<[Descriptor]>::from_physbox_uninit_unsized(physbox, queue_size)?; - - table.assume_init() - }; - - let avail_addr = addr + desc_size; - let avail = unsafe { Available::from_raw(avail_addr, avail_size, queue_size)? }; - - let used_addr = avail_addr + avail_size; - let used = unsafe { Used::from_raw(used_addr, used_size, queue_size)? }; - - self.write::(LegacyRegister::QueueMsixVector, vector); - self.write::(LegacyRegister::QueueAddress, (addr as u32) >> 12); - - log::info!("virtio-core: enabled queue #{queue_index} (size={queue_size})"); - - let queue = Queue::new( - descriptor, - avail, - used, - LegacyBell(self.2.clone()), - queue_index, - vector, - ); - - spawn_irq_thread(irq_handle, &queue)?; - Ok(queue) - } - - fn load_config(&self, offset: u8, size: u8) -> u64 { - // We always enable MSI-X. So, the device configuration space offset will - // always be 0x18. - // - // Checkout 4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout - const DEVICE_SPACE_OFFSET: usize = 0x18; - - let size = size as usize; - let offset = DEVICE_SPACE_OFFSET + offset as usize; - - unsafe { - if size == size_of::() { - self.read_raw::(offset) as u64 - } else if size == size_of::() { - self.read_raw::(offset) as u64 - } else if size == size_of::() { - self.read_raw::(offset) as u64 - } else if size == size_of::() { - self.read_raw::(offset) as u64 - } else { - unreachable!() - } - } - } - - fn insert_status(&self, status: DeviceStatusFlags) { - let old = self.read::(LegacyRegister::DeviceStatus); - self.write(LegacyRegister::DeviceStatus, old | status.bits()); - } - - fn reinit_queue(&self, _queue: Arc) { - todo!() - } - - // Legacy devices do not have the `FEATURES_OK` bit. - fn finalize_features(&self) {} -} - struct StandardBell<'a>(&'a mut AtomicU16); impl NotifyBell for StandardBell<'_> {