summary refs log tree commit diff
path: root/arch
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2018-07-23 17:58:09 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-09-10 17:17:35 -0700
commit059a188d0dd7162315c1e15b8e11d1db8bf3e832 (patch)
tree50cb7cf60e92dccf6109fdb860a47174b95fe07d /arch
parent7621d910f56ff85400b252f88fdef324a1cc13d6 (diff)
downloadcrosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.tar
crosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.tar.gz
crosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.tar.bz2
crosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.tar.lz
crosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.tar.xz
crosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.tar.zst
crosvm-059a188d0dd7162315c1e15b8e11d1db8bf3e832.zip
Arch: Big refactor and add an empty PCI bus
When setting up IO, accept an optional PciRoot device to put on the IO
bus.

For aarch64, it's currently ignored. For x86_64, it will be added at
0xcf8.

 break up mmio device creation and registration

Moving forward registration will be handled by the architecture specific
code. However, creation will be handled by the common code. To make that
easier split up the two steps so a list of devices is created, then each
is registered later.

Start moving to a model where the configuration generates a set of
components that are passed to the architecture. The architecture will
crate a VM from the components.

Break up the big run_config function and move architecture specific
parts to the various architectures.

This doesn't refactor the function calls each architecture makes, but
moves the setup flow in to the arch impls so that they can diverge in
the future.

Change-Id: I5b10d092896606796dc0c9afc5e34a1b288b867b
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1099860
Commit-Ready: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/Cargo.toml1
-rw-r--r--arch/src/lib.rs215
2 files changed, 129 insertions, 87 deletions
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
index 20c5466..4a22f17 100644
--- a/arch/Cargo.toml
+++ b/arch/Cargo.toml
@@ -5,6 +5,7 @@ authors = ["The Chromium OS Authors"]
 
 [dependencies]
 devices = { path = "../devices" }
+io_jail = { path = "../io_jail" }
 kvm = { path = "../kvm" }
 sys_util = { path = "../sys_util" }
 resources = { path = "../resources" }
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index e2d447c..d7a1a63 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+extern crate io_jail;
 extern crate sys_util;
 extern crate resources;
 extern crate kernel_cmdline;
@@ -9,114 +10,154 @@ extern crate kvm;
 extern crate libc;
 extern crate devices;
 
-use std::ffi::CStr;
+use std::fmt;
 use std::fs::File;
+use std::os::unix::io::{AsRawFd, RawFd};
 use std::result;
 use std::sync::{Arc, Mutex};
 
-use devices::Bus;
-use kvm::{Kvm, Vm, Vcpu};
-use sys_util::{EventFd, GuestMemory};
+use devices::{Bus, PciDeviceList, Serial};
+use devices::virtio::VirtioDevice;
+use io_jail::Minijail;
+use kvm::{IoeventAddress, Kvm, Vm, Vcpu};
+use sys_util::{EventFd, GuestMemory, syslog};
 use resources::SystemAllocator;
 
 pub type Result<T> = result::Result<T, Box<std::error::Error>>;
 
+/// Holds the pieces needed to build a VM. Passed to `build_vm` in the `LinuxArch` trait below to
+/// create a `RunnableLinuxVm`.
+pub struct VmComponents {
+    pub pci_devices: PciDeviceList,
+    pub memory_mb: u64,
+    pub vcpu_count: u32,
+    pub kernel_image: File,
+    pub extra_kernel_params: Vec<String>,
+    pub wayland_dmabuf: bool,
+}
+
+/// Holds the elements needed to run a Linux VM. Created by `build_vm`.
+pub struct RunnableLinuxVm {
+    pub vm: Vm,
+    pub kvm: Kvm,
+    pub resources: SystemAllocator,
+    pub stdio_serial: Arc<Mutex<Serial>>,
+    pub exit_evt: EventFd,
+    pub vcpus: Vec<Vcpu>,
+    pub irq_chip: Option<File>,
+    pub io_bus: Bus,
+    pub mmio_bus: Bus,
+}
+
+/// The device and optional jail.
+pub struct VirtioDeviceStub {
+    pub dev: Box<VirtioDevice>,
+    pub jail: Option<Minijail>,
+}
+
 /// Trait which is implemented for each Linux Architecture in order to
 /// set up the memory, cpus, and system devices and to boot the kernel.
 pub trait LinuxArch {
-    /// Loads the kernel from an open file.
+    /// Takes `VmComponents` and generates a `RunnableLinuxVm`.
     ///
     /// # Arguments
     ///
-    /// * `mem` - The memory to be used by the guest.
-    /// * `kernel_image` - the File object for the specified kernel.
-    fn load_kernel(mem: &GuestMemory, kernel_image: &mut File) -> Result<()>;
+    /// * `components` - Parts to use to build the VM.
+    /// * `virtio_devs` - Function to generate a list of virtio devices.
+    fn build_vm<F>(components: VmComponents, virtio_devs: F) -> Result<RunnableLinuxVm>
+        where
+            F: FnOnce(&GuestMemory, &EventFd) -> Result<Vec<VirtioDeviceStub>>;
+}
 
