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;
|
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};
|
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-04-27 08:35:33 +00:00
|
|
|
use acpi_tables::aml::Aml;
|
2020-05-21 06:27:59 +00:00
|
|
|
use acpi_tables::sdt::SDT;
|
2021-07-13 08:57:57 +00:00
|
|
|
use base::{syslog, AsRawDescriptor, AsRawDescriptors, Event, Tube};
|
2018-07-24 00:58:09 +00:00
|
|
|
use devices::virtio::VirtioDevice;
|
2018-10-03 17:22:32 +00:00
|
|
|
use devices::{
|
2021-09-22 05:50:46 +00:00
|
|
|
Bus, BusDevice, BusDeviceObj, BusError, BusResumeDevice, HotPlugBus, IrqChip, PciAddress,
|
|
|
|
PciDevice, PciDeviceError, PciInterruptPin, PciRoot, ProtectionType, ProxyDevice,
|
2021-09-17 12:30:52 +00:00
|
|
|
SerialHardware, SerialParameters, VfioPlatformDevice,
|
2018-10-03 17:22:32 +00:00
|
|
|
};
|
2020-09-29 23:00:24 +00:00
|
|
|
use hypervisor::{IoEventAddress, Vm};
|
2020-06-19 14:19:48 +00:00
|
|
|
use minijail::Minijail;
|
2021-08-18 21:17:06 +00:00
|
|
|
use remain::sorted;
|
2020-04-27 08:35:33 +00:00
|
|
|
use resources::{MmioType, SystemAllocator};
|
2018-12-04 07:37:46 +00:00
|
|
|
use sync::Mutex;
|
2021-08-18 21:17:06 +00:00
|
|
|
use thiserror::Error;
|
2021-01-07 16:30:28 +00:00
|
|
|
use vm_control::{BatControl, BatteryType};
|
2020-07-21 03:21:11 +00:00
|
|
|
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
|
2018-02-14 06:09:43 +00:00
|
|
|
|
2020-10-21 06:57:33 +00:00
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
2021-08-04 18:44:49 +00:00
|
|
|
use gdbstub_arch::x86::reg::X86_64CoreRegs as GdbStubRegs;
|
2020-10-21 06:57:33 +00:00
|
|
|
|
2020-06-21 04:45:32 +00:00
|
|
|
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
|
|
|
use {
|
2021-04-12 07:07:17 +00:00
|
|
|
devices::{IrqChipAArch64 as IrqChipArch, PciConfigMmio as RootConfigArch},
|
2020-06-21 04:45:32 +00:00
|
|
|
hypervisor::{Hypervisor as HypervisorArch, VcpuAArch64 as VcpuArch, VmAArch64 as VmArch},
|
|
|
|
};
|
|
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
|
|
use {
|
2021-04-12 07:07:17 +00:00
|
|
|
devices::{IrqChipX86_64 as IrqChipArch, PciConfigIo as RootConfigArch},
|
2020-06-21 04:45:32 +00:00
|
|
|
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,
|
2021-08-16 07:42:27 +00:00
|
|
|
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,
|
2021-07-30 18:03:06 +00:00
|
|
|
pub swiotlb: Option<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>,
|
main: add --cpu-cluster and --cpu-capacity options
The --cpu-cluster option can be used to report a physical grouping of
CPUs in the device tree as a "cpu-map" node:
https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/topology.txt
Each instance of the --cpu-cluster option specifies a new group
consisting of a list of CPUs, and it may be repeated to add multiple
groups.
The --cpu-capacity option can be used to specify the relative
performance of CPUs so that the guest kernel's scheduler can make better
decisions on systems with heterogeneous cores (e.g. big.LITTLE).
--cpu-capacity units are left up to each architecture; for devicetree
systems, capacity is used to fill the cpu capacity-dmips-mhz (Dhrystone
benchmark MIPS per MHz) field in each cpu node:
https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/cpu-capacity.txt
For example, on a trogdor board, there are 6 little cores (0-5) with a
capacity of 452, and 2 big cores (6-7) with a capacity of 1024, which
results in a crosvm command line argument of:
--cpu-capacity 0-452,1=452,2=452,3=452,4=452,5=452,6=1024,7=1024
Currently, these options only have an effect on devicetree platforms and
are ignored elsewhere; they may be expanded in the future to fill in the
equivalent ACPI tables if systems with heterogeneous CPUs are used
there.
BUG=b:182198842
TEST=Start crosvm on kevin with --cpu-cluster options
TEST=crosvm run --cpu-cluster 0,1,2,3 --cpu-cluster 4,5
Change-Id: I59c466549ccd908f8eea1da0651d82716bc82972
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2762298
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
2021-03-16 00:55:52 +00:00
|
|
|
pub cpu_clusters: Vec<Vec<usize>>,
|
|
|
|
pub cpu_capacity: BTreeMap<usize, u32>,
|
2020-10-07 05:15:41 +00:00
|
|
|
pub no_smt: bool,
|
2021-04-13 11:23:51 +00:00
|
|
|
pub hugepages: bool,
|
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>,
|
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>,
|
2021-02-18 02:53:11 +00:00
|
|
|
pub delay_rt: bool,
|
2021-01-08 13:29:03 +00:00
|
|
|
pub protected_vm: ProtectionType,
|
2020-10-21 06:57:33 +00:00
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
2021-01-07 16:30:28 +00:00
|
|
|
pub gdb: Option<(u32, Tube)>, // port and control tube.
|
2021-03-31 05:44:11 +00:00
|
|
|
pub dmi_path: Option<PathBuf>,
|
2021-04-12 18:00:24 +00:00
|
|
|
pub no_legacy: bool,
|
2021-10-10 10:22:29 +00:00
|
|
|
pub host_cpu_topology: bool,
|
2018-07-24 00:58:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Holds the elements needed to run a Linux VM. Created by `build_vm`.
|
2021-04-14 20:59:30 +00:00
|
|
|
pub struct RunnableLinuxVm<V: VmArch, Vcpu: VcpuArch> {
|
2020-06-21 04:45:32 +00:00
|
|
|
pub vm: V,
|
|
|
|
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.
|
2020-09-29 23:00:24 +00:00
|
|
|
pub vcpus: Option<Vec<Vcpu>>,
|
2020-09-08 20:47:49 +00:00
|
|
|
pub vcpu_affinity: Option<VcpuAffinity>,
|
2020-10-07 05:15:41 +00:00
|
|
|
pub no_smt: bool,
|
2021-04-14 20:59:30 +00:00
|
|
|
pub irq_chip: Box<dyn IrqChipArch>,
|
2020-06-21 04:45:32 +00:00
|
|
|
pub has_bios: bool,
|
2021-08-12 08:04:48 +00:00
|
|
|
pub io_bus: Arc<Bus>,
|
|
|
|
pub mmio_bus: Arc<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>,
|
2021-02-18 02:53:11 +00:00
|
|
|
pub delay_rt: bool,
|
2020-04-27 08:39:33 +00:00
|
|
|
pub bat_control: Option<BatControl>,
|
2020-10-21 06:57:33 +00:00
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
2021-01-07 16:30:28 +00:00
|
|
|
pub gdb: Option<(u32, Tube)>,
|
2021-07-21 20:49:02 +00:00
|
|
|
/// Devices to be notified before the system resumes from the S3 suspended state.
|
|
|
|
pub resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
|
2021-04-12 07:07:17 +00:00
|
|
|
pub root_config: Arc<Mutex<RootConfigArch>>,
|
2021-05-21 08:54:12 +00:00
|
|
|
pub hotplug_bus: Vec<Arc<Mutex<dyn HotPlugBus>>>,
|
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;
|
|
|
|
|
2021-03-31 19:56:08 +00:00
|
|
|
/// Returns a Vec of the valid memory addresses as pairs of address and length. These should be
|
|
|
|
/// used to configure the `GuestMemory` structure for the platform.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `components` - Parts used to determine the memory layout.
|
|
|
|
fn guest_memory_layout(
|
|
|
|
components: &VmComponents,
|
|
|
|
) -> std::result::Result<Vec<(GuestAddress, u64)>, Self::Error>;
|
|
|
|
|
2021-04-14 20:59:30 +00:00
|
|
|
/// Creates a new `SystemAllocator` that fits the given `GuestMemory`'s layout.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `guest_mem` - The memory to be used as a template for the `SystemAllocator`.
|
|
|
|
fn create_system_allocator(guest_mem: &GuestMemory) -> SystemAllocator;
|
|
|
|
|
2021-03-03 23:02:02 +00:00
|
|
|
fn get_phys_max_addr() -> u64;
|
|
|
|
|
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.
|
2021-12-03 19:19:13 +00:00
|
|
|
/// * `exit_evt` - Event used by sub-devices to request that crosvm exit because guest
|
|
|
|
/// wants to stop/shut down.
|
|
|
|
/// * `reset_evt` - Event used by sub-devices to request that crosvm exit because guest
|
|
|
|
/// requested reset.
|
2021-04-14 20:59:30 +00:00
|
|
|
/// * `system_allocator` - Allocator created by this trait's implementation of
|
|
|
|
/// `create_system_allocator`.
|
|
|
|
/// * `serial_parameters` - Definitions for how the serial devices should be configured.
|
|
|
|
/// * `serial_jail` - Jail used for serial devices created here.
|
|
|
|
/// * `battery` - Defines what battery device will be created.
|
|
|
|
/// * `vm` - A VM implementation to build upon.
|
2021-08-18 10:07:29 +00:00
|
|
|
/// * `ramoops_region` - Region allocated for ramoops.
|
2021-09-22 05:50:46 +00:00
|
|
|
/// * `devices` - The devices to be built into the VM.
|
2021-04-14 20:59:30 +00:00
|
|
|
/// * `irq_chip` - The IRQ chip implemention for the VM.
|
|
|
|
fn build_vm<V, Vcpu>(
|
2019-01-30 05:21:48 +00:00
|
|
|
components: VmComponents,
|
2021-04-14 20:59:30 +00:00
|
|
|
exit_evt: &Event,
|
2021-12-03 19:19:13 +00:00
|
|
|
reset_evt: &Event,
|
2021-04-14 20:59:30 +00:00
|
|
|
system_allocator: &mut SystemAllocator,
|
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-04-27 08:37:10 +00:00
|
|
|
battery: (&Option<BatteryType>, Option<Minijail>),
|
2021-03-31 19:56:08 +00:00
|
|
|
vm: V,
|
2021-08-18 10:07:29 +00:00
|
|
|
ramoops_region: Option<pstore::RamoopsRegion>,
|
2021-09-22 05:50:46 +00:00
|
|
|
devices: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
|
2021-04-14 20:59:30 +00:00
|
|
|
irq_chip: &mut dyn IrqChipArch,
|
2021-10-10 10:22:29 +00:00
|
|
|
kvm_vcpu_ids: &mut Vec<usize>,
|
2021-04-14 20:59:30 +00:00
|
|
|
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
|
2018-10-03 17:22:32 +00:00
|
|
|
where
|
2020-06-21 04:45:32 +00:00
|
|
|
V: VmArch,
|
2021-04-14 20:59:30 +00:00
|
|
|
Vcpu: VcpuArch;
|
2020-06-21 04:45:32 +00:00
|
|
|
|
|
|
|
/// 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
|
2020-09-29 23:00:24 +00:00
|
|
|
fn configure_vcpu(
|
2020-06-21 04:45:32 +00:00
|
|
|
guest_mem: &GuestMemory,
|
2020-09-29 23:00:24 +00:00
|
|
|
hypervisor: &dyn HypervisorArch,
|
|
|
|
irq_chip: &mut dyn IrqChipArch,
|
|
|
|
vcpu: &mut dyn VcpuArch,
|
2020-06-21 04:45:32 +00:00
|
|
|
vcpu_id: usize,
|
|
|
|
num_cpus: usize,
|
|
|
|
has_bios: bool,
|
2020-10-07 05:15:41 +00:00
|
|
|
no_smt: bool,
|
2021-10-10 10:22:29 +00:00
|
|
|
host_cpu_topology: bool,
|
2020-06-21 04:45:32 +00:00
|
|
|
) -> Result<(), Self::Error>;
|
2020-10-21 06:57:33 +00:00
|
|
|
|
2021-04-12 07:07:17 +00:00
|
|
|
/// 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,
|
2021-06-07 06:16:45 +00:00
|
|
|
) -> Result<PciAddress, Self::Error>;
|
2021-04-12 07:07:17 +00:00
|
|
|
|
2020-10-21 06:57:33 +00:00
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
|
|
|
/// Reads vCPU's registers.
|
|
|
|
fn debug_read_registers<T: VcpuArch>(vcpu: &T) -> Result<GdbStubRegs, Self::Error>;
|
|
|
|
|
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
|
|
|
/// Writes vCPU's registers.
|
|
|
|
fn debug_write_registers<T: VcpuArch>(vcpu: &T, regs: &GdbStubRegs) -> Result<(), Self::Error>;
|
|
|
|
|
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
|
|
|
/// Reads bytes from the guest memory.
|
|
|
|
fn debug_read_memory<T: VcpuArch>(
|
|
|
|
vcpu: &T,
|
|
|
|
guest_mem: &GuestMemory,
|
|
|
|
vaddr: GuestAddress,
|
|
|
|
len: usize,
|
|
|
|
) -> Result<Vec<u8>, Self::Error>;
|
|
|
|
|
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
|
|
|
/// Writes bytes to the specified guest memory.
|
|
|
|
fn debug_write_memory<T: VcpuArch>(
|
|
|
|
vcpu: &T,
|
|
|
|
guest_mem: &GuestMemory,
|
|
|
|
vaddr: GuestAddress,
|
|
|
|
buf: &[u8],
|
|
|
|
) -> Result<(), Self::Error>;
|
2020-10-22 08:43:06 +00:00
|
|
|
|
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
|
|
|
/// Make the next vCPU's run single-step.
|
|
|
|
fn debug_enable_singlestep<T: VcpuArch>(vcpu: &T) -> Result<(), Self::Error>;
|
|
|
|
|
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
|
|
|
/// Set hardware breakpoints at the given addresses.
|
|
|
|
fn debug_set_hw_breakpoints<T: VcpuArch>(
|
|
|
|
vcpu: &T,
|
|
|
|
breakpoints: &[GuestAddress],
|
|
|
|
) -> 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.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[sorted]
|
|
|
|
#[derive(Error, Debug)]
|
2018-07-09 22:39:34 +00:00
|
|
|
pub enum DeviceRegistrationError {
|
2021-08-18 21:17:06 +00:00
|
|
|
/// No more MMIO space available.
|
|
|
|
#[error("no more addresses are available")]
|
|
|
|
AddrsExhausted,
|
|
|
|
/// Could not allocate device address space for the device.
|
|
|
|
#[error("Allocating device addresses: {0}")]
|
|
|
|
AllocateDeviceAddrs(PciDeviceError),
|
2018-07-09 22:39:34 +00:00
|
|
|
/// Could not allocate IO space for the device.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Allocating IO addresses: {0}")]
|
2018-07-09 22:39:34 +00:00
|
|
|
AllocateIoAddrs(PciDeviceError),
|
2020-04-27 08:35:33 +00:00
|
|
|
/// Could not allocate MMIO or IO resource for the device.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Allocating IO resource: {0}")]
|
2020-04-27 08:35:33 +00:00
|
|
|
AllocateIoResource(resources::Error),
|
2018-07-09 22:39:34 +00:00
|
|
|
/// Could not allocate an IRQ number.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Allocating IRQ number")]
|
2018-07-09 22:39:34 +00:00
|
|
|
AllocateIrq,
|
2021-09-17 12:30:52 +00:00
|
|
|
/// Could not allocate IRQ resource for the device.
|
|
|
|
#[error("Allocating IRQ resource: {0}")]
|
|
|
|
AllocateIrqResource(devices::vfio::VfioError),
|
2021-05-11 20:53:05 +00:00
|
|
|
/// Unable to clone a jail for the device.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to clone jail: {0}")]
|
2021-05-11 20:53:05 +00:00
|
|
|
CloneJail(minijail::Error),
|
2021-08-18 21:17:06 +00:00
|
|
|
/// Appending to kernel command line failed.
|
|
|
|
#[error("unable to add device to kernel command line: {0}")]
|
|
|
|
Cmdline(kernel_cmdline::Error),
|
2019-08-13 18:20:14 +00:00
|
|
|
// Unable to create a pipe.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to create pipe: {0}")]
|
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
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to create serial device: {0}")]
|
2021-08-16 07:42:27 +00:00
|
|
|
CreateSerialDevice(devices::SerialError),
|
2021-01-07 16:30:28 +00:00
|
|
|
// Unable to create tube
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to create tube: {0}")]
|
2021-01-07 16:30:28 +00:00
|
|
|
CreateTube(base::TubeError),
|
2020-09-16 22:29:20 +00:00
|
|
|
/// Could not clone an event.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to clone event: {0}")]
|
2020-09-16 22:29:20 +00:00
|
|
|
EventClone(base::Error),
|
|
|
|
/// Could not create an event.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to create event: {0}")]
|
2020-09-16 22:29:20 +00:00
|
|
|
EventCreate(base::Error),
|
2021-08-18 21:17:06 +00:00
|
|
|
/// No more IRQs are available.
|
|
|
|
#[error("no more IRQs are available")]
|
|
|
|
IrqsExhausted,
|
2020-03-09 20:16:46 +00:00
|
|
|
/// Missing a required serial device.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("missing required serial device {0}")]
|
2020-03-09 20:16:46 +00:00
|
|
|
MissingRequiredSerialDevice(u8),
|
2018-07-09 22:39:34 +00:00
|
|
|
/// Could not add a device to the mmio bus.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to add to mmio bus: {0}")]
|
2018-07-09 22:39:34 +00:00
|
|
|
MmioInsert(BusError),
|
2018-07-24 00:58:09 +00:00
|
|
|
/// Failed to initialize proxy device for jailed device.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("failed to create proxy device: {0}")]
|
2018-07-24 00:58:09 +00:00
|
|
|
ProxyDeviceCreation(devices::ProxyError),
|
2021-08-18 21:17:06 +00:00
|
|
|
/// Failed to register battery device.
|
|
|
|
#[error("failed to register battery device to VM: {0}")]
|
|
|
|
RegisterBattery(devices::BatteryError),
|
2019-03-15 20:13:08 +00:00
|
|
|
/// Could not register PCI device capabilities.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("could not register PCI device capabilities: {0}")]
|
2019-03-15 20:13:08 +00:00
|
|
|
RegisterDeviceCapabilities(PciDeviceError),
|
2021-08-18 21:17:06 +00:00
|
|
|
/// Failed to register ioevent with VM.
|
|
|
|
#[error("failed to register ioevent to VM: {0}")]
|
|
|
|
RegisterIoevent(base::Error),
|
|
|
|
/// Failed to register irq event with VM.
|
|
|
|
#[error("failed to register irq event to VM: {0}")]
|
|
|
|
RegisterIrqfd(base::Error),
|
2018-07-09 22:39:34 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 08:20:24 +00:00
|
|
|
/// Config a PCI device for used by this vm.
|
|
|
|
pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>(
|
|
|
|
linux: &mut RunnableLinuxVm<V, Vcpu>,
|
|
|
|
mut device: Box<dyn PciDevice>,
|
|
|
|
jail: Option<Minijail>,
|
|
|
|
resources: &mut SystemAllocator,
|
2021-06-07 06:16:45 +00:00
|
|
|
) -> Result<PciAddress, DeviceRegistrationError> {
|
2021-04-09 08:20:24 +00:00
|
|
|
// Allocate PCI device address before allocating BARs.
|
|
|
|
let pci_address = device
|
|
|
|
.allocate_address(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
|
|
|
|
|
|
|
|
// Allocate ranges that may need to be in the low MMIO region (MmioType::Low).
|
|
|
|
let mmio_ranges = device
|
|
|
|
.allocate_io_bars(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateIoAddrs)?;
|
|
|
|
|
|
|
|
// Allocate device ranges that may be in low or high MMIO after low-only ranges.
|
|
|
|
let device_ranges = device
|
|
|
|
.allocate_device_bars(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
|
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
// Do not suggest INTx for hot-plug devices.
|
|
|
|
let intx_event = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
let intx_resample = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
2021-04-09 08:20:24 +00:00
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
if let Some((gsi, _pin)) = device.assign_irq(&intx_event, &intx_resample, None) {
|
|
|
|
resources.reserve_irq(gsi);
|
2021-04-09 08:20:24 +00:00
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
linux
|
|
|
|
.irq_chip
|
|
|
|
.as_irq_chip_mut()
|
|
|
|
.register_irq_event(gsi, &intx_event, Some(&intx_resample))
|
|
|
|
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
|
|
|
|
}
|
2021-04-09 08:20:24 +00:00
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
let mut keep_rds = device.keep_rds();
|
|
|
|
syslog::push_descriptors(&mut keep_rds);
|
2021-04-09 08:20:24 +00:00
|
|
|
|
|
|
|
device
|
|
|
|
.register_device_capabilities()
|
|
|
|
.map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
|
|
|
|
for (event, addr, datamatch) in device.ioevents() {
|
|
|
|
let io_addr = IoEventAddress::Mmio(addr);
|
|
|
|
linux
|
|
|
|
.vm
|
2021-08-20 00:05:29 +00:00
|
|
|
.register_ioevent(event, io_addr, datamatch)
|
2021-04-09 08:20:24 +00:00
|
|
|
.map_err(DeviceRegistrationError::RegisterIoevent)?;
|
|
|
|
keep_rds.push(event.as_raw_descriptor());
|
|
|
|
}
|
|
|
|
let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
|
|
|
|
let proxy = ProxyDevice::new(device, &jail, keep_rds)
|
|
|
|
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
|
|
|
|
linux
|
|
|
|
.pid_debug_label_map
|
|
|
|
.insert(proxy.pid() as u32, proxy.debug_label());
|
|
|
|
Arc::new(Mutex::new(proxy))
|
|
|
|
} else {
|
|
|
|
device.on_sandboxed();
|
|
|
|
Arc::new(Mutex::new(device))
|
|
|
|
};
|
2021-04-12 07:07:17 +00:00
|
|
|
|
|
|
|
linux
|
|
|
|
.root_config
|
|
|
|
.lock()
|
|
|
|
.add_device(pci_address, arced_dev.clone());
|
|
|
|
|
2021-04-09 08:20:24 +00:00
|
|
|
for range in &mmio_ranges {
|
|
|
|
linux
|
|
|
|
.mmio_bus
|
|
|
|
.insert(arced_dev.clone(), range.0, range.1)
|
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
for range in &device_ranges {
|
|
|
|
linux
|
|
|
|
.mmio_bus
|
|
|
|
.insert(arced_dev.clone(), range.0, range.1)
|
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
|
|
|
}
|
|
|
|
|
2021-06-07 06:16:45 +00:00
|
|
|
Ok(pci_address)
|
2021-04-09 08:20:24 +00:00
|
|
|
}
|
|
|
|
|
2021-09-17 12:30:52 +00:00
|
|
|
/// Creates a platform device for use by this Vm.
|
|
|
|
pub fn generate_platform_bus(
|
|
|
|
devices: Vec<(VfioPlatformDevice, Option<Minijail>)>,
|
|
|
|
irq_chip: &mut dyn IrqChip,
|
2021-08-12 08:04:48 +00:00
|
|
|
mmio_bus: &Bus,
|
2021-09-17 12:30:52 +00:00
|
|
|
resources: &mut SystemAllocator,
|
|
|
|
) -> Result<BTreeMap<u32, String>, DeviceRegistrationError> {
|
|
|
|
let mut pid_labels = BTreeMap::new();
|
|
|
|
|
|
|
|
// Allocate ranges that may need to be in the Platform MMIO region (MmioType::Platform).
|
|
|
|
for (mut device, jail) in devices.into_iter() {
|
|
|
|
let ranges = device
|
|
|
|
.allocate_regions(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateIoResource)?;
|
|
|
|
|
|
|
|
let mut keep_rds = device.keep_rds();
|
|
|
|
syslog::push_descriptors(&mut keep_rds);
|
|
|
|
|
|
|
|
let irqs = device
|
|
|
|
.get_platform_irqs()
|
|
|
|
.map_err(DeviceRegistrationError::AllocateIrqResource)?;
|
|
|
|
for irq in irqs.into_iter() {
|
|
|
|
let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
keep_rds.push(irqfd.as_raw_descriptor());
|
|
|
|
let irq_resample_fd = if device.irq_is_automask(&irq) {
|
|
|
|
let evt = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
keep_rds.push(evt.as_raw_descriptor());
|
|
|
|
Some(evt)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let irq_num = resources
|
|
|
|
.allocate_irq()
|
|
|
|
.ok_or(DeviceRegistrationError::AllocateIrq)?;
|
|
|
|
irq_chip
|
|
|
|
.register_irq_event(irq_num, &irqfd, irq_resample_fd.as_ref())
|
|
|
|
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
|
|
|
|
device.assign_platform_irq(irqfd, irq_resample_fd, irq.index);
|
|
|
|
}
|
|
|
|
|
|
|
|
let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
|
|
|
|
let proxy = ProxyDevice::new(device, &jail, keep_rds)
|
|
|
|
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
|
|
|
|
pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
|
|
|
|
Arc::new(Mutex::new(proxy))
|
|
|
|
} else {
|
|
|
|
device.on_sandboxed();
|
|
|
|
Arc::new(Mutex::new(device))
|
|
|
|
};
|
|
|
|
for range in &ranges {
|
|
|
|
mmio_bus
|
|
|
|
.insert(arced_dev.clone(), range.0, range.1)
|
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(pid_labels)
|
|
|
|
}
|
|
|
|
|
2018-07-09 22:39:34 +00:00
|
|
|
/// Creates a root PCI device for use by this Vm.
|
2020-09-29 23:00:24 +00:00
|
|
|
pub fn generate_pci_root(
|
2020-09-28 17:42:26 +00:00
|
|
|
mut devices: Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
|
2021-04-14 20:59:30 +00:00
|
|
|
irq_chip: &mut dyn IrqChip,
|
2021-08-12 08:04:48 +00:00
|
|
|
mmio_bus: Arc<Bus>,
|
|
|
|
io_bus: Arc<Bus>,
|
2018-10-03 17:22:32 +00:00
|
|
|
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,
|
|
|
|
> {
|
2021-08-12 08:04:48 +00:00
|
|
|
let mut root = PciRoot::new(Arc::downgrade(&mmio_bus), Arc::downgrade(&io_bus));
|
2019-01-24 03:04:43 +00:00
|
|
|
let mut pid_labels = BTreeMap::new();
|
2020-07-24 21:08:51 +00:00
|
|
|
|
2021-02-04 05:45:47 +00:00
|
|
|
// Allocate PCI device address before allocating BARs.
|
|
|
|
let mut device_addrs = Vec::<PciAddress>::new();
|
|
|
|
for (device, _jail) in devices.iter_mut() {
|
|
|
|
let address = device
|
|
|
|
.allocate_address(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
|
|
|
|
device_addrs.push(address);
|
|
|
|
}
|
2020-09-28 17:42:26 +00:00
|
|
|
|
|
|
|
// Allocate ranges that may need to be in the low MMIO region (MmioType::Low).
|
|
|
|
let mut io_ranges = BTreeMap::new();
|
|
|
|
for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() {
|
|
|
|
let ranges = device
|
|
|
|
.allocate_io_bars(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateIoAddrs)?;
|
|
|
|
io_ranges.insert(dev_idx, ranges);
|
|
|
|
}
|
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
|
|
|
|
2020-09-28 17:42:26 +00:00
|
|
|
// Allocate device ranges that may be in low or high MMIO after low-only ranges.
|
|
|
|
let mut device_ranges = BTreeMap::new();
|
|
|
|
for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() {
|
|
|
|
let ranges = device
|
|
|
|
.allocate_device_bars(resources)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
|
|
|
|
device_ranges.insert(dev_idx, ranges);
|
|
|
|
}
|
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
// Allocate legacy INTx
|
|
|
|
let mut pci_irqs = Vec::new();
|
|
|
|
let mut irqs: Vec<Option<u32>> = vec![None; max_irqs];
|
2018-07-09 22:39:34 +00:00
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() {
|
|
|
|
// For default interrupt routing use next preallocated interrupt from the pool.
|
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
|
|
|
|
};
|
2020-06-21 04:45:32 +00:00
|
|
|
|
2021-04-29 02:44:23 +00:00
|
|
|
let intx_event = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
let intx_resample = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
|
|
|
|
if let Some((gsi, pin)) = device.assign_irq(&intx_event, &intx_resample, Some(irq_num)) {
|
|
|
|
// reserve INTx if needed and non-default.
|
|
|
|
if gsi != irq_num {
|
|
|
|
resources.reserve_irq(gsi);
|
|
|
|
};
|
|
|
|
irq_chip
|
|
|
|
.register_irq_event(gsi, &intx_event, Some(&intx_resample))
|
|
|
|
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
|
|
|
|
|
|
|
|
pci_irqs.push((device_addrs[dev_idx], gsi, pin));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
|
|
|
|
let address = device_addrs[dev_idx];
|
|
|
|
|
|
|
|
let mut keep_rds = device.keep_rds();
|
|
|
|
syslog::push_descriptors(&mut keep_rds);
|
|
|
|
keep_rds.append(&mut vm.get_memory().as_raw_descriptors());
|
2020-06-21 04:45:32 +00:00
|
|
|
|
2020-09-28 17:42:26 +00:00
|
|
|
let ranges = io_ranges.remove(&dev_idx).unwrap_or_default();
|
|
|
|
let device_ranges = device_ranges.remove(&dev_idx).unwrap_or_default();
|
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);
|
2021-08-20 00:05:29 +00:00
|
|
|
vm.register_ioevent(event, io_addr, datamatch)
|
2018-07-09 20:35:40 +00:00
|
|
|
.map_err(DeviceRegistrationError::RegisterIoevent)?;
|
2020-10-21 05:12:20 +00:00
|
|
|
keep_rds.push(event.as_raw_descriptor());
|
2018-07-09 20:35:40 +00:00
|
|
|
}
|
2021-04-29 02:44:23 +00:00
|
|
|
|
2019-03-09 00:56:14 +00:00
|
|
|
let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
|
2020-10-21 05:12:20 +00:00
|
|
|
let proxy = ProxyDevice::new(device, &jail, keep_rds)
|
2018-10-18 23:45:13 +00:00
|
|
|
.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
|
2020-10-08 22:02:20 +00:00
|
|
|
.insert(arced_dev.clone(), range.0, range.1)
|
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
|
2020-10-08 22:02:20 +00:00
|
|
|
.insert(arced_dev.clone(), range.0, range.1)
|
2019-02-21 02:56:22 +00:00
|
|
|
.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
|
|
|
|
2020-04-27 08:35:33 +00:00
|
|
|
/// Adds goldfish battery
|
|
|
|
/// return the platform needed resouces include its AML data, irq number
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `amls` - the vector to put the goldfish battery AML
|
2020-04-27 08:37:10 +00:00
|
|
|
/// * `battery_jail` - used when sandbox is enabled
|
2020-04-27 08:35:33 +00:00
|
|
|
/// * `mmio_bus` - bus to add the devices to
|
|
|
|
/// * `irq_chip` - the IrqChip object for registering irq events
|
|
|
|
/// * `irq_num` - assigned interrupt to use
|
|
|
|
/// * `resources` - the SystemAllocator to allocate IO and MMIO for acpi
|
|
|
|
pub fn add_goldfish_battery(
|
|
|
|
amls: &mut Vec<u8>,
|
2020-04-27 08:37:10 +00:00
|
|
|
battery_jail: Option<Minijail>,
|
2021-08-12 08:04:48 +00:00
|
|
|
mmio_bus: &Bus,
|
2021-04-14 20:59:30 +00:00
|
|
|
irq_chip: &mut dyn IrqChip,
|
2020-04-27 08:35:33 +00:00
|
|
|
irq_num: u32,
|
|
|
|
resources: &mut SystemAllocator,
|
2021-01-07 16:30:28 +00:00
|
|
|
) -> Result<Tube, DeviceRegistrationError> {
|
2020-04-27 08:35:33 +00:00
|
|
|
let alloc = resources.get_anon_alloc();
|
|
|
|
let mmio_base = resources
|
|
|
|
.mmio_allocator(MmioType::Low)
|
|
|
|
.allocate_with_align(
|
|
|
|
devices::bat::GOLDFISHBAT_MMIO_LEN,
|
|
|
|
alloc,
|
|
|
|
"GoldfishBattery".to_string(),
|
|
|
|
devices::bat::GOLDFISHBAT_MMIO_LEN,
|
|
|
|
)
|
|
|
|
.map_err(DeviceRegistrationError::AllocateIoResource)?;
|
|
|
|
|
|
|
|
let irq_evt = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
let irq_resample_evt = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
|
|
|
|
|
|
|
|
irq_chip
|
|
|
|
.register_irq_event(irq_num, &irq_evt, Some(&irq_resample_evt))
|
|
|
|
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
|
|
|
|
|
2021-01-07 16:30:28 +00:00
|
|
|
let (control_tube, response_tube) =
|
|
|
|
Tube::pair().map_err(DeviceRegistrationError::CreateTube)?;
|
2020-04-27 08:39:33 +00:00
|
|
|
|
2020-11-10 09:24:04 +00:00
|
|
|
#[cfg(feature = "power-monitor-powerd")]
|
|
|
|
let create_monitor = Some(Box::new(power_monitor::powerd::DBusMonitor::connect)
|
|
|
|
as Box<dyn power_monitor::CreatePowerMonitorFn>);
|
|
|
|
|
|
|
|
#[cfg(not(feature = "power-monitor-powerd"))]
|
|
|
|
let create_monitor = None;
|
|
|
|
|
2020-04-27 08:39:33 +00:00
|
|
|
let goldfish_bat = devices::GoldfishBattery::new(
|
|
|
|
mmio_base,
|
|
|
|
irq_num,
|
|
|
|
irq_evt,
|
|
|
|
irq_resample_evt,
|
2021-01-07 16:30:28 +00:00
|
|
|
response_tube,
|
2020-11-10 09:24:04 +00:00
|
|
|
create_monitor,
|
2020-04-27 08:39:33 +00:00
|
|
|
)
|
|
|
|
.map_err(DeviceRegistrationError::RegisterBattery)?;
|
2020-04-27 08:35:33 +00:00
|
|
|
Aml::to_aml_bytes(&goldfish_bat, amls);
|
|
|
|
|
2020-04-27 08:37:10 +00:00
|
|
|
match battery_jail.as_ref() {
|
|
|
|
Some(jail) => {
|
2020-12-03 23:35:01 +00:00
|
|
|
let mut keep_rds = goldfish_bat.keep_rds();
|
|
|
|
syslog::push_fds(&mut keep_rds);
|
2020-04-27 08:37:10 +00:00
|
|
|
mmio_bus
|
|
|
|
.insert(
|
|
|
|
Arc::new(Mutex::new(
|
2021-08-20 00:05:29 +00:00
|
|
|
ProxyDevice::new(goldfish_bat, jail, keep_rds)
|
2020-04-27 08:37:10 +00:00
|
|
|
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
|
|
|
|
)),
|
|
|
|
mmio_base,
|
|
|
|
devices::bat::GOLDFISHBAT_MMIO_LEN,
|
|
|
|
)
|
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
mmio_bus
|
|
|
|
.insert(
|
|
|
|
Arc::new(Mutex::new(goldfish_bat)),
|
|
|
|
mmio_base,
|
|
|
|
devices::bat::GOLDFISHBAT_MMIO_LEN,
|
|
|
|
)
|
|
|
|
.map_err(DeviceRegistrationError::MmioInsert)?;
|
|
|
|
}
|
|
|
|
}
|
2020-04-27 08:35:33 +00:00
|
|
|
|
2021-01-07 16:30:28 +00:00
|
|
|
Ok(control_tube)
|
2020-04-27 08:35:33 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 23:20:30 +00:00
|
|
|
/// Errors for image loading.
|
2021-08-18 21:17:06 +00:00
|
|
|
#[sorted]
|
|
|
|
#[derive(Error, Debug)]
|
2018-12-12 23:20:30 +00:00
|
|
|
pub enum LoadImageError {
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Alignment not a power of two: {0}")]
|
2019-05-17 17:55:45 +00:00
|
|
|
BadAlignment(u64),
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Image size too large: {0}")]
|
2018-12-12 23:20:30 +00:00
|
|
|
ImageSizeTooLarge(u64),
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Reading image into memory failed: {0}")]
|
2018-12-12 23:20:30 +00:00
|
|
|
ReadToMemory(GuestMemoryError),
|
2021-08-18 21:17:06 +00:00
|
|
|
#[error("Seek failed: {0}")]
|
|
|
|
Seek(io::Error),
|
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))
|
|
|
|
}
|