virtio-devices: Handle queue addresses translation

Upon the enablement of the queue by the guest, we perform a translation
of the descriptor table, the available ring and used ring addresses
prior to enabling the device itself. This only applies to the case where
the device is placed behind a vIOMMU, which is the reason why the
translation is needed. Indeed, the addresses allocated by the guest are
IOVAs which must be translated into GPAs before we can access the queue.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2022-01-26 11:35:48 +01:00 committed by Rob Bradford
parent 5ee0291ddc
commit ce6446501d
2 changed files with 30 additions and 2 deletions

View File

@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use versionize::{VersionMap, Versionize, VersionizeResult}; use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize; use versionize_derive::Versionize;
use virtio_queue::Queue; use virtio_queue::{AccessPlatform, Queue};
use vm_memory::{GuestAddress, GuestMemoryAtomic}; use vm_memory::{GuestAddress, GuestMemoryAtomic};
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped}; use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
@ -52,6 +52,7 @@ impl VersionMapped for VirtioPciCommonConfigState {}
/// le64 queue_avail; // 0x28 // read-write /// le64 queue_avail; // 0x28 // read-write
/// le64 queue_used; // 0x30 // read-write /// le64 queue_used; // 0x30 // read-write
pub struct VirtioPciCommonConfig { pub struct VirtioPciCommonConfig {
pub access_platform: Option<Arc<dyn AccessPlatform>>,
pub driver_status: u8, pub driver_status: u8,
pub config_generation: u8, pub config_generation: u8,
pub device_feature_select: u32, pub device_feature_select: u32,
@ -196,7 +197,32 @@ impl VirtioPciCommonConfig {
0x16 => self.queue_select = value, 0x16 => self.queue_select = value,
0x18 => self.with_queue_mut(queues, |q| q.state.size = value), 0x18 => self.with_queue_mut(queues, |q| q.state.size = value),
0x1a => self.msix_queues.lock().unwrap()[self.queue_select as usize] = value, 0x1a => self.msix_queues.lock().unwrap()[self.queue_select as usize] = value,
0x1c => self.with_queue_mut(queues, |q| q.enable(value == 1)), 0x1c => self.with_queue_mut(queues, |q| {
let ready = value == 1;
q.set_ready(ready);
// Translate address of descriptor table and vrings.
if let Some(access_platform) = &self.access_platform {
if ready {
let desc_table =
access_platform.translate(q.state.desc_table.0, 0).unwrap();
let avail_ring =
access_platform.translate(q.state.avail_ring.0, 0).unwrap();
let used_ring = access_platform.translate(q.state.used_ring.0, 0).unwrap();
q.set_desc_table_address(
Some((desc_table & 0xffff_ffff) as u32),
Some((desc_table >> 32) as u32),
);
q.set_avail_ring_address(
Some((avail_ring & 0xffff_ffff) as u32),
Some((avail_ring >> 32) as u32),
);
q.set_used_ring_address(
Some((used_ring & 0xffff_ffff) as u32),
Some((used_ring >> 32) as u32),
);
}
}
}),
_ => { _ => {
warn!("invalid virtio register word write: 0x{:x}", offset); warn!("invalid virtio register word write: 0x{:x}", offset);
} }
@ -374,6 +400,7 @@ mod tests {
#[test] #[test]
fn write_base_regs() { fn write_base_regs() {
let mut regs = VirtioPciCommonConfig { let mut regs = VirtioPciCommonConfig {
access_platform: None,
driver_status: 0xaa, driver_status: 0xaa,
config_generation: 0x55, config_generation: 0x55,
device_feature_select: 0x0, device_feature_select: 0x0,

View File

@ -423,6 +423,7 @@ impl VirtioPciDevice {
id, id,
configuration, configuration,
common_config: VirtioPciCommonConfig { common_config: VirtioPciCommonConfig {
access_platform,
driver_status: 0, driver_status: 0,
config_generation: 0, config_generation: 0,
device_feature_select: 0, device_feature_select: 0,