devices: virtio: Advertise VIRTIO_F_ACCESS_PLATFORM for protected VMs

Virtio 1.1 introduces the VIRTIO_F_ACCESS_PLATFORM reserved feature bit,
which allows the guest operating system to discover that a virtio device
has limited access to memory. In the case of Linux, this forces the use
of the DMA API for virtio transfers, which in turn can bounce data
through a shared window that is negotiated between the guest and the
hypervisor.

Advertise the VIRTIO_F_ACCESS_PLATFORM reserved feature bit when crosvm
is running with the '--protected-vm' option.

BUG=None
TEST=./build_test

Change-Id: I78e8d9e78999790059639b64611b8081c39d24ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2453560
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Will Deacon 2020-10-06 18:51:12 +01:00 committed by Commit Bot
parent 0405973907
commit 7d2b8ac342
13 changed files with 53 additions and 25 deletions

View file

@ -273,6 +273,7 @@ impl arch::LinuxArch for AArch64 {
let com_evt_1_3 = Event::new().map_err(Error::CreateEvent)?;
let com_evt_2_4 = Event::new().map_err(Error::CreateEvent)?;
arch::add_serial_devices(
components.protected_vm,
&mut mmio_bus,
&com_evt_1_3,
&com_evt_2_4,

View file

@ -83,6 +83,7 @@ pub struct VmComponents {
pub wayland_dmabuf: bool,
pub acpi_sdts: Vec<SDT>,
pub rt_cpus: Vec<usize>,
pub protected_vm: bool,
}
/// Holds the elements needed to run a Linux VM. Created by `build_vm`.

View file

@ -224,6 +224,7 @@ impl SerialParameters {
/// process. `evt` will always be added to this vector by this function.
pub fn create_serial_device<T: SerialDevice>(
&self,
protected_vm: bool,
evt: &Event,
keep_fds: &mut Vec<RawFd>,
) -> std::result::Result<T, Error> {
@ -287,7 +288,7 @@ impl SerialParameters {
None => return Err(Error::PathRequired),
},
};
Ok(T::new(evt, input, output, keep_fds.to_vec()))
Ok(T::new(protected_vm, evt, input, output, keep_fds.to_vec()))
}
pub fn add_bind_mounts(&self, jail: &mut Minijail) -> Result<(), minijail::Error> {
@ -371,6 +372,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,
io_bus: &mut Bus,
com_evt_1_3: &Event,
com_evt_2_4: &Event,
@ -392,7 +394,7 @@ pub fn add_serial_devices(
let mut preserved_fds = Vec::new();
let com = param
.create_serial_device::<Serial>(&com_evt, &mut preserved_fds)
.create_serial_device::<Serial>(protected_vm, &com_evt, &mut preserved_fds)
.map_err(DeviceRegistrationError::CreateSerialDevice)?;
match serial_jail.as_ref() {

View file

@ -84,6 +84,7 @@ pub struct Serial {
impl SerialDevice for Serial {
fn new(
_protected_vm: bool,
interrupt_evt: Event,
input: Option<Box<dyn io::Read + Send>>,
out: Option<Box<dyn io::Write + Send>>,
@ -409,6 +410,7 @@ mod tests {
let serial_out = SharedBuffer::new();
let mut serial = Serial::new(
false,
intr_evt,
None,
Some(Box::new(serial_out.clone())),
@ -430,6 +432,7 @@ mod tests {
let serial_out = SharedBuffer::new();
let mut serial = Serial::new(
false,
intr_evt.try_clone().unwrap(),
None,
Some(Box::new(serial_out.clone())),

View file

@ -11,6 +11,7 @@ use base::Event;
/// output streams.
pub trait SerialDevice {
fn new(
protected_vm: bool,
interrupt_evt: Event,
input: Option<Box<dyn io::Read + Send>>,
output: Option<Box<dyn io::Write + Send>>,

View file

@ -848,7 +848,7 @@ mod tests {
let f = tempfile().unwrap();
f.set_len(0x1000).unwrap();
let features = base_features();
let features = base_features(false);
let b = Block::new(features, Box::new(f), true, false, 512, None).unwrap();
let mut num_sectors = [0u8; 4];
b.read_config(0, &mut num_sectors);
@ -865,7 +865,7 @@ mod tests {
let f = tempfile().unwrap();
f.set_len(0x1000).unwrap();
let features = base_features();
let features = base_features(false);
let b = Block::new(features, Box::new(f), true, false, 4096, None).unwrap();
let mut blk_size = [0u8; 4];
b.read_config(20, &mut blk_size);
@ -878,7 +878,7 @@ mod tests {
// read-write block device
{
let f = tempfile().unwrap();
let features = base_features();
let features = base_features(false);
let b = Block::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
@ -889,7 +889,7 @@ mod tests {
// read-write block device, non-sparse
{
let f = tempfile().unwrap();
let features = base_features();
let features = base_features(false);
let b = Block::new(features, Box::new(f), false, false, 512, None).unwrap();
// writable device should set VIRTIO_BLK_F_FLUSH
// + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 + VIRTIO_BLK_F_BLK_SIZE
@ -900,7 +900,7 @@ mod tests {
// read-only block device
{
let f = tempfile().unwrap();
let features = base_features();
let features = base_features(false);
let b = Block::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

@ -309,13 +309,14 @@ pub struct Console {
impl SerialDevice for Console {
fn new(
protected_vm: bool,
_evt: Event,
input: Option<Box<dyn io::Read + Send>>,
output: Option<Box<dyn io::Write + Send>>,
keep_fds: Vec<RawFd>,
) -> Console {
Console {
base_features: base_features(),
base_features: base_features(protected_vm),
kill_evt: None,
worker_thread: None,
input,

View file

@ -88,6 +88,7 @@ const TYPE_WL: u32 = MAX_VIRTIO_DEVICE_ID;
const TYPE_TPM: u32 = MAX_VIRTIO_DEVICE_ID - 1;
const VIRTIO_F_VERSION_1: u32 = 32;
const VIRTIO_F_ACCESS_PLATFORM: u32 = 33;
const INTERRUPT_STATUS_USED_RING: u32 = 0x1;
const INTERRUPT_STATUS_CONFIG_CHANGED: u32 = 0x2;
@ -151,6 +152,12 @@ 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() -> u64 {
1 << VIRTIO_F_VERSION_1
pub fn base_features(protected_vm: bool) -> u64 {
let mut features: u64 = 1 << VIRTIO_F_VERSION_1;
if protected_vm {
features |= 1 << VIRTIO_F_ACCESS_PLATFORM;
}
features
}

View file

@ -364,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();
let features = base_features(false);
Net::<FakeTap, FakeNet<FakeTap>>::new(
features,
Ipv4Addr::new(127, 0, 0, 1),

View file

@ -209,6 +209,7 @@ pub struct Config {
pub video_dec: bool,
pub video_enc: bool,
pub acpi_tables: Vec<PathBuf>,
pub protected_vm: bool,
}
impl Default for Config {
@ -263,6 +264,7 @@ impl Default for Config {
video_dec: false,
video_enc: false,
acpi_tables: Vec::new(),
protected_vm: false,
}
}
}

View file

@ -460,7 +460,7 @@ fn create_block_device(
let disk_file = disk::create_disk_file(raw_image).map_err(Error::CreateDiskError)?;
let dev = virtio::Block::new(
virtio::base_features(),
virtio::base_features(cfg.protected_vm),
disk_file,
disk.read_only,
disk.sparse,
@ -609,8 +609,8 @@ fn create_vinput_device(cfg: &Config, dev_path: &Path) -> DeviceResult {
}
fn create_balloon_device(cfg: &Config, socket: BalloonControlResponseSocket) -> DeviceResult {
let dev =
virtio::Balloon::new(virtio::base_features(), socket).map_err(Error::BalloonDeviceNew)?;
let dev = virtio::Balloon::new(virtio::base_features(cfg.protected_vm), socket)
.map_err(Error::BalloonDeviceNew)?;
Ok(VirtioDeviceStub {
dev: Box::new(dev),
@ -631,7 +631,7 @@ fn create_tap_net_device(cfg: &Config, tap_fd: RawFd) -> DeviceResult {
error!("net vq pairs must be smaller than vcpu count, fall back to single queue mode");
vq_pairs = 1;
}
let features = virtio::base_features();
let features = virtio::base_features(cfg.protected_vm);
let dev = virtio::Net::from(features, tap, vq_pairs).map_err(Error::NetDeviceNew)?;
Ok(VirtioDeviceStub {
@ -654,7 +654,7 @@ fn create_net_device(
vq_pairs = 1;
}
let features = virtio::base_features();
let features = virtio::base_features(cfg.protected_vm);
let dev = if cfg.vhost_net {
let dev = virtio::vhost::Net::<Tap, vhost::Net<Tap>>::new(
features,
@ -722,7 +722,7 @@ fn create_gpu_device(
event_devices,
map_request,
cfg.sandbox,
virtio::base_features(),
virtio::base_features(cfg.protected_vm),
);
let jail = match simple_jail(&cfg, "gpu_device")? {
@ -832,7 +832,7 @@ fn create_wayland_device(
.collect::<Option<Vec<_>>>()
.ok_or(Error::InvalidWaylandPath)?;
let features = virtio::base_features();
let features = virtio::base_features(cfg.protected_vm);
let dev = virtio::Wl::new(
features,
cfg.wayland_socket_paths.clone(),
@ -929,7 +929,7 @@ fn create_video_device(
Ok(VirtioDeviceStub {
dev: Box::new(devices::virtio::VideoDevice::new(
virtio::base_features(),
virtio::base_features(cfg.protected_vm),
typ,
Some(resource_bridge),
)),
@ -952,7 +952,7 @@ fn register_video_device(
}
fn create_vhost_vsock_device(cfg: &Config, cid: u64, mem: &GuestMemory) -> DeviceResult {
let features = virtio::base_features();
let features = virtio::base_features(cfg.protected_vm);
let dev = virtio::vhost::Vsock::new(features, cid, mem).map_err(Error::VhostVsockDeviceNew)?;
Ok(VirtioDeviceStub {
@ -989,7 +989,7 @@ fn create_fs_device(
create_base_minijail(src, Some(max_open_files), None)?
};
let features = virtio::base_features();
let features = virtio::base_features(cfg.protected_vm);
// TODO(chirantan): Use more than one worker once the kernel driver has been fixed to not panic
// when num_queues > 1.
let dev = virtio::fs::Fs::new(features, tag, 1, fs_cfg).map_err(Error::FsDeviceNew)?;
@ -1032,7 +1032,7 @@ fn create_9p_device(
(None, src)
};
let features = virtio::base_features();
let features = virtio::base_features(cfg.protected_vm);
let dev = virtio::P9::new(features, root, tag).map_err(Error::P9DeviceNew)?;
Ok(VirtioDeviceStub {
@ -1115,7 +1115,7 @@ fn create_pmem_device(
.map_err(Error::AddPmemDeviceMemory)?;
let dev = virtio::Pmem::new(
virtio::base_features(),
virtio::base_features(cfg.protected_vm),
fd,
GuestAddress(mapping_address),
slot,
@ -1134,7 +1134,7 @@ fn create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult
let mut keep_fds = Vec::new();
let evt = Event::new().map_err(Error::CreateEvent)?;
let dev = param
.create_serial_device::<Console>(&evt, &mut keep_fds)
.create_serial_device::<Console>(cfg.protected_vm, &evt, &mut keep_fds)
.map_err(Error::CreateConsole)?;
let jail = match simple_jail(&cfg, "serial")? {
@ -1981,6 +1981,7 @@ where
.map(|path| SDT::from_file(path).map_err(|e| Error::OpenAcpiTable(path.clone(), e)))
.collect::<Result<Vec<SDT>>>()?,
rt_cpus: cfg.rt_cpus.clone(),
protected_vm: cfg.protected_vm,
};
let control_server_socket = match &cfg.socket_path {

View file

@ -1378,6 +1378,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.params.push("swiotlb=force".to_string());
}

View file

@ -387,7 +387,13 @@ impl arch::LinuxArch for X8664arch {
components.memory_size,
)?;
Self::setup_serial_devices(&mut irq_chip, &mut io_bus, serial_parameters, serial_jail)?;
Self::setup_serial_devices(
components.protected_vm,
&mut irq_chip,
&mut io_bus,
serial_parameters,
serial_jail,
)?;
let acpi_dev_resource = Self::setup_acpi_devices(
&mut io_bus,
@ -824,6 +830,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,
irq_chip: &mut impl IrqChip,
io_bus: &mut devices::Bus,
serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
@ -833,6 +840,7 @@ impl X8664arch {
let com_evt_2_4 = Event::new().map_err(Error::CreateEvent)?;
arch::add_serial_devices(
protected_vm,
io_bus,
&com_evt_1_3,
&com_evt_2_4,