2018-02-14 06:09:43 +00:00
|
|
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2019-04-24 02:03:29 +00:00
|
|
|
pub mod android;
|
2018-12-22 00:01:56 +00:00
|
|
|
pub mod fdt;
|
2019-12-18 04:13:14 +00:00
|
|
|
pub mod pstore;
|
2020-02-15 00:46:36 +00:00
|
|
|
pub mod serial;
|
2018-12-22 00:01:56 +00:00
|
|
|
|
2019-01-24 03:04:43 +00:00
|
|
|
use std::collections::BTreeMap;
|
2019-03-05 01:48:36 +00:00
|
|
|
use std::error::Error as StdError;
|
2019-03-02 02:07:56 +00:00
|
|
|
use std::fmt::{self, Display};
|
2018-02-14 06:09:43 +00:00
|
|
|
use std::fs::File;
|
2019-03-05 01:48:36 +00:00
|
|
|
use std::io::{self, Read, Seek, SeekFrom};
|
2018-10-15 21:32:30 +00:00
|
|
|
use std::os::unix::io::AsRawFd;
|
2019-12-18 04:13:14 +00:00
|
|
|
use std::path::PathBuf;
|
2018-12-04 07:37:46 +00:00
|
|
|
use std::sync::Arc;
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2020-05-21 06:27:59 +00:00
|
|
|
use acpi_tables::sdt::SDT;
|
2020-09-16 22:29:20 +00:00
|
|
|
use base::{syslog, AsRawDescriptor, Event};
|
2018-07-24 00:58:09 +00:00
|
|
|
use devices::virtio::VirtioDevice;
|
2018-10-03 17:22:32 +00:00
|
|
|
use devices::{
|
2020-06-21 04:45:32 +00:00
|
|
|
Bus, BusDevice, BusError, IrqChip, PciAddress, PciDevice, PciDeviceError, PciInterruptPin,
|
|
|
|
PciRoot, ProxyDevice,
|
2018-10-03 17:22:32 +00:00
|
|
|
};
|
2020-06-21 04:45:32 +00:00
|
|
|
use hypervisor::{IoEventAddress, Vcpu, Vm};
|
2020-06-19 14:19:48 +00:00
|
|
|
use minijail::Minijail;
|
2018-05-18 01:47:11 +00:00
|
|
|
use resources::SystemAllocator;
|
2018-12-04 07:37:46 +00:00
|
|
|
use sync::Mutex;
|
2020-07-21 03:21:11 +00:00
|
|
|
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2020-06-21 04:45:32 +00:00
|
|
|
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
|
|
use {
|
|
|
|
devices::IrqChipAArch64 as IrqChipArch,
|
|
|
|
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,
|
|
|
|
hypervisor::{HypervisorX86_64 as HypervisorArch, VcpuX86_64 as VcpuArch, VmX86_64 as VmArch},
|
|
|
|
};
|
|
|
|
|
2020-02-15 00:46:36 +00:00
|
|
|
pub use serial::{
|
2020-03-09 20:16:46 +00:00
|
|
|
add_serial_devices, get_serial_cmdline, set_default_serial_parameters, GetSerialCmdlineError,
|
|
|
|
SerialHardware, SerialParameters, SerialType, SERIAL_ADDR,
|
2020-02-15 00:46:36 +00:00
|
|
|
};
|
|
|
|
|
2019-05-21 19:12:38 +00:00
|
|
|
pub enum VmImage {
|
|
|
|
Kernel(File),
|
|
|
|
Bios(File),
|
|
|
|
}
|
|
|
|
|
2019-12-18 04:13:14 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Pstore {
|
|
|
|
pub path: PathBuf,
|
|
|
|
pub size: u32,
|
|
|
|
}
|
|
|
|
|
2020-09-08 20:47:49 +00:00
|
|
|
/// Mapping of guest VCPU threads to host CPU cores.
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
pub enum VcpuAffinity {
|
|
|
|
/// All VCPU threads will be pinned to the same set of host CPU cores.
|
|
|
|
Global(Vec<usize>),
|
|
|
|
/// Each VCPU may be pinned to a set of host CPU cores.
|
|
|
|
/// The map key is a guest VCPU index, and the corresponding value is the set of
|
|
|
|
/// host CPU indices that the VCPU thread will be allowed to run on.
|
|
|
|
/// If a VCPU index is not present in the map, its affinity will not be set.
|
|
|
|
PerVcpu(BTreeMap<usize, Vec<usize>>),
|
|
|
|
}
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Holds the pieces needed to build a VM. Passed to `build_vm` in the `LinuxArch` trait below to
|
|
|
|
/// create a `RunnableLinuxVm`.
|
|
|
|
pub struct VmComponents {
|
2019-04-26 18:22:51 +00:00
|
|
|
pub memory_size: u64,
|
2020-06-21 04:45:32 +00:00
|
|
|
pub vcpu_count: usize,
|
2020-09-08 20:47:49 +00:00
|
|
|
pub vcpu_affinity: Option<VcpuAffinity>,
|
2019-05-21 19:12:38 +00:00
|
|
|
pub vm_image: VmImage,
|
2018-12-22 00:01:56 +00:00
|
|
|
pub android_fstab: Option<File>,
|
2019-12-18 04:13:14 +00:00
|
|
|
pub pstore: Option<Pstore>,
|
2018-12-12 00:29:26 +00:00
|
|
|
pub initrd_image: Option<File>,
|
2018-07-24 00:58:09 +00:00
|
|
|
pub extra_kernel_params: Vec<String>,
|
|
|
|
pub wayland_dmabuf: bool,
|
2020-05-21 06:27:59 +00:00
|
|
|
pub acpi_sdts: Vec<SDT>,
|
2020-08-13 09:17:50 +00:00
|
|
|
pub rt_cpus: Vec<usize>,
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Holds the elements needed to run a Linux VM. Created by `build_vm`.
|
2020-06-21 04:45:32 +00:00
|
|
|
pub struct RunnableLinuxVm<V: VmArch, I: IrqChipArch<V::Vcpu>> {
|
|
|
|
pub vm: V,
|
2018-07-24 00:58:09 +00:00
|
|
|
pub resources: SystemAllocator,
|
2020-09-16 22:29:20 +00:00
|
|
|
pub exit_evt: Event,
|
2020-06-21 04:45:32 +00:00
|
|
|
pub vcpu_count: usize,
|
|
|
|
/// If vcpus is None, then it's the responsibility of the vcpu thread to create vcpus.
|
|
|
|
/// If it's Some, then `build_vm` already created the vcpus.
|
|
|
|
pub vcpus: Option<Vec<V::Vcpu>>,
|
2020-09-08 20:47:49 +00:00
|
|
|
pub vcpu_affinity: Option<VcpuAffinity>,
|
2020-06-21 04:45:32 +00:00
|
|
|
pub irq_chip: I,
|
|
|
|
pub has_bios: bool,
|
2018-07-24 00:58:09 +00:00
|
|
|
pub io_bus: Bus,
|
|
|
|
pub mmio_bus: Bus,
|
2019-01-24 03:04:43 +00:00
|
|
|
pub pid_debug_label_map: BTreeMap<u32, String>,
|
2020-09-16 22:29:20 +00:00
|
|
|
pub suspend_evt: Event,
|
2020-08-13 09:17:50 +00:00
|
|
|
pub rt_cpus: Vec<usize>,
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The device and optional jail.
|
|
|
|
pub struct VirtioDeviceStub {
|
2019-03-09 00:56:14 +00:00
|
|
|
pub dev: Box<dyn VirtioDevice>,
|
2018-07-24 00:58:09 +00:00
|
|
|
pub jail: Option<Minijail>,
|
|
|
|
}
|
|
|
|
|
2018-02-14 06:09:43 +00:00
|
|
|
/// 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 {
|
2019-03-05 01:48:36 +00:00
|
|
|
type Error: StdError;
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Takes `VmComponents` and generates a `RunnableLinuxVm`.
|
2018-02-14 06:09:43 +00:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2018-07-24 00:58:09 +00:00
|
|
|
/// * `components` - Parts to use to build the VM.
|
2019-04-17 19:51:25 +00:00
|
|
|
/// * `serial_parameters` - definitions for how the serial devices should be configured.
|
2019-02-20 21:50:42 +00:00
|
|
|
/// * `create_devices` - Function to generate a list of devices.
|
2020-06-21 04:45:32 +00:00
|
|
|
/// * `create_vm` - Function to generate a VM.
|
|
|
|
/// * `create_irq_chip` - Function to generate an IRQ chip.
|
|
|
|
fn build_vm<V, I, FD, FV, FI, E1, E2, E3>(
|
2019-01-30 05:21:48 +00:00
|
|
|
components: VmComponents,
|
2020-03-09 20:16:46 +00:00
|
|
|
serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
|
2019-08-13 18:20:14 +00:00
|
|
|
serial_jail: Option<Minijail>,
|
2020-06-21 04:45:32 +00:00
|
|
|
create_devices: FD,
|
|
|
|
create_vm: FV,
|
|
|
|
create_irq_chip: FI,
|
|
|
|
) -> std::result::Result<RunnableLinuxVm<V, I>, Self::Error>
|
2018-10-03 17:22:32 +00:00
|
|
|
where
|
2020-06-21 04:45:32 +00:00
|
|
|
V: VmArch,
|
|
|
|
I: IrqChipArch<V::Vcpu>,
|
|
|
|
FD: FnOnce(
|
2019-04-24 17:55:25 +00:00
|
|
|
&GuestMemory,
|
2020-06-21 04:45:32 +00:00
|
|
|
&mut V,
|
2019-04-24 17:55:25 +00:00
|
|
|
&mut SystemAllocator,
|
2020-09-16 22:29:20 +00:00
|
|
|
&Event,
|
2020-06-21 04:45:32 +00:00
|
|
|
) -> std::result::Result<Vec<(Box<dyn PciDevice>, Option<Minijail>)>, E1>,
|
|
|
|
FV: FnOnce(GuestMemory) -> std::result::Result<V, E2>,
|
|
|
|
FI: FnOnce(&V, /* vcpu_count: */ usize) -> std::result::Result<I, E3>,
|
|
|
|
E1: StdError + 'static,
|
|
|
|
E2: StdError + 'static,
|
|
|
|
E3: StdError + 'static;
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
/// * `hypervisor` - The `Hypervisor` that created the vcpu.
|
|
|
|
/// * `irq_chip` - The `IrqChip` associated with this vm.
|
|
|
|
/// * `vcpu` - The VCPU object to configure.
|
|
|
|
/// * `vcpu_id` - The id of the given `vcpu`.
|
|
|
|
/// * `num_cpus` - Number of virtual CPUs the guest will have.
|
|
|
|
/// * `has_bios` - Whether the `VmImage` is a `Bios` image
|
|
|
|
fn configure_vcpu<T: VcpuArch>(
|
|
|
|
guest_mem: &GuestMemory,
|
|
|
|
hypervisor: &impl HypervisorArch,
|
|
|
|
irq_chip: &mut impl IrqChipArch<T>,
|
|
|
|
vcpu: &mut impl VcpuArch,
|
|
|
|
vcpu_id: usize,
|
|
|
|
num_cpus: usize,
|
|
|
|
has_bios: bool,
|
|
|
|
) -> Result<(), Self::Error>;
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Errors for device manager.
|
|
|
|
#[derive(Debug)]
|
2018-07-09 22:39:34 +00:00
|
|
|
pub enum DeviceRegistrationError {
|
|
|
|
/// Could not allocate IO space for the device.
|
|
|
|
AllocateIoAddrs(PciDeviceError),
|
2019-02-21 02:56:22 +00:00
|
|
|
/// Could not allocate device address space for the device.
|
|
|
|
AllocateDeviceAddrs(PciDeviceError),
|
2018-07-09 22:39:34 +00:00
|
|
|
/// Could not allocate an IRQ number.
|
|
|
|
AllocateIrq,
|
2019-08-13 18:20:14 +00:00
|
|
|
// Unable to create a pipe.
|
2020-08-03 03:09:41 +00:00
|
|
|
CreatePipe(base::Error),
|
2019-08-13 18:20:14 +00:00
|
|
|
// Unable to create serial device from serial parameters
|
2020-02-15 00:46:36 +00:00
|
|
|
CreateSerialDevice(serial::Error),
|
2020-09-16 22:29:20 +00:00
|
|
|
/// Could not clone an event.
|
|
|
|
EventClone(base::Error),
|
|
|
|
/// Could not create an event.
|
|
|
|
EventCreate(base::Error),
|
2020-03-09 20:16:46 +00:00
|
|
|
/// Missing a required serial device.
|
|
|
|
MissingRequiredSerialDevice(u8),
|
2018-07-09 22:39:34 +00:00
|
|
|
/// Could not add a device to the mmio bus.
|
|
|
|
MmioInsert(BusError),
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Failed to register ioevent with VM.
|
2020-08-03 03:09:41 +00:00
|
|
|
RegisterIoevent(base::Error),
|
2020-09-16 22:29:20 +00:00
|
|
|
/// Failed to register irq event with VM.
|
2020-08-03 03:09:41 +00:00
|
|
|
RegisterIrqfd(base::Error),
|
2018-07-24 00:58:09 +00:00
|
|
|
/// 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,
|
2019-03-15 20:13:08 +00:00
|
|
|
/// Could not register PCI device capabilities.
|
|
|
|
RegisterDeviceCapabilities(PciDeviceError),
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2019-03-02 02:07:56 +00:00
|
|
|
impl Display for DeviceRegistrationError {
|
2018-07-24 00:58:09 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2019-03-02 02:07:56 +00:00
|
|
|
use self::DeviceRegistrationError::*;
|
|
|
|
|
2018-07-24 00:58:09 +00:00
|
|
|
match self {
|
2019-03-02 02:07:56 +00:00
|
|
|
AllocateIoAddrs(e) => write!(f, "Allocating IO addresses: {}", e),
|
|
|
|
AllocateDeviceAddrs(e) => write!(f, "Allocating device addresses: {}", e),
|
|
|
|
AllocateIrq => write!(f, "Allocating IRQ number"),
|
2019-08-13 18:20:14 +00:00
|
|
|
CreatePipe(e) => write!(f, "failed to create pipe: {}", e),
|
2019-04-17 19:51:25 +00:00
|
|
|
CreateSerialDevice(e) => write!(f, "failed to create serial device: {}", e),
|
2019-03-02 02:07:56 +00:00
|
|
|
Cmdline(e) => write!(f, "unable to add device to kernel command line: {}", e),
|
2020-09-16 22:29:20 +00:00
|
|
|
EventClone(e) => write!(f, "failed to clone event: {}", e),
|
|
|
|
EventCreate(e) => write!(f, "failed to create event: {}", e),
|
2020-03-09 20:16:46 +00:00
|
|
|
MissingRequiredSerialDevice(n) => write!(f, "missing required serial device {}", n),
|
2019-03-02 02:07:56 +00:00
|
|
|
MmioInsert(e) => write!(f, "failed to add to mmio bus: {}", e),
|
|
|
|
RegisterIoevent(e) => write!(f, "failed to register ioevent to VM: {}", e),
|
2020-09-16 22:29:20 +00:00
|
|
|
RegisterIrqfd(e) => write!(f, "failed to register irq event to VM: {}", e),
|
2019-03-02 02:07:56 +00:00
|
|
|
ProxyDeviceCreation(e) => write!(f, "failed to create proxy device: {}", e),
|
|
|
|
IrqsExhausted => write!(f, "no more IRQs are available"),
|
|
|
|
AddrsExhausted => write!(f, "no more addresses are available"),
|
2019-03-15 20:13:08 +00:00
|
|
|
RegisterDeviceCapabilities(e) => {
|
|
|
|
write!(f, "could not register PCI device capabilities: {}", e)
|
|
|
|
}
|
2018-07-09 22:39:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a root PCI device for use by this Vm.
|
2020-06-21 04:45:32 +00:00
|
|
|
pub fn generate_pci_root<T: Vcpu>(
|
2019-03-09 00:56:14 +00:00
|
|
|
devices: Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
|
2020-06-21 04:45:32 +00:00
|
|
|
irq_chip: &mut impl IrqChip<T>,
|
2018-10-03 17:22:32 +00:00
|
|
|
mmio_bus: &mut Bus,
|
|
|
|
resources: &mut SystemAllocator,
|
2020-06-21 04:45:32 +00:00
|
|
|
vm: &mut impl Vm,
|
2020-07-24 21:08:51 +00:00
|
|
|
max_irqs: usize,
|
2020-05-01 16:54:59 +00:00
|
|
|
) -> Result<
|
|
|
|
(
|
|
|
|
PciRoot,
|
|
|
|
Vec<(PciAddress, u32, PciInterruptPin)>,
|
|
|
|
BTreeMap<u32, String>,
|
|
|
|
),
|
|
|
|
DeviceRegistrationError,
|
|
|
|
> {
|
2018-07-09 22:39:34 +00:00
|
|
|
let mut root = PciRoot::new();
|
|
|
|
let mut pci_irqs = Vec::new();
|
2019-01-24 03:04:43 +00:00
|
|
|
let mut pid_labels = BTreeMap::new();
|
2020-07-24 21:08:51 +00:00
|
|
|
|
|
|
|
let mut irqs: Vec<Option<u32>> = vec![None; max_irqs];
|
|
|
|
|
2018-07-09 22:39:34 +00:00
|
|
|
for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
|
2020-04-29 19:58:11 +00:00
|
|
|
// Auto assign PCI device numbers starting from 1
|
|
|
|
let address = PciAddress {
|
|
|
|
bus: 0,
|
|
|
|
dev: 1 + dev_idx as u8,
|
|
|
|
func: 0,
|
|
|
|
};
|
|
|
|
device.assign_address(address);
|
resources+pci: allocator rework (allocation tags)
AddressAllocator now maintains a HashMap<Alloc, (u64, u64, u64)>,
which uniquely maps a Allocation enum (e.g: PciBar(bus, dev, bar),
GpuRenderNode, etc...) to it's address, size, and human-readable tag
/ description.
The interface has also been modified to use Error instead of Option.
Aside from improving debugging, tracking allocations will have
numerous uses in the future. For example, when allocating guest memory
over VmControl sockets, it will be possible to restrict allocations to
pre-allocated slices of memory owned by the requesting device.
To plumb through PCI information to PCI devices, this CL necessitated
the addition of a PciDevice method called `assign_bus_dev`, which
notifies PCI devices of their uniquely assigned Bus and Device numbers.
BUG=chromium:936567
TEST=cargo test -p resources && cargo build --features="gpu gpu-forward"
Change-Id: I8b4b0e32c6f3168138739249ede53d03143ee5c3
Reviewed-on: https://chromium-review.googlesource.com/1536207
Commit-Ready: Daniel Prilik <prilik@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
2019-03-26 21:28:19 +00:00
|
|
|
|
2018-09-20 17:59:06 +00:00
|
|
|
let mut keep_fds = device.keep_fds();
|
2018-07-09 22:39:34 +00:00
|
|
|
syslog::push_fds(&mut keep_fds);
|
|
|
|
|
2020-09-16 22:29:20 +00:00
|
|
|
let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
let irq_resample_fd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
2020-07-24 21:08:51 +00:00
|
|
|
let irq_num = if let Some(irq) = irqs[dev_idx % max_irqs] {
|
|
|
|
irq
|
|
|
|
} else {
|
|
|
|
let irq = resources
|
|
|
|
.allocate_irq()
|
|
|
|
.ok_or(DeviceRegistrationError::AllocateIrq)?;
|
|
|
|
irqs[dev_idx % max_irqs] = Some(irq);
|
|
|
|
irq
|
|
|
|
};
|
2018-07-09 22:39:34 +00:00
|
|
|
let pci_irq_pin = match dev_idx % 4 {
|
|
|
|
0 => PciInterruptPin::IntA,
|
|
|
|
1 => PciInterruptPin::IntB,
|
|
|
|
2 => PciInterruptPin::IntC,
|
|
|
|
3 => PciInterruptPin::IntD,
|
2019-12-02 07:50:28 +00:00
|
|
|
_ => unreachable!(), // Obviously not possible, but the compiler is not smart enough.
|
2018-07-09 22:39:34 +00:00
|
|
|
};
|
2020-06-21 04:45:32 +00:00
|
|
|
|
|
|
|
irq_chip
|
|
|
|
.register_irq_event(irq_num, &irqfd, Some(&irq_resample_fd))
|
|
|
|
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
|
|
|
|
|
2018-09-17 21:42:59 +00:00
|
|
|
keep_fds.push(irqfd.as_raw_fd());
|
2018-10-25 00:06:07 +00:00
|
|
|
keep_fds.push(irq_resample_fd.as_raw_fd());
|
|
|
|
device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin);
|
2020-05-01 16:54:59 +00:00
|
|
|
pci_irqs.push((address, irq_num, pci_irq_pin));
|
2018-07-09 22:39:34 +00:00
|
|
|
|
|
|
|
let ranges = device
|
|
|
|
.allocate_io_bars(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateIoAddrs)?;
|
2019-02-21 02:56:22 +00:00
|
|
|
let device_ranges = device
|
|
|
|
.allocate_device_bars(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
|
2019-03-15 20:13:08 +00:00
|
|
|
device
|
|
|
|
.register_device_capabilities()
|
|
|
|
.map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
|
2020-09-16 22:29:20 +00:00
|
|
|
for (event, addr, datamatch) in device.ioevents() {
|
2020-06-21 04:45:32 +00:00
|
|
|
let io_addr = IoEventAddress::Mmio(addr);
|
2018-10-05 21:51:22 +00:00
|
|
|
vm.register_ioevent(&event, io_addr, datamatch)
|
2018-07-09 20:35:40 +00:00
|
|
|
.map_err(DeviceRegistrationError::RegisterIoevent)?;
|
|
|
|
keep_fds.push(event.as_raw_fd());
|
|
|
|
}
|
2019-03-09 00:56:14 +00:00
|
|
|
let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
|
2018-10-18 23:45:13 +00:00
|
|
|
let proxy = ProxyDevice::new(device, &jail, keep_fds)
|
|
|
|
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
|
2019-01-24 03:04:43 +00:00
|
|
|
pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
|
2018-10-18 23:45:13 +00:00
|
|
|
Arc::new(Mutex::new(proxy))
|
|
|
|
} else {
|
2019-03-09 04:41:57 +00:00
|
|
|
device.on_sandboxed();
|
2018-10-18 23:45:13 +00:00
|
|
|
Arc::new(Mutex::new(device))
|
|
|
|
};
|
2020-04-29 19:58:11 +00:00
|
|
|
root.add_device(address, arced_dev.clone());
|
2018-07-09 22:39:34 +00:00
|
|
|
for range in &ranges {
|
2018-10-03 17:22:32 +00:00
|
|
|
mmio_bus
|
|
|
|
.insert(arced_dev.clone(), range.0, range.1, true)
|
2018-07-09 22:39:34 +00:00
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
2019-02-21 02:56:22 +00:00
|
|
|
|
|
|
|
for range in &device_ranges {
|
|
|
|
mmio_bus
|
|
|
|
.insert(arced_dev.clone(), range.0, range.1, true)
|
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
|
|
|
}
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
2019-01-24 03:04:43 +00:00
|
|
|
Ok((root, pci_irqs, pid_labels))
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
2018-12-12 23:20:30 +00:00
|
|
|
|
|
|
|
/// Errors for image loading.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum LoadImageError {
|
2019-05-17 17:55:45 +00:00
|
|
|
BadAlignment(u64),
|
2019-03-05 01:48:36 +00:00
|
|
|
Seek(io::Error),
|
2018-12-12 23:20:30 +00:00
|
|
|
ImageSizeTooLarge(u64),
|
|
|
|
ReadToMemory(GuestMemoryError),
|
|
|
|
}
|
|
|
|
|
2019-03-02 02:07:56 +00:00
|
|
|
impl Display for LoadImageError {
|
2018-12-12 23:20:30 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2019-02-13 01:51:26 +00:00
|
|
|
use self::LoadImageError::*;
|
|
|
|
|
2018-12-12 23:20:30 +00:00
|
|
|
match self {
|
2019-05-17 17:55:45 +00:00
|
|
|
BadAlignment(a) => write!(f, "Alignment not a power of two: {}", a),
|
2019-02-13 01:51:26 +00:00
|
|
|
Seek(e) => write!(f, "Seek failed: {}", e),
|
|
|
|
ImageSizeTooLarge(size) => write!(f, "Image size too large: {}", size),
|
|
|
|
ReadToMemory(e) => write!(f, "Reading image into memory failed: {}", e),
|
2018-12-12 23:20:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Load an image from a file into guest memory.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `guest_mem` - The memory to be used by the guest.
|
|
|
|
/// * `guest_addr` - The starting address to load the image in the guest memory.
|
|
|
|
/// * `max_size` - The amount of space in bytes available in the guest memory for the image.
|
|
|
|
/// * `image` - The file containing the image to be loaded.
|
|
|
|
///
|
|
|
|
/// The size in bytes of the loaded image is returned.
|
|
|
|
pub fn load_image<F>(
|
|
|
|
guest_mem: &GuestMemory,
|
|
|
|
image: &mut F,
|
|
|
|
guest_addr: GuestAddress,
|
|
|
|
max_size: u64,
|
2019-03-05 01:48:36 +00:00
|
|
|
) -> Result<usize, LoadImageError>
|
2018-12-12 23:20:30 +00:00
|
|
|
where
|
2020-08-06 01:50:47 +00:00
|
|
|
F: Read + Seek + AsRawDescriptor,
|
2018-12-12 23:20:30 +00:00
|
|
|
{
|
|
|
|
let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?;
|
|
|
|
|
|
|
|
if size > usize::max_value() as u64 || size > max_size {
|
|
|
|
return Err(LoadImageError::ImageSizeTooLarge(size));
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is safe due to the bounds check above.
|
|
|
|
let size = size as usize;
|
|
|
|
|
|
|
|
image
|
|
|
|
.seek(SeekFrom::Start(0))
|
|
|
|
.map_err(LoadImageError::Seek)?;
|
|
|
|
|
|
|
|
guest_mem
|
|
|
|
.read_to_memory(guest_addr, image, size)
|
|
|
|
.map_err(LoadImageError::ReadToMemory)?;
|
|
|
|
|
|
|
|
Ok(size)
|
|
|
|
}
|
2019-05-17 17:55:45 +00:00
|
|
|
|
|
|
|
/// Load an image from a file into guest memory at the highest possible address.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `guest_mem` - The memory to be used by the guest.
|
|
|
|
/// * `image` - The file containing the image to be loaded.
|
|
|
|
/// * `min_guest_addr` - The minimum address of the start of the image.
|
|
|
|
/// * `max_guest_addr` - The address to load the last byte of the image.
|
|
|
|
/// * `align` - The minimum alignment of the start address of the image in bytes
|
|
|
|
/// (must be a power of two).
|
|
|
|
///
|
|
|
|
/// The guest address and size in bytes of the loaded image are returned.
|
|
|
|
pub fn load_image_high<F>(
|
|
|
|
guest_mem: &GuestMemory,
|
|
|
|
image: &mut F,
|
|
|
|
min_guest_addr: GuestAddress,
|
|
|
|
max_guest_addr: GuestAddress,
|
|
|
|
align: u64,
|
|
|
|
) -> Result<(GuestAddress, usize), LoadImageError>
|
|
|
|
where
|
2020-08-06 01:50:47 +00:00
|
|
|
F: Read + Seek + AsRawDescriptor,
|
2019-05-17 17:55:45 +00:00
|
|
|
{
|
|
|
|
if !align.is_power_of_two() {
|
|
|
|
return Err(LoadImageError::BadAlignment(align));
|
|
|
|
}
|
|
|
|
|
|
|
|
let max_size = max_guest_addr.offset_from(min_guest_addr) & !(align - 1);
|
|
|
|
let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?;
|
|
|
|
|
|
|
|
if size > usize::max_value() as u64 || size > max_size {
|
|
|
|
return Err(LoadImageError::ImageSizeTooLarge(size));
|
|
|
|
}
|
|
|
|
|
|
|
|
image
|
|
|
|
.seek(SeekFrom::Start(0))
|
|
|
|
.map_err(LoadImageError::Seek)?;
|
|
|
|
|
|
|
|
// Load image at the maximum aligned address allowed.
|
|
|
|
// The subtraction cannot underflow because of the size checks above.
|
|
|
|
let guest_addr = GuestAddress((max_guest_addr.offset() - size) & !(align - 1));
|
|
|
|
|
|
|
|
// This is safe due to the bounds check above.
|
|
|
|
let size = size as usize;
|
|
|
|
|
|
|
|
guest_mem
|
|
|
|
.read_to_memory(guest_addr, image, size)
|
|
|
|
.map_err(LoadImageError::ReadToMemory)?;
|
|
|
|
|
|
|
|
Ok((guest_addr, size))
|
|
|
|
}
|