Arch: Vfio device could be created and configured at runtime

When a vfio pci device is added through hotplug in, it should be configured
at runtime and added into pci_root->devices tree, so pci_root is added
into linux.

BUG=b:185084350
TEST=Boot a guest with and without passthrough device

Change-Id: Ibcb5f4a849134f64fbceeac645bebd80d6ca72d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2954672
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Xiong Zhang 2021-04-12 15:07:17 +08:00 committed by Commit Bot
parent b56de80b07
commit 8c9fe3ef34
6 changed files with 80 additions and 5 deletions

View file

@ -167,6 +167,7 @@ pub enum Error {
SetDeviceAttr(base::Error),
SetReg(base::Error),
SetupGuestMemory(GuestMemoryError),
Unsupported,
VcpuInit(base::Error),
}
@ -206,6 +207,7 @@ impl Display for Error {
SetDeviceAttr(e) => write!(f, "failed to set device attr: {}", e),
SetReg(e) => write!(f, "failed to set register: {}", e),
SetupGuestMemory(e) => write!(f, "failed to set up guest memory: {}", e),
Unsupported => write!(f, "this function isn't supported"),
VcpuInit(e) => write!(f, "failed to initialize VCPU: {}", e),
}
}
@ -457,6 +459,7 @@ impl arch::LinuxArch for AArch64 {
delay_rt: components.delay_rt,
bat_control: None,
resume_notify_devices: Vec::new(),
root_config: pci_bus,
})
}
@ -473,6 +476,16 @@ impl arch::LinuxArch for AArch64 {
// AArch64 doesn't configure vcpus on the vcpu thread, so nothing to do here.
Ok(())
}
fn register_pci_device<V: VmAArch64, Vcpu: VcpuAArch64>(
_linux: &mut RunnableLinuxVm<V, Vcpu>,
_device: Box<dyn PciDevice>,
_minijail: Option<Minijail>,
_resources: &mut SystemAllocator,
) -> std::result::Result<(), Self::Error> {
// hotplug function isn't verified on AArch64, so set it unsupported here.
Err(Error::Unsupported)
}
}
impl AArch64 {

View file

@ -35,12 +35,12 @@ use gdbstub_arch::x86::reg::X86_64CoreRegs as GdbStubRegs;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
use {
devices::IrqChipAArch64 as IrqChipArch,
devices::{IrqChipAArch64 as IrqChipArch, PciConfigMmio as RootConfigArch},
hypervisor::{Hypervisor as HypervisorArch, VcpuAArch64 as VcpuArch, VmAArch64 as VmArch},
};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use {
devices::IrqChipX86_64 as IrqChipArch,
devices::{IrqChipX86_64 as IrqChipArch, PciConfigIo as RootConfigArch},
hypervisor::{HypervisorX86_64 as HypervisorArch, VcpuX86_64 as VcpuArch, VmX86_64 as VmArch},
};
@ -120,6 +120,7 @@ pub struct RunnableLinuxVm<V: VmArch, Vcpu: VcpuArch> {
pub gdb: Option<(u32, Tube)>,
/// Devices to be notified before the system resumes from the S3 suspended state.
pub resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
pub root_config: Arc<Mutex<RootConfigArch>>,
}
/// The device and optional jail.
@ -203,6 +204,14 @@ pub trait LinuxArch {
no_smt: bool,
) -> Result<(), Self::Error>;
/// Configures and add a pci device into vm
fn register_pci_device<V: VmArch, Vcpu: VcpuArch>(
linux: &mut RunnableLinuxVm<V, Vcpu>,
device: Box<dyn PciDevice>,
minijail: Option<Minijail>,
resources: &mut SystemAllocator,
) -> Result<(), Self::Error>;
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
/// Reads vCPU's registers.
fn debug_read_registers<T: VcpuArch>(vcpu: &T) -> Result<GdbStubRegs, Self::Error>;
@ -322,7 +331,6 @@ pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>(
linux: &mut RunnableLinuxVm<V, Vcpu>,
mut device: Box<dyn PciDevice>,
jail: Option<Minijail>,
root: &mut PciRoot,
resources: &mut SystemAllocator,
) -> Result<(), DeviceRegistrationError> {
// Allocate PCI device address before allocating BARs.
@ -390,7 +398,12 @@ pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>(
device.on_sandboxed();
Arc::new(Mutex::new(device))
};
root.add_device(pci_address, arced_dev.clone());
linux
.root_config
.lock()
.add_device(pci_address, arced_dev.clone());
for range in &mmio_ranges {
linux
.mmio_bus

View file

@ -261,6 +261,10 @@ impl PciConfigIo {
};
self.config_address = (self.config_address & !mask) | value;
}
pub fn add_device(&mut self, address: PciAddress, device: Arc<Mutex<dyn BusDevice>>) {
self.pci_root.add_device(address, device)
}
}
impl BusDevice for PciConfigIo {
@ -321,6 +325,10 @@ impl PciConfigMmio {
self.pci_root
.config_space_write(address, register, offset, data)
}
pub fn add_device(&mut self, address: PciAddress, device: Arc<Mutex<dyn BusDevice>>) {
self.pci_root.add_device(address, device)
}
}
impl BusDevice for PciConfigMmio {

View file

@ -43,6 +43,7 @@ pub enum Error {
CloneEvent(base::Error),
CloneVcpu(base::Error),
ConfigureVcpu(<Arch as LinuxArch>::Error),
ConfigureVfioDevice(<Arch as LinuxArch>::Error),
ConnectTube(io::Error),
#[cfg(feature = "audio")]
CreateAc97(devices::PciDeviceError),
@ -169,6 +170,7 @@ impl Display for Error {
CloneEvent(e) => write!(f, "failed to clone event: {}", e),
CloneVcpu(e) => write!(f, "failed to clone vcpu: {}", e),
ConfigureVcpu(e) => write!(f, "failed to configure vcpu: {}", e),
ConfigureVfioDevice(e) => write!(f, "Failed to configure vfio device:{}", e),
ConnectTube(e) => write!(f, "failed to connect to tube: {}", e),
#[cfg(feature = "audio")]
CreateAc97(e) => write!(f, "failed to create ac97 device: {}", e),

View file

@ -2512,6 +2512,32 @@ where
)
}
#[allow(dead_code)]
fn add_vfio_device<V: VmArch, Vcpu: VcpuArch>(
linux: &mut RunnableLinuxVm<V, Vcpu>,
sys_allocator: &mut SystemAllocator,
cfg: &Config,
control_tubes: &mut Vec<TaggedControlTube>,
vfio_path: &Path,
) -> Result<()> {
let vm = &linux.vm;
let mut endpoints: BTreeMap<u32, Arc<Mutex<VfioContainer>>> = BTreeMap::new();
let (vfio_pci_device, jail) = create_vfio_device(
cfg,
vm,
sys_allocator,
control_tubes,
vfio_path,
&mut endpoints,
false,
)?;
Arch::register_pci_device(linux, vfio_pci_device, jail, sys_allocator)
.map_err(Error::ConfigureVfioDevice)
}
#[allow(dead_code)]
fn remove_vfio_device() {}
/// Signals all running VCPUs to vmexit, sends VcpuControl message to each VCPU tube, and tells
/// `irq_chip` to stop blocking halted VCPUs. The channel message is set first because both the
/// signal and the irq_chip kick could cause the VCPU thread to continue through the VCPU run

View file

@ -83,6 +83,7 @@ pub enum Error {
CloneEvent(base::Error),
CloneIrqChip(base::Error),
Cmdline(kernel_cmdline::Error),
ConfigurePciDevice(arch::DeviceRegistrationError),
ConfigureSystem,
CreateBatDevices(arch::DeviceRegistrationError),
CreateDevices(Box<dyn StdError>),
@ -143,6 +144,7 @@ impl Display for Error {
CloneEvent(e) => write!(f, "unable to clone an Event: {}", e),
CloneIrqChip(e) => write!(f, "failed to clone IRQ chip: {}", e),
Cmdline(e) => write!(f, "the given kernel command line was invalid: {}", e),
ConfigurePciDevice(e) => write!(f, "failed to configure hotplugged pci device: {}", e),
ConfigureSystem => write!(f, "error configuring the system"),
CreateBatDevices(e) => write!(f, "unable to create battery devices: {}", e),
CreateDevices(e) => write!(f, "error creating devices: {}", e),
@ -413,7 +415,7 @@ impl arch::LinuxArch for X8664arch {
)
.map_err(Error::CreatePciRoot)?;
let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci)));
io_bus.insert(pci_bus, 0xcf8, 0x8).unwrap();
io_bus.insert(pci_bus.clone(), 0xcf8, 0x8).unwrap();
// Event used to notify crosvm that guest OS is trying to suspend.
let suspend_evt = Event::new().map_err(Error::CreateEvent)?;
@ -553,6 +555,7 @@ impl arch::LinuxArch for X8664arch {
bat_control,
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
gdb: components.gdb,
root_config: pci_bus,
})
}
@ -592,6 +595,16 @@ impl arch::LinuxArch for X8664arch {
Ok(())
}
fn register_pci_device<V: VmX86_64, Vcpu: VcpuX86_64>(
linux: &mut RunnableLinuxVm<V, Vcpu>,
device: Box<dyn PciDevice>,
minijail: Option<Minijail>,
resources: &mut SystemAllocator,
) -> Result<()> {
arch::configure_pci_device(linux, device, minijail, resources)
.map_err(Error::ConfigurePciDevice)
}
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
fn debug_read_registers<T: VcpuX86_64>(vcpu: &T) -> Result<X86_64CoreRegs> {
// General registers: RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15