mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 10:10:41 +00:00
Enable KVM_CAP_ARM_PROTECTED_VM when --protected-vm is passed.
- Add an address space region for the protected KVM firmware. - Query firmware size, mmap something that size and create a memslot. BUG=b:163789172 TEST=cargo test Change-Id: I054cf5d763c980d073c17bce70e85a781816b64d Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2623942 Auto-Submit: Andrew Walbran <qwandor@google.com> Reviewed-by: Dylan Reid <dgreid@chromium.org> Commit-Queue: Andrew Walbran <qwandor@google.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
9b006f13c8
commit
413f854564
24 changed files with 190 additions and 58 deletions
|
@ -17,6 +17,7 @@ use arch::{
|
|||
use base::Event;
|
||||
use devices::{
|
||||
Bus, BusError, IrqChip, IrqChipAArch64, PciAddress, PciConfigMmio, PciDevice, PciInterruptPin,
|
||||
ProtectionType,
|
||||
};
|
||||
use hypervisor::{
|
||||
DeviceKind, Hypervisor, HypervisorCap, PsciVersion, VcpuAArch64, VcpuFeature, VmAArch64,
|
||||
|
@ -49,6 +50,10 @@ const AARCH64_FDT_OFFSET_IN_BIOS_MODE: u64 = 0x0;
|
|||
const AARCH64_BIOS_OFFSET: u64 = AARCH64_FDT_MAX_SIZE;
|
||||
const AARCH64_BIOS_MAX_LEN: u64 = 1 << 20;
|
||||
|
||||
const AARCH64_PROTECTED_VM_FW_MAX_SIZE: u64 = 0x200000;
|
||||
const AARCH64_PROTECTED_VM_FW_START: u64 =
|
||||
AARCH64_PHYS_MEM_START - AARCH64_PROTECTED_VM_FW_MAX_SIZE;
|
||||
|
||||
// These constants indicate the placement of the GIC registers in the physical
|
||||
// address space.
|
||||
const AARCH64_GIC_DIST_BASE: u64 = AARCH64_AXI_BASE - AARCH64_GIC_DIST_SIZE;
|
||||
|
@ -141,6 +146,7 @@ pub enum Error {
|
|||
GetSerialCmdline(GetSerialCmdlineError),
|
||||
InitrdLoadFailure(arch::LoadImageError),
|
||||
KernelLoadFailure(arch::LoadImageError),
|
||||
ProtectVm(base::Error),
|
||||
RegisterIrqfd(base::Error),
|
||||
RegisterPci(BusError),
|
||||
RegisterVsock(arch::DeviceRegistrationError),
|
||||
|
@ -175,6 +181,7 @@ impl Display for Error {
|
|||
GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e),
|
||||
InitrdLoadFailure(e) => write!(f, "initrd could not be loaded: {}", e),
|
||||
KernelLoadFailure(e) => write!(f, "kernel could not be loaded: {}", e),
|
||||
ProtectVm(e) => write!(f, "failed to protect vm: {}", e),
|
||||
RegisterIrqfd(e) => write!(f, "failed to register irq fd: {}", e),
|
||||
RegisterPci(e) => write!(f, "error registering PCI bus: {}", e),
|
||||
RegisterVsock(e) => write!(f, "error registering virtual socket device: {}", e),
|
||||
|
@ -249,6 +256,14 @@ impl arch::LinuxArch for AArch64 {
|
|||
let mem = Self::setup_memory(components.memory_size)?;
|
||||
let mut vm = create_vm(mem.clone()).map_err(|e| Error::CreateVm(Box::new(e)))?;
|
||||
|
||||
if components.protected_vm == ProtectionType::Protected {
|
||||
vm.enable_protected_vm(
|
||||
GuestAddress(AARCH64_PROTECTED_VM_FW_START),
|
||||
AARCH64_PROTECTED_VM_FW_MAX_SIZE,
|
||||
)
|
||||
.map_err(Error::ProtectVm)?;
|
||||
}
|
||||
|
||||
let mut use_pmu = vm
|
||||
.get_hypervisor()
|
||||
.check_capability(&HypervisorCap::ArmPmuV3);
|
||||
|
|
|
@ -21,7 +21,7 @@ use base::{syslog, AsRawDescriptor, Event};
|
|||
use devices::virtio::VirtioDevice;
|
||||
use devices::{
|
||||
Bus, BusDevice, BusError, IrqChip, PciAddress, PciDevice, PciDeviceError, PciInterruptPin,
|
||||
PciRoot, ProxyDevice,
|
||||
PciRoot, ProtectionType, ProxyDevice,
|
||||
};
|
||||
use hypervisor::{IoEventAddress, Vm};
|
||||
use minijail::Minijail;
|
||||
|
@ -91,7 +91,7 @@ pub struct VmComponents {
|
|||
pub wayland_dmabuf: bool,
|
||||
pub acpi_sdts: Vec<SDT>,
|
||||
pub rt_cpus: Vec<usize>,
|
||||
pub protected_vm: bool,
|
||||
pub protected_vm: ProtectionType,
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
pub gdb: Option<(u32, VmControlRequestSocket)>, // port and control socket.
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use std::thread;
|
|||
use std::time::Duration;
|
||||
|
||||
use base::{error, info, read_raw_stdin, syslog, AsRawDescriptor, Event, RawDescriptor};
|
||||
use devices::{Bus, ProxyDevice, Serial, SerialDevice};
|
||||
use devices::{Bus, ProtectionType, ProxyDevice, Serial, SerialDevice};
|
||||
use minijail::Minijail;
|
||||
use sync::Mutex;
|
||||
|
||||
|
@ -211,7 +211,7 @@ impl SerialParameters {
|
|||
/// this function.
|
||||
pub fn create_serial_device<T: SerialDevice>(
|
||||
&self,
|
||||
protected_vm: bool,
|
||||
protected_vm: ProtectionType,
|
||||
evt: &Event,
|
||||
keep_rds: &mut Vec<RawDescriptor>,
|
||||
) -> std::result::Result<T, Error> {
|
||||
|
@ -409,7 +409,7 @@ pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
|
|||
/// * `serial_parameters` - definitions of serial parameter configurations.
|
||||
/// All four of the traditional PC-style serial ports (COM1-COM4) must be specified.
|
||||
pub fn add_serial_devices(
|
||||
protected_vm: bool,
|
||||
protected_vm: ProtectionType,
|
||||
io_bus: &mut Bus,
|
||||
com_evt_1_3: &Event,
|
||||
com_evt_2_4: &Event,
|
||||
|
|
|
@ -48,3 +48,12 @@ pub use self::usb::host_backend::host_backend_device_provider::HostBackendDevice
|
|||
pub use self::usb::xhci::xhci_controller::XhciController;
|
||||
pub use self::vfio::{VfioContainer, VfioDevice};
|
||||
pub use self::virtio::VirtioPciDevice;
|
||||
|
||||
/// Whether the VM should be run in protected mode or not.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ProtectionType {
|
||||
/// The VM should be run in the unprotected mode, where the host has access to its memory.
|
||||
Unprotected,
|
||||
/// The VM should be run in protected mode, so the host cannot access its memory directly.
|
||||
Protected,
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::thread::{self};
|
|||
use base::{error, Event, RawDescriptor, Result};
|
||||
|
||||
use crate::bus::BusAccessInfo;
|
||||
use crate::{BusDevice, SerialDevice};
|
||||
use crate::{BusDevice, ProtectionType, SerialDevice};
|
||||
|
||||
const LOOP_SIZE: usize = 0x40;
|
||||
|
||||
|
@ -84,7 +84,7 @@ pub struct Serial {
|
|||
|
||||
impl SerialDevice for Serial {
|
||||
fn new(
|
||||
_protected_vm: bool,
|
||||
_protected_vm: ProtectionType,
|
||||
interrupt_evt: Event,
|
||||
input: Option<Box<dyn io::Read + Send>>,
|
||||
out: Option<Box<dyn io::Write + Send>>,
|
||||
|
@ -419,7 +419,7 @@ mod tests {
|
|||
let serial_out = SharedBuffer::new();
|
||||
|
||||
let mut serial = Serial::new(
|
||||
false,
|
||||
ProtectionType::Unprotected,
|
||||
intr_evt,
|
||||
None,
|
||||
Some(Box::new(serial_out.clone())),
|
||||
|
@ -441,7 +441,7 @@ mod tests {
|
|||
let serial_out = SharedBuffer::new();
|
||||
|
||||
let mut serial = Serial::new(
|
||||
false,
|
||||
ProtectionType::Unprotected,
|
||||
intr_evt.try_clone().unwrap(),
|
||||
None,
|
||||
Some(Box::new(serial_out.clone())),
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
|
||||
use std::io;
|
||||
|
||||
use crate::ProtectionType;
|
||||
use base::{Event, RawDescriptor};
|
||||
|
||||
/// Abstraction over serial-like devices that can be created given an event and optional input and
|
||||
/// output streams.
|
||||
pub trait SerialDevice {
|
||||
fn new(
|
||||
protected_vm: bool,
|
||||
protected_vm: ProtectionType,
|
||||
interrupt_evt: Event,
|
||||
input: Option<Box<dyn io::Read + Send>>,
|
||||
output: Option<Box<dyn io::Write + Send>>,
|
||||
|
|
|
@ -868,6 +868,7 @@ mod tests {
|
|||
|
||||
use crate::virtio::base_features;
|
||||
use crate::virtio::descriptor_utils::{create_descriptor_chain, DescriptorType};
|
||||
use crate::ProtectionType;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -876,7 +877,7 @@ mod tests {
|
|||
let f = tempfile().unwrap();
|
||||
f.set_len(0x1000).unwrap();
|
||||
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = Block::new(features, Box::new(f), true, false, 512, None, None).unwrap();
|
||||
let mut num_sectors = [0u8; 4];
|
||||
b.read_config(0, &mut num_sectors);
|
||||
|
@ -893,7 +894,7 @@ mod tests {
|
|||
let f = tempfile().unwrap();
|
||||
f.set_len(0x1000).unwrap();
|
||||
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = Block::new(features, Box::new(f), true, false, 4096, None, None).unwrap();
|
||||
let mut blk_size = [0u8; 4];
|
||||
b.read_config(20, &mut blk_size);
|
||||
|
@ -906,7 +907,7 @@ mod tests {
|
|||
// read-write block device
|
||||
{
|
||||
let f = tempfile().unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = Block::new(features, Box::new(f), false, true, 512, None, None).unwrap();
|
||||
// writable device should set VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_DISCARD
|
||||
// + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE
|
||||
|
@ -917,7 +918,7 @@ mod tests {
|
|||
// read-write block device, non-sparse
|
||||
{
|
||||
let f = tempfile().unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = Block::new(features, Box::new(f), false, false, 512, None, None).unwrap();
|
||||
// writable device should set VIRTIO_BLK_F_FLUSH
|
||||
// + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE
|
||||
|
@ -928,7 +929,7 @@ mod tests {
|
|||
// read-only block device
|
||||
{
|
||||
let f = tempfile().unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = Block::new(features, Box::new(f), true, true, 512, None, None).unwrap();
|
||||
// read-only device should set VIRTIO_BLK_F_FLUSH and VIRTIO_BLK_F_RO
|
||||
// + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE + VIRTIO_BLK_F_SEG_MAX
|
||||
|
|
|
@ -950,6 +950,7 @@ mod tests {
|
|||
|
||||
use crate::virtio::base_features;
|
||||
use crate::virtio::descriptor_utils::{create_descriptor_chain, DescriptorType};
|
||||
use crate::ProtectionType;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -961,7 +962,7 @@ mod tests {
|
|||
let f = File::create(&path).unwrap();
|
||||
f.set_len(0x1000).unwrap();
|
||||
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = BlockAsync::new(features, Box::new(f), true, false, 512, None).unwrap();
|
||||
let mut num_sectors = [0u8; 4];
|
||||
b.read_config(0, &mut num_sectors);
|
||||
|
@ -981,7 +982,7 @@ mod tests {
|
|||
let f = File::create(&path).unwrap();
|
||||
f.set_len(0x1000).unwrap();
|
||||
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = BlockAsync::new(features, Box::new(f), true, false, 4096, None).unwrap();
|
||||
let mut blk_size = [0u8; 4];
|
||||
b.read_config(20, &mut blk_size);
|
||||
|
@ -998,7 +999,7 @@ mod tests {
|
|||
// read-write block device
|
||||
{
|
||||
let f = File::create(&path).unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = BlockAsync::new(features, Box::new(f), false, true, 512, None).unwrap();
|
||||
// writable device should set VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_DISCARD
|
||||
// + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE
|
||||
|
@ -1009,7 +1010,7 @@ mod tests {
|
|||
// read-write block device, non-sparse
|
||||
{
|
||||
let f = File::create(&path).unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = BlockAsync::new(features, Box::new(f), false, false, 512, None).unwrap();
|
||||
// read-only device should set VIRTIO_BLK_F_FLUSH and VIRTIO_BLK_F_RO
|
||||
// + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE + VIRTIO_BLK_F_SEG_MAX
|
||||
|
@ -1020,7 +1021,7 @@ mod tests {
|
|||
// read-only block device
|
||||
{
|
||||
let f = File::create(&path).unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
let b = BlockAsync::new(features, Box::new(f), true, true, 512, None).unwrap();
|
||||
// read-only device should set VIRTIO_BLK_F_FLUSH and VIRTIO_BLK_F_RO
|
||||
// + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE + VIRTIO_BLK_F_SEG_MAX
|
||||
|
|
|
@ -13,7 +13,7 @@ use vm_memory::GuestMemory;
|
|||
use super::{
|
||||
base_features, copy_config, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_CONSOLE,
|
||||
};
|
||||
use crate::SerialDevice;
|
||||
use crate::{ProtectionType, SerialDevice};
|
||||
|
||||
const QUEUE_SIZE: u16 = 256;
|
||||
|
||||
|
@ -308,7 +308,7 @@ pub struct Console {
|
|||
|
||||
impl SerialDevice for Console {
|
||||
fn new(
|
||||
protected_vm: bool,
|
||||
protected_vm: ProtectionType,
|
||||
_evt: Event,
|
||||
input: Option<Box<dyn io::Read + Send>>,
|
||||
output: Option<Box<dyn io::Write + Send>>,
|
||||
|
|
|
@ -56,6 +56,7 @@ pub use self::virtio_device::*;
|
|||
pub use self::virtio_pci_device::*;
|
||||
pub use self::wl::*;
|
||||
|
||||
use crate::ProtectionType;
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
@ -156,10 +157,10 @@ pub fn copy_config(dst: &mut [u8], dst_offset: u64, src: &[u8], src_offset: u64)
|
|||
}
|
||||
|
||||
/// Returns the set of reserved base features common to all virtio devices.
|
||||
pub fn base_features(protected_vm: bool) -> u64 {
|
||||
pub fn base_features(protected_vm: ProtectionType) -> u64 {
|
||||
let mut features: u64 = 1 << VIRTIO_F_VERSION_1;
|
||||
|
||||
if protected_vm {
|
||||
if protected_vm == ProtectionType::Protected {
|
||||
features |= 1 << VIRTIO_F_ACCESS_PLATFORM;
|
||||
}
|
||||
|
||||
|
|
|
@ -348,6 +348,7 @@ pub mod tests {
|
|||
use super::*;
|
||||
use crate::virtio::base_features;
|
||||
use crate::virtio::VIRTIO_MSI_NO_VECTOR;
|
||||
use crate::ProtectionType;
|
||||
use net_util::fakes::FakeTap;
|
||||
use std::result;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
@ -363,7 +364,7 @@ pub mod tests {
|
|||
|
||||
fn create_net_common() -> Net<FakeTap, FakeNet<FakeTap>> {
|
||||
let guest_memory = create_guest_memory().unwrap();
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
Net::<FakeTap, FakeNet<FakeTap>>::new(
|
||||
features,
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::sync::Arc;
|
|||
use base::Event;
|
||||
use cros_fuzz::fuzz_target;
|
||||
use devices::virtio::{base_features, Block, Interrupt, Queue, VirtioDevice};
|
||||
use devices::ProtectionType;
|
||||
use tempfile;
|
||||
use vm_memory::{GuestAddress, GuestMemory};
|
||||
|
||||
|
@ -77,7 +78,7 @@ fuzz_target!(|bytes| {
|
|||
let queue_evts: Vec<Event> = vec![Event::new().unwrap()];
|
||||
let queue_evt = queue_evts[0].try_clone().unwrap();
|
||||
|
||||
let features = base_features(false);
|
||||
let features = base_features(ProtectionType::Unprotected);
|
||||
|
||||
let disk_file = tempfile::tempfile().unwrap();
|
||||
let mut block =
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use base::Result;
|
||||
use downcast_rs::impl_downcast;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
use crate::{Hypervisor, IrqRoute, IrqSource, IrqSourceChip, Vcpu, Vm};
|
||||
|
||||
|
@ -18,6 +19,10 @@ pub trait VmAArch64: Vm {
|
|||
/// Gets the `Hypervisor` that created this VM.
|
||||
fn get_hypervisor(&self) -> &dyn Hypervisor;
|
||||
|
||||
/// Enables protected mode for the VM, creating a memslot for the firmware as needed.
|
||||
/// Only works on VMs that support `VmCap::Protected`.
|
||||
fn enable_protected_vm(&mut self, fw_addr: GuestAddress, fw_max_size: u64) -> Result<()>;
|
||||
|
||||
/// Create a Vcpu with the specified Vcpu ID.
|
||||
fn create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuAArch64>>;
|
||||
}
|
||||
|
|
|
@ -22,4 +22,6 @@ pub enum VmCap {
|
|||
PvClock,
|
||||
/// PV clock can be notified when guest is being paused
|
||||
PvClockSuspend,
|
||||
/// VM can be run in protected mode, where the host does not have access to its memory.
|
||||
Protected,
|
||||
}
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use libc::ENXIO;
|
||||
use libc::{EINVAL, ENOMEM, ENOSYS, ENXIO};
|
||||
|
||||
use base::{errno_result, error, ioctl_with_mut_ref, ioctl_with_ref, Error, Result};
|
||||
use base::{
|
||||
errno_result, error, ioctl_with_mut_ref, ioctl_with_ref, Error, MemoryMappingBuilder, Result,
|
||||
};
|
||||
use kvm_sys::*;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
use super::{KvmVcpu, KvmVm};
|
||||
use super::{KvmCap, KvmVcpu, KvmVm};
|
||||
use crate::{
|
||||
ClockState, DeviceKind, Hypervisor, IrqSourceChip, PsciVersion, VcpuAArch64, VcpuFeature,
|
||||
ClockState, DeviceKind, Hypervisor, IrqSourceChip, PsciVersion, VcpuAArch64, VcpuFeature, Vm,
|
||||
VmAArch64, VmCap,
|
||||
};
|
||||
|
||||
|
@ -47,6 +50,29 @@ impl KvmVm {
|
|||
pub fn set_pvclock_arch(&self, _state: &ClockState) -> Result<()> {
|
||||
Err(Error::new(ENXIO))
|
||||
}
|
||||
|
||||
fn get_protected_vm_info(&self) -> Result<KvmProtectedVmInfo> {
|
||||
let mut info = KvmProtectedVmInfo {
|
||||
firmware_size: 0,
|
||||
reserved: [0; 7],
|
||||
};
|
||||
// Safe because we allocated the struct and we know the kernel won't write beyond the end of
|
||||
// the struct or keep a pointer to it.
|
||||
unsafe {
|
||||
self.enable_raw_capability(
|
||||
KvmCap::ArmProtectedVm,
|
||||
KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO,
|
||||
&[&mut info as *mut KvmProtectedVmInfo as u64, 0, 0, 0],
|
||||
)
|
||||
}?;
|
||||
Ok(info)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct KvmProtectedVmInfo {
|
||||
firmware_size: u64,
|
||||
reserved: [u64; 7],
|
||||
}
|
||||
|
||||
impl VmAArch64 for KvmVm {
|
||||
|
@ -54,6 +80,32 @@ impl VmAArch64 for KvmVm {
|
|||
&self.kvm
|
||||
}
|
||||
|
||||
fn enable_protected_vm(&mut self, fw_addr: GuestAddress, fw_max_size: u64) -> Result<()> {
|
||||
if !self.check_capability(VmCap::Protected) {
|
||||
return Err(Error::new(ENOSYS));
|
||||
}
|
||||
let info = self.get_protected_vm_info()?;
|
||||
let memslot = if info.firmware_size == 0 {
|
||||
u64::MAX
|
||||
} else {
|
||||
if info.firmware_size > fw_max_size {
|
||||
return Err(Error::new(ENOMEM));
|
||||
}
|
||||
let mem = MemoryMappingBuilder::new(info.firmware_size as usize)
|
||||
.build()
|
||||
.map_err(|_| Error::new(EINVAL))?;
|
||||
self.add_memory_region(fw_addr, Box::new(mem), false, false)? as u64
|
||||
};
|
||||
// Safe because none of the args are pointers.
|
||||
unsafe {
|
||||
self.enable_raw_capability(
|
||||
KvmCap::ArmProtectedVm,
|
||||
KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE,
|
||||
&[memslot, 0, 0, 0],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuAArch64>> {
|
||||
// create_vcpu is declared separately in VmAArch64 and VmX86, so it can return VcpuAArch64
|
||||
// or VcpuX86. But both use the same implementation in KvmVm::create_vcpu.
|
||||
|
|
|
@ -375,6 +375,38 @@ impl KvmVm {
|
|||
errno_result()
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether a particular KVM-specific capability is available for this VM.
|
||||
fn check_raw_capability(&self, capability: KvmCap) -> bool {
|
||||
// Safe because we know that our file is a KVM fd, and if the cap is invalid KVM assumes
|
||||
// it's an unavailable extension and returns 0.
|
||||
unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), capability as c_ulong) == 1 }
|
||||
}
|
||||
|
||||
// Currently only used on aarch64, but works on any architecture.
|
||||
#[allow(dead_code)]
|
||||
/// Enables a KVM-specific capability for this VM, with the given arguments.
|
||||
unsafe fn enable_raw_capability(
|
||||
&self,
|
||||
capability: KvmCap,
|
||||
flags: u32,
|
||||
args: &[u64; 4],
|
||||
) -> Result<()> {
|
||||
let kvm_cap = kvm_enable_cap {
|
||||
cap: capability as u32,
|
||||
args: *args,
|
||||
flags,
|
||||
..Default::default()
|
||||
};
|
||||
// Safe because we allocated the struct and we know the kernel will read
|
||||
// exactly the size of the struct.
|
||||
let ret = ioctl_with_ref(self, KVM_ENABLE_CAP(), &kvm_cap);
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
errno_result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Vm for KvmVm {
|
||||
|
@ -395,16 +427,11 @@ impl Vm for KvmVm {
|
|||
match c {
|
||||
VmCap::DirtyLog => true,
|
||||
VmCap::PvClock => false,
|
||||
VmCap::PvClockSuspend => self.check_raw_capability(KVM_CAP_KVMCLOCK_CTRL),
|
||||
VmCap::PvClockSuspend => self.check_raw_capability(KvmCap::KvmclockCtrl),
|
||||
VmCap::Protected => self.check_raw_capability(KvmCap::ArmProtectedVm),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_raw_capability(&self, cap: u32) -> bool {
|
||||
// Safe because we know that our file is a KVM fd, and if the cap is invalid KVM assumes
|
||||
// it's an unavailable extension and returns 0.
|
||||
unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), cap as c_ulong) == 1 }
|
||||
}
|
||||
|
||||
fn get_memory(&self) -> &GuestMemory {
|
||||
&self.guest_mem
|
||||
}
|
||||
|
@ -821,7 +848,7 @@ impl Vcpu for KvmVcpu {
|
|||
args: *args,
|
||||
..Default::default()
|
||||
};
|
||||
// Safe becuase we allocated the struct and we know the kernel will read
|
||||
// Safe because we allocated the struct and we know the kernel will read
|
||||
// exactly the size of the struct.
|
||||
let ret = unsafe { ioctl_with_ref(self, KVM_ENABLE_CAP(), &kvm_cap) };
|
||||
if ret == 0 {
|
||||
|
@ -1176,9 +1203,9 @@ mod tests {
|
|||
let kvm = Kvm::new().unwrap();
|
||||
let gm = GuestMemory::new(&[(GuestAddress(0), 0x1000)]).unwrap();
|
||||
let vm = KvmVm::new(&kvm, gm).unwrap();
|
||||
assert!(vm.check_raw_capability(KVM_CAP_USER_MEMORY));
|
||||
assert!(vm.check_raw_capability(KvmCap::UserMemory));
|
||||
// I assume nobody is testing this on s390
|
||||
assert!(!vm.check_raw_capability(KVM_CAP_S390_USER_SIGP));
|
||||
assert!(!vm.check_raw_capability(KvmCap::S390UserSigp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -47,17 +47,10 @@ pub trait Vm: Send {
|
|||
/// Checks if a particular `VmCap` is available.
|
||||
///
|
||||
/// This is distinct from the `Hypervisor` version of this method because some extensions depend
|
||||
/// on the particular `Vm` existence. This method is encouraged because it more accurately
|
||||
/// on the particular `Vm` instance. This method is encouraged because it more accurately
|
||||
/// reflects the usable capabilities.
|
||||
fn check_capability(&self, c: VmCap) -> bool;
|
||||
|
||||
/// Checks if a particular hypervisor-specific capability is available.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cap` - hypervisor-specific constant defined by the hypervisor API (e.g., kvm.h)
|
||||
fn check_raw_capability(&self, cap: u32) -> bool;
|
||||
|
||||
/// Gets the guest-mapped memory for the Vm.
|
||||
fn get_memory(&self) -> &GuestMemory;
|
||||
|
||||
|
@ -267,12 +260,12 @@ pub trait Vcpu: downcast_rs::DowncastSync {
|
|||
fn set_data(&self, data: &[u8]) -> Result<()>;
|
||||
|
||||
/// Signals to the hypervisor that this guest is being paused by userspace. Only works on Vms
|
||||
/// that support `VmCapability::PvClockSuspend`.
|
||||
/// that support `VmCap::PvClockSuspend`.
|
||||
fn pvclock_ctrl(&self) -> Result<()>;
|
||||
|
||||
/// Specifies set of signals that are blocked during execution of `RunnableVcpu::run`. Signals
|
||||
/// that are not blocked will cause run to return with `VcpuExit::Intr`. Only works on Vms that
|
||||
/// support `VmCapability::SignalMask`.
|
||||
/// support `VmCap::SignalMask`.
|
||||
fn set_signal_mask(&self, signals: &[c_int]) -> Result<()>;
|
||||
|
||||
/// Enables a hypervisor-specific extension on this Vcpu. `cap` is a constant defined by the
|
||||
|
|
|
@ -120,4 +120,5 @@ pub enum Cap {
|
|||
S390UserSigp = KVM_CAP_S390_USER_SIGP,
|
||||
ImmediateExit = KVM_CAP_IMMEDIATE_EXIT,
|
||||
ArmPmuV3 = KVM_CAP_ARM_PMU_V3,
|
||||
ArmProtectedVm = KVM_CAP_ARM_PROTECTED_VM,
|
||||
}
|
||||
|
|
|
@ -618,6 +618,10 @@ pub const KVM_CAP_ARM_PTRAUTH_GENERIC: u32 = 172;
|
|||
pub const KVM_CAP_PMU_EVENT_FILTER: u32 = 173;
|
||||
pub const KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: u32 = 174;
|
||||
pub const KVM_CAP_HYPERV_DIRECT_TLBFLUSH: u32 = 175;
|
||||
// TODO(qwandor): Update this once the pKVM patches are merged upstream with a stable capability ID.
|
||||
pub const KVM_CAP_ARM_PROTECTED_VM: u32 = 0xffbadab1;
|
||||
pub const KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE: u32 = 0;
|
||||
pub const KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO: u32 = 1;
|
||||
pub const KVM_IRQ_ROUTING_IRQCHIP: u32 = 1;
|
||||
pub const KVM_IRQ_ROUTING_MSI: u32 = 2;
|
||||
pub const KVM_IRQ_ROUTING_S390_ADAPTER: u32 = 3;
|
||||
|
|
|
@ -568,6 +568,10 @@ pub const KVM_CAP_ARM_PTRAUTH_GENERIC: u32 = 172;
|
|||
pub const KVM_CAP_PMU_EVENT_FILTER: u32 = 173;
|
||||
pub const KVM_CAP_ARM_IRQ_LINE_LAYOUT_2: u32 = 174;
|
||||
pub const KVM_CAP_HYPERV_DIRECT_TLBFLUSH: u32 = 175;
|
||||
// TODO(qwandor): Update this once the pKVM patches are merged upstream with a stable capability ID.
|
||||
pub const KVM_CAP_ARM_PROTECTED_VM: u32 = 0xffbadab1;
|
||||
pub const KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE: u32 = 0;
|
||||
pub const KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO: u32 = 1;
|
||||
pub const KVM_IRQ_ROUTING_IRQCHIP: u32 = 1;
|
||||
pub const KVM_IRQ_ROUTING_MSI: u32 = 2;
|
||||
pub const KVM_IRQ_ROUTING_S390_ADAPTER: u32 = 3;
|
||||
|
|
|
@ -25,6 +25,7 @@ use devices::virtio::fs::passthrough;
|
|||
use devices::virtio::gpu::GpuParameters;
|
||||
#[cfg(feature = "audio")]
|
||||
use devices::Ac97Parameters;
|
||||
use devices::ProtectionType;
|
||||
use libc::{getegid, geteuid};
|
||||
use vm_control::BatteryType;
|
||||
|
||||
|
@ -223,7 +224,7 @@ pub struct Config {
|
|||
pub video_dec: bool,
|
||||
pub video_enc: bool,
|
||||
pub acpi_tables: Vec<PathBuf>,
|
||||
pub protected_vm: bool,
|
||||
pub protected_vm: ProtectionType,
|
||||
pub battery_type: Option<BatteryType>,
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
pub gdb: Option<u32>,
|
||||
|
@ -283,7 +284,7 @@ impl Default for Config {
|
|||
video_dec: false,
|
||||
video_enc: false,
|
||||
acpi_tables: Vec::new(),
|
||||
protected_vm: false,
|
||||
protected_vm: ProtectionType::Unprotected,
|
||||
battery_type: None,
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
gdb: None,
|
||||
|
|
|
@ -34,6 +34,7 @@ use crosvm::{
|
|||
};
|
||||
#[cfg(feature = "gpu")]
|
||||
use devices::virtio::gpu::{GpuMode, GpuParameters};
|
||||
use devices::ProtectionType;
|
||||
#[cfg(feature = "audio")]
|
||||
use devices::{Ac97Backend, Ac97Parameters};
|
||||
use disk::QcowFile;
|
||||
|
@ -1505,7 +1506,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
cfg.acpi_tables.push(acpi_table);
|
||||
}
|
||||
"protected-vm" => {
|
||||
cfg.protected_vm = true;
|
||||
cfg.protected_vm = ProtectionType::Protected;
|
||||
cfg.params.push("swiotlb=force".to_string());
|
||||
}
|
||||
"battery" => {
|
||||
|
|
|
@ -61,7 +61,7 @@ use arch::{
|
|||
VmComponents, VmImage,
|
||||
};
|
||||
use base::Event;
|
||||
use devices::{IrqChip, IrqChipX86_64, PciConfigIo, PciDevice};
|
||||
use devices::{IrqChip, IrqChipX86_64, PciConfigIo, PciDevice, ProtectionType};
|
||||
use hypervisor::{HypervisorX86_64, VcpuX86_64, VmX86_64};
|
||||
use minijail::Minijail;
|
||||
use remain::sorted;
|
||||
|
@ -124,6 +124,7 @@ pub enum Error {
|
|||
SetupSmbios(smbios::Error),
|
||||
SetupSregs(regs::Error),
|
||||
TranslatingVirtAddr,
|
||||
UnsupportedProtectionType,
|
||||
WriteRegs(base::Error),
|
||||
WritingGuestMemory(GuestMemoryError),
|
||||
ZeroPagePastRamEnd,
|
||||
|
@ -183,6 +184,7 @@ impl Display for Error {
|
|||
SetupSmbios(e) => write!(f, "failed to set up SMBIOS: {}", e),
|
||||
SetupSregs(e) => write!(f, "failed to set up sregs: {}", e),
|
||||
TranslatingVirtAddr => write!(f, "failed to translate virtual address"),
|
||||
UnsupportedProtectionType => write!(f, "protected VMs not supported on x86_64"),
|
||||
WriteRegs(e) => write!(f, "error writing CPU registers {}", e),
|
||||
WritingGuestMemory(e) => write!(f, "error writing guest memory {}", e),
|
||||
ZeroPagePastRamEnd => write!(f, "the zero page extends past the end of guest_mem"),
|
||||
|
@ -372,6 +374,10 @@ impl arch::LinuxArch for X8664arch {
|
|||
E2: StdError + 'static,
|
||||
E3: StdError + 'static,
|
||||
{
|
||||
if components.protected_vm != ProtectionType::Unprotected {
|
||||
return Err(Error::UnsupportedProtectionType);
|
||||
}
|
||||
|
||||
let bios_size = match components.vm_image {
|
||||
VmImage::Bios(ref mut bios_file) => {
|
||||
Some(bios_file.metadata().map_err(Error::LoadBios)?.len())
|
||||
|
@ -1124,7 +1130,7 @@ impl X8664arch {
|
|||
/// * - `io_bus` the I/O bus to add the devices to
|
||||
/// * - `serial_parmaters` - definitions for how the serial devices should be configured
|
||||
fn setup_serial_devices(
|
||||
protected_vm: bool,
|
||||
protected_vm: ProtectionType,
|
||||
irq_chip: &mut impl IrqChip,
|
||||
io_bus: &mut devices::Bus,
|
||||
serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
|
||||
use devices::IrqChipX86_64;
|
||||
use devices::{IrqChipX86_64, ProtectionType};
|
||||
use hypervisor::{HypervisorX86_64, VcpuExit, VcpuX86_64, VmX86_64};
|
||||
use vm_memory::{GuestAddress, GuestMemory};
|
||||
|
||||
|
@ -158,8 +158,14 @@ where
|
|||
|
||||
arch::set_default_serial_parameters(&mut serial_params);
|
||||
|
||||
X8664arch::setup_serial_devices(false, &mut irq_chip, &mut io_bus, &serial_params, None)
|
||||
.unwrap();
|
||||
X8664arch::setup_serial_devices(
|
||||
ProtectionType::Unprotected,
|
||||
&mut irq_chip,
|
||||
&mut io_bus,
|
||||
&serial_params,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let param_args = "nokaslr";
|
||||
|
||||
|
|
Loading…
Reference in a new issue