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:
Andrew Walbran 2021-01-08 13:29:03 +00:00 committed by Commit Bot
parent 9b006f13c8
commit 413f854564
24 changed files with 190 additions and 58 deletions

View file

@ -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);

View file

@ -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.
}

View file

@ -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,

View file

@ -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,
}

View file

@ -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())),

View file

@ -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>>,

View file

@ -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

View file

@ -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

View file

@ -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>>,

View file

@ -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;
}

View file

@ -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),

View file

@ -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 =

View file

@ -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>>;
}

View file

@ -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,
}

View file

@ -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.

View file

@ -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]

View file

@ -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

View file

@ -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,
}

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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" => {

View file

@ -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>,

View file

@ -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";