mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Move virtio-mmio bus outside OSTD
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
238b89da46
commit
a3c5ab8cb4
@ -24,5 +24,9 @@ component = { path = "../../libs/comp-sys/component" }
|
||||
log = "0.4"
|
||||
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
||||
|
||||
[features]
|
||||
all = ["cvm_guest"]
|
||||
cvm_guest = ["ostd/cvm_guest"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
94
kernel/comps/virtio/src/transport/mmio/bus/bus.rs
Normal file
94
kernel/comps/virtio/src/transport/mmio/bus/bus.rs
Normal file
@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! MMIO bus.
|
||||
|
||||
use alloc::{collections::VecDeque, fmt::Debug, sync::Arc, vec::Vec};
|
||||
|
||||
use log::{debug, error};
|
||||
use ostd::bus::BusProbeError;
|
||||
|
||||
use super::common_device::MmioCommonDevice;
|
||||
|
||||
/// MMIO device trait
|
||||
pub trait MmioDevice: Sync + Send + Debug {
|
||||
/// Device ID
|
||||
fn device_id(&self) -> u32;
|
||||
}
|
||||
|
||||
/// MMIO device driver.
|
||||
pub trait MmioDriver: Sync + Send + Debug {
|
||||
/// Probe an unclaimed mmio device.
|
||||
///
|
||||
/// If the driver matches and succeeds in initializing the unclaimed device,
|
||||
/// then the driver will return an claimed instance of the device,
|
||||
/// signaling that the mmio device is now ready to work.
|
||||
///
|
||||
/// Once a device is matched and claimed by a driver,
|
||||
/// it won't be fed to another driver for probing.
|
||||
fn probe(
|
||||
&self,
|
||||
device: MmioCommonDevice,
|
||||
) -> Result<Arc<dyn MmioDevice>, (BusProbeError, MmioCommonDevice)>;
|
||||
}
|
||||
|
||||
/// MMIO bus
|
||||
pub struct MmioBus {
|
||||
common_devices: VecDeque<MmioCommonDevice>,
|
||||
devices: Vec<Arc<dyn MmioDevice>>,
|
||||
drivers: Vec<Arc<dyn MmioDriver>>,
|
||||
}
|
||||
|
||||
impl MmioBus {
|
||||
/// Registers a MMIO driver to the MMIO bus.
|
||||
pub fn register_driver(&mut self, driver: Arc<dyn MmioDriver>) {
|
||||
debug!("Register driver:{:#x?}", driver);
|
||||
let length = self.common_devices.len();
|
||||
for _ in (0..length).rev() {
|
||||
let common_device = self.common_devices.pop_front().unwrap();
|
||||
let device_id = common_device.read_device_id().unwrap();
|
||||
let device = match driver.probe(common_device) {
|
||||
Ok(device) => {
|
||||
debug_assert!(device_id == device.device_id());
|
||||
self.devices.push(device);
|
||||
continue;
|
||||
}
|
||||
Err((err, device)) => {
|
||||
if err != BusProbeError::DeviceNotMatch {
|
||||
error!("MMIO device construction failed, reason: {:?}", err);
|
||||
}
|
||||
device
|
||||
}
|
||||
};
|
||||
self.common_devices.push_back(device);
|
||||
}
|
||||
self.drivers.push(driver);
|
||||
}
|
||||
|
||||
pub(super) fn register_mmio_device(&mut self, mut mmio_device: MmioCommonDevice) {
|
||||
let device_id = mmio_device.read_device_id().unwrap();
|
||||
for driver in self.drivers.iter() {
|
||||
mmio_device = match driver.probe(mmio_device) {
|
||||
Ok(device) => {
|
||||
debug_assert!(device_id == device.device_id());
|
||||
self.devices.push(device);
|
||||
return;
|
||||
}
|
||||
Err((err, common_device)) => {
|
||||
if err != BusProbeError::DeviceNotMatch {
|
||||
error!("MMIO device construction failed, reason: {:?}", err);
|
||||
}
|
||||
common_device
|
||||
}
|
||||
};
|
||||
}
|
||||
self.common_devices.push_back(mmio_device);
|
||||
}
|
||||
|
||||
pub(super) const fn new() -> Self {
|
||||
Self {
|
||||
common_devices: VecDeque::new(),
|
||||
devices: Vec::new(),
|
||||
drivers: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
82
kernel/comps/virtio/src/transport/mmio/bus/common_device.rs
Normal file
82
kernel/comps/virtio/src/transport/mmio/bus/common_device.rs
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! MMIO device common definitions or functions.
|
||||
|
||||
use int_to_c_enum::TryFromInt;
|
||||
use log::info;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use ostd::arch::kernel::MappedIrqLine;
|
||||
#[cfg(target_arch = "riscv64")] // TODO: Add `MappedIrqLine` support for RISC-V.
|
||||
use ostd::trap::IrqLine as MappedIrqLine;
|
||||
use ostd::{io::IoMem, mm::VmIoOnce, trap::IrqLine, Error, Result};
|
||||
|
||||
/// A MMIO common device.
|
||||
#[derive(Debug)]
|
||||
pub struct MmioCommonDevice {
|
||||
io_mem: IoMem,
|
||||
irq: MappedIrqLine,
|
||||
}
|
||||
|
||||
impl MmioCommonDevice {
|
||||
pub(super) fn new(io_mem: IoMem, irq: MappedIrqLine) -> Self {
|
||||
debug_assert!(mmio_check_magic(&io_mem));
|
||||
|
||||
let this = Self { io_mem, irq };
|
||||
info!(
|
||||
"[Virtio]: Found MMIO device at {:#x}, device ID {}, IRQ number {}",
|
||||
this.io_mem.paddr(),
|
||||
this.read_device_id().unwrap(),
|
||||
this.irq.num(),
|
||||
);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Returns a reference to the I/O memory.
|
||||
pub fn io_mem(&self) -> &IoMem {
|
||||
&self.io_mem
|
||||
}
|
||||
|
||||
/// Reads the device ID from the I/O memory.
|
||||
pub fn read_device_id(&self) -> Result<u32> {
|
||||
mmio_read_device_id(&self.io_mem)
|
||||
}
|
||||
|
||||
/// Reads the version number from the I/O memory.
|
||||
pub fn read_version(&self) -> Result<VirtioMmioVersion> {
|
||||
VirtioMmioVersion::try_from(mmio_read_version(&self.io_mem)?)
|
||||
.map_err(|_| Error::InvalidArgs)
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the IRQ line.
|
||||
pub fn irq(&self) -> &IrqLine {
|
||||
&self.irq
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtio MMIO version.
|
||||
#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(u32)]
|
||||
pub enum VirtioMmioVersion {
|
||||
/// Legacy
|
||||
Legacy = 1,
|
||||
/// Modern
|
||||
Modern = 2,
|
||||
}
|
||||
|
||||
const OFFSET_TO_MAGIC: usize = 0;
|
||||
const OFFSET_TO_VERSION: usize = 4;
|
||||
const OFFSET_TO_DEVICE_ID: usize = 8;
|
||||
|
||||
pub(super) fn mmio_check_magic(io_mem: &IoMem) -> bool {
|
||||
const MAGIC_VALUE: u32 = 0x74726976;
|
||||
io_mem
|
||||
.read_once::<u32>(OFFSET_TO_MAGIC)
|
||||
.is_ok_and(|val| val == MAGIC_VALUE)
|
||||
}
|
||||
fn mmio_read_version(io_mem: &IoMem) -> Result<u32> {
|
||||
io_mem.read_once(OFFSET_TO_VERSION)
|
||||
}
|
||||
pub(super) fn mmio_read_device_id(io_mem: &IoMem) -> Result<u32> {
|
||||
io_mem.read_once(OFFSET_TO_DEVICE_ID)
|
||||
}
|
109
kernel/comps/virtio/src/transport/mmio/bus/mod.rs
Normal file
109
kernel/comps/virtio/src/transport/mmio/bus/mod.rs
Normal file
@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Virtio over MMIO
|
||||
|
||||
use bus::MmioBus;
|
||||
use ostd::sync::SpinLock;
|
||||
|
||||
#[expect(clippy::module_inception)]
|
||||
pub(super) mod bus;
|
||||
pub(super) mod common_device;
|
||||
|
||||
/// The MMIO bus instance.
|
||||
pub(super) static MMIO_BUS: SpinLock<MmioBus> = SpinLock::new(MmioBus::new());
|
||||
|
||||
pub(super) fn init() {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ostd::if_tdx_enabled!({
|
||||
// TODO: support virtio-mmio devices on TDX.
|
||||
//
|
||||
// Currently, virtio-mmio devices need to acquire sub-page MMIO regions,
|
||||
// which are not supported by `IoMem::acquire` in the TDX environment.
|
||||
} else {
|
||||
x86_probe();
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn x86_probe() {
|
||||
use common_device::{mmio_check_magic, mmio_read_device_id, MmioCommonDevice};
|
||||
use log::debug;
|
||||
use ostd::{arch::kernel::IRQ_CHIP, io::IoMem, trap::IrqLine};
|
||||
|
||||
// TODO: The correct method for detecting VirtIO-MMIO devices on x86_64 systems is to parse the
|
||||
// kernel command line if ACPI tables are absent [1], or the ACPI SSDT if ACPI tables are
|
||||
// present [2]. Neither of them is supported for now. This function's approach of blindly
|
||||
// scanning the MMIO region is only a workaround.
|
||||
// [1]: https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/drivers/virtio/virtio_mmio.c#L733
|
||||
// [2]: https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/drivers/virtio/virtio_mmio.c#L840
|
||||
|
||||
// Constants from QEMU MicroVM. We should remove them as they're QEMU's implementation details.
|
||||
//
|
||||
// https://github.com/qemu/qemu/blob/3c5a5e213e5f08fbfe70728237f7799ac70f5b99/hw/i386/microvm.c#L201
|
||||
const QEMU_MMIO_BASE: usize = 0xFEB0_0000;
|
||||
const QEMU_MMIO_SIZE: usize = 512;
|
||||
// https://github.com/qemu/qemu/blob/3c5a5e213e5f08fbfe70728237f7799ac70f5b99/hw/i386/microvm.c#L196
|
||||
const QEMU_IOAPIC1_GSI_BASE: u32 = 16;
|
||||
const QEMU_IOAPIC1_NUM_TRANS: u32 = 8;
|
||||
// https://github.com/qemu/qemu/blob/3c5a5e213e5f08fbfe70728237f7799ac70f5b99/hw/i386/microvm.c#L192
|
||||
const QEMU_IOAPIC2_GSI_BASE: u32 = 24;
|
||||
const QEMU_IOAPIC2_NUM_TRANS: u32 = 24;
|
||||
|
||||
let mut mmio_bus = MMIO_BUS.lock();
|
||||
|
||||
let irq_chip = IRQ_CHIP.get().unwrap();
|
||||
let (gsi_base, num_trans) = match irq_chip.count_io_apics() {
|
||||
1 => (QEMU_IOAPIC1_GSI_BASE, QEMU_IOAPIC1_NUM_TRANS),
|
||||
2.. => (QEMU_IOAPIC2_GSI_BASE, QEMU_IOAPIC2_NUM_TRANS),
|
||||
0 => {
|
||||
debug!("[Virtio]: Skip MMIO detection because there are no I/O APICs");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for index in 0..num_trans {
|
||||
let mmio_base = QEMU_MMIO_BASE + (index as usize) * QEMU_MMIO_SIZE;
|
||||
let Ok(io_mem) = IoMem::acquire(mmio_base..(mmio_base + QEMU_MMIO_SIZE)) else {
|
||||
debug!(
|
||||
"[Virtio]: Abort MMIO detection at {:#x} because the MMIO address is not available",
|
||||
mmio_base
|
||||
);
|
||||
break;
|
||||
};
|
||||
|
||||
// We now check the the rquirements specified in Virtual I/O Device (VIRTIO) Version 1.3,
|
||||
// Section 4.2.2.2 Driver Requirements: MMIO Device Register Layout.
|
||||
|
||||
// "The driver MUST ignore a device with MagicValue which is not 0x74726976, although it
|
||||
// MAY report an error."
|
||||
if !mmio_check_magic(&io_mem) {
|
||||
debug!(
|
||||
"[Virtio]: Abort MMIO detection at {:#x} because the magic number does not match",
|
||||
mmio_base
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: "The driver MUST ignore a device with Version which is not 0x2, although it MAY
|
||||
// report an error."
|
||||
|
||||
// "The driver MUST ignore a device with DeviceID 0x0, but MUST NOT report any error."
|
||||
match mmio_read_device_id(&io_mem) {
|
||||
Err(_) | Ok(0) => continue,
|
||||
Ok(_) => (),
|
||||
}
|
||||
|
||||
let Ok(irq_line) = IrqLine::alloc()
|
||||
.and_then(|irq_line| irq_chip.map_gsi_pin_to(irq_line, gsi_base + index))
|
||||
else {
|
||||
debug!(
|
||||
"[Virtio]: Ignore MMIO device at {:#x} because its IRQ line is not available",
|
||||
mmio_base
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let device = MmioCommonDevice::new(io_mem, irq_line);
|
||||
mmio_bus.register_mmio_device(device);
|
||||
}
|
||||
}
|
@ -7,20 +7,21 @@ 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},
|
||||
},
|
||||
pci::cfg_space::Bar,
|
||||
},
|
||||
bus::pci::cfg_space::Bar,
|
||||
io::IoMem,
|
||||
mm::{DmaCoherent, PAGE_SIZE},
|
||||
sync::RwLock,
|
||||
trap::IrqCallbackFunction,
|
||||
};
|
||||
|
||||
use super::{layout::VirtioMmioLayout, multiplex::MultiplexIrq};
|
||||
use super::{
|
||||
bus::{
|
||||
bus::MmioDevice,
|
||||
common_device::{MmioCommonDevice, VirtioMmioVersion},
|
||||
},
|
||||
layout::VirtioMmioLayout,
|
||||
multiplex::MultiplexIrq,
|
||||
};
|
||||
use crate::{
|
||||
queue::{AvailRing, Descriptor, UsedRing},
|
||||
transport::{ConfigManager, DeviceStatus, VirtioTransport, VirtioTransportError},
|
||||
@ -36,7 +37,7 @@ pub struct VirtioMmioDevice {
|
||||
pub struct VirtioMmioTransport {
|
||||
layout: SafePtr<VirtioMmioLayout, IoMem>,
|
||||
device: Arc<VirtioMmioDevice>,
|
||||
common_device: ostd::bus::mmio::common_device::MmioCommonDevice,
|
||||
common_device: MmioCommonDevice,
|
||||
multiplex: Arc<RwLock<MultiplexIrq>>,
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,15 @@
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
|
||||
use ostd::{
|
||||
bus::{
|
||||
mmio::{
|
||||
bus::{MmioDevice, MmioDriver},
|
||||
common_device::MmioCommonDevice,
|
||||
},
|
||||
BusProbeError,
|
||||
},
|
||||
sync::SpinLock,
|
||||
};
|
||||
use ostd::{bus::BusProbeError, sync::SpinLock};
|
||||
|
||||
use super::device::VirtioMmioTransport;
|
||||
use super::{
|
||||
bus::{
|
||||
bus::{MmioDevice, MmioDriver},
|
||||
common_device::MmioCommonDevice,
|
||||
},
|
||||
device::VirtioMmioTransport,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VirtioMmioDriver {
|
||||
|
@ -2,18 +2,22 @@
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use ostd::bus::mmio::MMIO_BUS;
|
||||
use bus::MMIO_BUS;
|
||||
use spin::Once;
|
||||
|
||||
use self::driver::VirtioMmioDriver;
|
||||
|
||||
mod bus;
|
||||
pub mod device;
|
||||
pub mod driver;
|
||||
pub mod layout;
|
||||
pub mod multiplex;
|
||||
|
||||
pub static VIRTIO_MMIO_DRIVER: Once<Arc<VirtioMmioDriver>> = Once::new();
|
||||
|
||||
pub fn virtio_mmio_init() {
|
||||
bus::init();
|
||||
|
||||
VIRTIO_MMIO_DRIVER.call_once(|| Arc::new(VirtioMmioDriver::new()));
|
||||
MMIO_BUS
|
||||
.lock()
|
||||
|
Reference in New Issue
Block a user