Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions crates/machine-arm/src/virt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@
//! [`VirtMachine::new`] builds the board and loads the kernel. Plug virtio devices with
//! [`VirtMachine::attach_virtio`] (virtio-mmio) or [`VirtMachine::attach_virtio_pci`] (a PCI
//! function on the root bus), and root ports with [`VirtMachine::attach_pcie_root_port`] and
//! functions behind them with [`VirtMachine::attach_virtio_pci_on`], then call [`VirtMachine::machine_done`], which finishes and
//! loads the device tree and resets the board. [`VirtMachine::create_vcpus`] then makes the
//! vCPUs on a [`Jit`], wired to the GIC, and resets them. On a guest reset request
//! ([`VirtMachine::take_request`]) the runner calls [`VirtMachine::system_reset`] and, on each
//! vCPU's thread, [`VirtMachine::reset_cpu`]. The runner also runs the timers of
//! [`VirtMachine::clock`]: the generic timers arm their deadlines there.
//! functions behind them with [`VirtMachine::attach_virtio_pci_on`], then call
//! [`VirtMachine::machine_done`], which finishes and loads the device tree and resets the
//! board. [`VirtMachine::create_vcpus`] then makes the vCPUs on a [`Jit`], wired to the GIC,
//! and resets them. On a guest reset request ([`VirtMachine::take_request`]) the runner calls
//! [`VirtMachine::system_reset`] and, on each vCPU's thread, [`VirtMachine::reset_cpu`]. The
//! runner also runs the timers of [`VirtMachine::clock`]: the generic timers arm their
//! deadlines there.
//!
//! # Not modelled yet
//!
Expand Down Expand Up @@ -1690,11 +1691,20 @@ impl VirtMachine {
/// does: the guest probes the transports from the top, so the first device plugged is
/// found first. Returns the transport index.
pub fn attach_virtio(&self, class: Box<dyn VirtioDeviceClass>) -> Result<usize, String> {
self.attach_virtio_with(class, false)
}

/// [`VirtMachine::attach_virtio`] with the `iommu_platform` property of the virtio device.
pub fn attach_virtio_with(
&self,
class: Box<dyn VirtioDeviceClass>,
iommu_platform: bool,
) -> Result<usize, String> {
let Some(index) = (0..self.virtio.len()).rev().find(|&i| !self.virtio[i].is_plugged())
else {
return Err("No 'virtio-bus' bus found for device".to_string());
};
self.attach_virtio_at(index, class, VIRTIO_MMIO_FORCE_LEGACY_DEFAULT)?;
self.attach_virtio_at_with(index, class, VIRTIO_MMIO_FORCE_LEGACY_DEFAULT, iommu_platform)?;
Ok(index)
}

Expand All @@ -1705,6 +1715,21 @@ impl VirtMachine {
index: usize,
class: Box<dyn VirtioDeviceClass>,
force_legacy: bool,
) -> Result<(), String> {
self.attach_virtio_at_with(index, class, force_legacy, false)
}

/// [`VirtMachine::attach_virtio_at`] with the `iommu_platform` property of the virtio
/// device. A virtio-mmio transport has no IOMMU in front of it, so as in
/// `virtio_bus_device_plugged()` without `get_dma_as` the device offers
/// `VIRTIO_F_IOMMU_PLATFORM` and does its DMA in system memory. A legacy transport only
/// shows the low 32 feature bits, so the guest does not see it there.
pub fn attach_virtio_at_with(
&self,
index: usize,
class: Box<dyn VirtioDeviceClass>,
force_legacy: bool,
iommu_platform: bool,
) -> Result<(), String> {
let force_legacy = force_legacy && self.virtio_force_legacy;
if self.done {
Expand All @@ -1718,7 +1743,10 @@ impl VirtMachine {
}
let memory: Arc<dyn GuestMemory + Send + Sync> =
Arc::new(WeakGuestMemory(Arc::downgrade(&self.memory_as)));
let backend = VirtioBackend::new(class, memory).map_err(err)?;
let mut backend = VirtioBackend::new(class, memory).map_err(err)?;
if iommu_platform {
backend.vdev_mut().set_host_feature(VIRTIO_F_IOMMU_PLATFORM, true);
}
let t = VirtioMmio::new(Some(backend), force_legacy).map_err(err)?;
t.irq().connect(slot.gsi.clone());
*slot.transport.write().unwrap_or_else(PoisonError::into_inner) = Arc::new(t);
Expand Down
21 changes: 21 additions & 0 deletions crates/machine-arm/tests/virt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,27 @@ fn virtio_plugging() {
);
}

#[test]
fn virtio_mmio_iommu_platform() {
let rng =
|| Box::new(VirtioRng::new(Box::new(RandomFile::default()), VirtioRngConf::default()));
let mut m = VirtMachine::new(VirtConfig::default()).unwrap();
assert_eq!(m.attach_virtio_with(rng(), true).unwrap(), 31);
m.attach_virtio_at_with(30, rng(), false, true).unwrap();
m.attach_virtio_at_with(29, rng(), false, false).unwrap();
m.machine_done().unwrap();
// The high word of DEVICE_FEATURES: VIRTIO_F_IOMMU_PLATFORM is bit 33. The legacy
// transport shows nothing there, as in QEMU.
let high = |i: u64| {
let base = VIRT_MMIO + i * 0x200;
w(&m, base + 0x14, 4, 1);
r32(&m, base + 0x10)
};
assert_eq!(high(31), 0);
assert_eq!(high(30) & 3, 3);
assert_eq!(high(29) & 3, 1);
}

#[test]
fn pcie_host() {
let rng =
Expand Down
16 changes: 11 additions & 5 deletions crates/system/src/arm/devices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
//! the root bus `pcie.0` (`virtio-*-pci`) or on a virtio-mmio transport (`virtio-*-device`).
//! The other virtio types and `loader` fail with "... is not supported with this machine by
//! ruvm yet". The PCI types take `addr`, `bus=pcie.0`, `disable-legacy`,
//! `disable-modern` and `vectors`; the virtio-mmio ones take `bus=virtio-mmio-bus.<n>`.
//! `disable-modern` and `vectors`; the virtio-mmio ones take `bus=virtio-mmio-bus.<n>`. Both
//! take `iommu_platform`.
//! On virt, `-device arm-smmuv3,primary-bus=pcie.0` is the SMMUv3 on the platform bus that
//! translates the functions plugged after it.
//! On virt, `-device pcie-root-port` with an `id` puts a root port on `pcie.0`, and the PCI
Expand Down Expand Up @@ -119,7 +120,8 @@ pub(crate) struct Plug {
devfn: Option<u8>,
/// `bus=virtio-mmio-bus.<n>` of a virtio-mmio device.
mmio_bus: Option<usize>,
/// `iommu_platform` of a PCI function: its DMA goes through the SMMU.
/// `iommu_platform`: the DMA of a PCI function goes through the SMMU. A virtio-mmio
/// device only offers `VIRTIO_F_IOMMU_PLATFORM`, since no IOMMU is in front of it.
iommu_platform: bool,
/// The properties of a virtio-iommu.
iommu: Option<VirtioIommuConf>,
Expand Down Expand Up @@ -280,7 +282,7 @@ fn plan_virtio(
"disable-modern" if pci => {
plug.props.disable_modern = prop_bool(k, v).map_err(|_| bad_value(k, v))?;
}
"iommu_platform" if pci => {
"iommu_platform" => {
plug.iommu_platform = prop_bool(k, v).map_err(|_| bad_value(k, v))?;
}
"vectors" if pci => {
Expand Down Expand Up @@ -933,12 +935,14 @@ pub(crate) fn plug(
Some(VirtioHandle::Pci(dev))
}
(Target::Virt(board), Transport::Mmio, Some(n)) => {
board.attach_virtio_at(n, class, VIRTIO_MMIO_FORCE_LEGACY_DEFAULT).map_err(at)?;
board
.attach_virtio_at_with(n, class, VIRTIO_MMIO_FORCE_LEGACY_DEFAULT, iommu)
.map_err(at)?;
board.virtio_transport(n).map(VirtioHandle::Mmio)
}
(Target::Virt(board), Transport::Mmio, None) => {
let n = board
.attach_virtio(class)
.attach_virtio_with(class, iommu)
.map_err(|e| at(format!("{e} '{}'", plug.typename)))?;
board.virtio_transport(n).map(VirtioHandle::Mmio)
}
Expand Down Expand Up @@ -1049,6 +1053,8 @@ mod tests {
assert_eq!(p.virtio[0].devfn, Some(0xff));
let p = plan(V, &[], &[dev("virtio-rng-pci,iommu_platform=on")]).unwrap();
assert!(p.virtio[0].iommu_platform);
let p = plan(V, &[], &[dev("virtio-rng-device,iommu_platform=on")]).unwrap();
assert!(p.virtio[0].iommu_platform);

let err = |args: &[&str]| {
let devs: Vec<_> = args.iter().map(|a| dev(a)).collect();
Expand Down