-    /// Configures the system memory space should be called once per vm before
-    /// starting vcpu threads.
-    ///
-    /// # Arguments
-    ///
-    /// * `mem` - The memory to be used by the guest
-    /// * `mem_size` - The size in bytes of system memory
-    /// * `vcpu_count` - Number of virtual CPUs the guest will have
-    /// * `cmdline` - the kernel commandline
-    /// * `pci_irqs` - Any PCI irqs that need to be configured (Interrupt Line, PCI pin).
-    fn setup_system_memory(mem: &GuestMemory,
-                           mem_size: u64,
-                           vcpu_count: u32,
-                           cmdline: &CStr,
-                           pci_irqs: Vec<(u32, devices::PciInterruptPin)>) -> Result<()>;
-
-    /// Creates a new VM object and initializes architecture specific devices
-    ///
-    /// # Arguments
-    ///
-    /// * `kvm` - The opened /dev/kvm object.
-    /// * `mem` - The memory to be used by the guest.
-    fn create_vm(kvm: &Kvm, mem: GuestMemory) -> Result<Vm>;
+/// Errors for device manager.
+#[derive(Debug)]
+pub enum MmioRegisterError {
+    /// Could not create the mmio device to wrap a VirtioDevice.
+    CreateMmioDevice(sys_util::Error),
+    /// Failed to register ioevent with VM.
+    RegisterIoevent(sys_util::Error),
+    /// Failed to register irq eventfd with VM.
+    RegisterIrqfd(sys_util::Error),
+    /// Failed to initialize proxy device for jailed device.
+    ProxyDeviceCreation(devices::ProxyError),
+    /// Appending to kernel command line failed.
+    Cmdline(kernel_cmdline::Error),
+    /// No more IRQs are available.
+    IrqsExhausted,
+    /// No more MMIO space available.
+    AddrsExhausted,
+}
 
-    /// This adds any early platform devices for this architecture.
-    ///
-    /// # Arguments
-    ///
-    /// * `vm` - The vm to add irqs to.
-    /// * `bus` - The bus to add devices to.
-    fn add_arch_devs(_vm: &mut Vm, _bus: &mut Bus) -> Result<()> { Ok(()) }
+impl fmt::Display for MmioRegisterError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &MmioRegisterError::CreateMmioDevice(ref e) => write!(f, "failed to create mmio device: {:?}", e),
+            &MmioRegisterError::Cmdline(ref e) => {
+                write!(f, "unable to add device to kernel command line: {}", e)
+            }
+            &MmioRegisterError::RegisterIoevent(ref e) => {
+                write!(f, "failed to register ioevent to VM: {:?}", e)
+            }
+            &MmioRegisterError::RegisterIrqfd(ref e) => {
+                write!(f, "failed to register irq eventfd to VM: {:?}", e)
+            }
+            &MmioRegisterError::ProxyDeviceCreation(ref e) => write!(f, "failed to create proxy device: {}", e),
+            &MmioRegisterError::IrqsExhausted => write!(f, "no more IRQs are available"),
+            &MmioRegisterError::AddrsExhausted => write!(f, "no more addresses are available"),
+        }
+    }
+}
 
-    /// This creates a GuestMemory object for this VM
-    ///
-    /// * `mem_size` - Desired physical memory size in bytes for this VM
-    fn setup_memory(mem_size: u64) -> Result<GuestMemory>;
+/// Register a device to be used via MMIO transport.
+pub fn register_mmio(bus: &mut devices::Bus,
+                     vm: &mut Vm,
+                     device: Box<devices::virtio::VirtioDevice>,
+                     jail: Option<Minijail>,
+                     resources: &mut SystemAllocator,
+                     cmdline: &mut kernel_cmdline::Cmdline)
+                     -> std::result::Result<(), MmioRegisterError> {
+    let irq = match resources.allocate_irq() {
+        None => return Err(MmioRegisterError::IrqsExhausted),
+        Some(i) => i,
+    };
 
-    /// The creates the interrupt controller device and optionally returns the fd for it.
-    /// Some architectures may not have a separate descriptor for the interrupt
-    /// controller, so they would return None even on success.
-    ///
-    /// # Arguments
-    ///
-    /// * `vm` - the vm object
-    fn create_irq_chip(vm: &kvm::Vm) -> Result<Option<File>>;
+    // List of FDs to keep open in the child after it forks.
+    let mut keep_fds: Vec<RawFd> = device.keep_fds();
+    syslog::push_fds(&mut keep_fds);
 
-    /// This returns the first page frame number for use by the balloon driver.
-    ///
-    /// # Arguments
-    ///
-    /// * `mem_size` - the size in bytes of physical ram for the guest
-    fn get_base_dev_pfn(mem_size: u64) -> u64;
+    let mmio_device = devices::virtio::MmioDevice::new((*vm.get_memory()).clone(),
+    device)
+        .map_err(MmioRegisterError::CreateMmioDevice)?;
+    let mmio_len = 0x1000; // TODO(dgreid) - configurable per arch?
+    let mmio_base = resources.allocate_mmio_addresses(mmio_len)
+        .ok_or(MmioRegisterError::AddrsExhausted)?;
+    for (i, queue_evt) in mmio_device.queue_evts().iter().enumerate() {
+        let io_addr = IoeventAddress::Mmio(mmio_base +
+                                           devices::virtio::NOTIFY_REG_OFFSET as u64);
+        vm.register_ioevent(&queue_evt, io_addr, i as u32)
+          .map_err(MmioRegisterError::RegisterIoevent)?;
+        keep_fds.push(queue_evt.as_raw_fd());
+    }
 
-    /// This returns a minimal kernel command for this architecture.
-    fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline;
+    if let Some(interrupt_evt) = mmio_device.interrupt_evt() {
+        vm.register_irqfd(&interrupt_evt, irq)
+          .map_err(MmioRegisterError::RegisterIrqfd)?;
+        keep_fds.push(interrupt_evt.as_raw_fd());
+    }
 
-    /// Returns a system resource allocator.
-    fn get_resource_allocator(mem_size: u64, gpu_allocation: bool) -> SystemAllocator;
+    if let Some(jail) = jail {
+        let proxy_dev = devices::ProxyDevice::new(mmio_device, &jail, keep_fds)
+            .map_err(MmioRegisterError::ProxyDeviceCreation)?;
 
-    /// Sets up the IO bus for this platform
-    ///
-    /// # Arguments
-    ///
-    /// * - `vm` the vm object
-    /// * - `exit_evt` - the event fd object which should receive exit events
-    fn setup_io_bus(vm: &mut Vm, exit_evt: EventFd)
-                    -> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)>;
+        bus.insert(Arc::new(Mutex::new(proxy_dev)), mmio_base, mmio_len, false).unwrap();
+    } else {
+        bus.insert(Arc::new(Mutex::new(mmio_device)), mmio_base, mmio_len, false).unwrap();
+    }
 
-    /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
-    ///
-    /// # Arguments
-    ///
-    /// * `guest_mem` - The memory to be used by the guest.
-    /// * `kernel_load_offset` - Offset in bytes from `guest_mem` at which the
-    ///                          kernel starts.
-    /// * `kvm` - The /dev/kvm object that created vcpu.
-    /// * `vm` - The VM object associated with this VCPU.
-    /// * `vcpu` - The VCPU object to configure.
-    /// * `cpu_id` - The id of the given `vcpu`.
-    /// * `num_cpus` - Number of virtual CPUs the guest will have.
-    fn configure_vcpu(guest_mem: &GuestMemory,
-                      kvm: &Kvm,
-                      vm: &Vm,
-                      vcpu: &Vcpu,
-                      cpu_id: u64,
-                      num_cpus: u64)
-                      -> Result<()>;
+    cmdline
+        .insert("virtio_mmio.device",
+                &format!("4K@0x{:08x}:{}", mmio_base, irq))
+        .map_err(MmioRegisterError::Cmdline)?;
+
+    Ok(())
 }