mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 12:34:31 +00:00
allow plugin to query for KVM extensions
The guest may need to check for KVM extensions before blindly using them. TEST=cargo test --features plugin; cargo test -p kvm; ./build_test BUG=chromium:800626 Change-Id: If87b928753cd71adeabac4fc7732c3fce7265834 Reviewed-on: https://chromium-review.googlesource.com/906008 Commit-Ready: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
parent
1d4a70029c
commit
dadb7625ea
10 changed files with 151 additions and 22 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -18,7 +18,7 @@ name = "crosvm"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crosvm_plugin 0.9.0",
|
||||
"crosvm_plugin 0.10.0",
|
||||
"data_model 0.1.0",
|
||||
"devices 0.1.0",
|
||||
"io_jail 0.1.0",
|
||||
|
@ -28,7 +28,7 @@ dependencies = [
|
|||
"kvm_sys 0.1.0",
|
||||
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net_util 0.1.0",
|
||||
"plugin_proto 0.9.0",
|
||||
"plugin_proto 0.10.0",
|
||||
"protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"qcow 0.1.0",
|
||||
"qcow_utils 0.1.0",
|
||||
|
@ -41,12 +41,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crosvm_plugin"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"kvm 0.1.0",
|
||||
"kvm_sys 0.1.0",
|
||||
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plugin_proto 0.9.0",
|
||||
"plugin_proto 0.10.0",
|
||||
"protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sys_util 0.1.0",
|
||||
]
|
||||
|
@ -160,7 +160,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "plugin_proto"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "crosvm_plugin"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -47,7 +47,7 @@ extern "C" {
|
|||
* do not indicate anything about what version of crosvm is running.
|
||||
*/
|
||||
#define CROSVM_API_MAJOR 0
|
||||
#define CROSVM_API_MINOR 9
|
||||
#define CROSVM_API_MINOR 10
|
||||
#define CROSVM_API_PATCH 0
|
||||
|
||||
enum crosvm_address_space {
|
||||
|
@ -108,6 +108,12 @@ int crosvm_destroy_connection(struct crosvm**);
|
|||
*/
|
||||
int crosvm_get_shutdown_eventfd(struct crosvm*);
|
||||
|
||||
/*
|
||||
* Gets a bool indicating if a KVM_CAP_* enum is supported on this VM
|
||||
*/
|
||||
int crosvm_check_extension(struct crosvm*, uint32_t __extension,
|
||||
bool *__has_extension);
|
||||
|
||||
/*
|
||||
* Registers a range in the given address space that, when accessed, will block
|
||||
* and wait for a crosvm_vcpu_resume call.
|
||||
|
|
|
@ -233,6 +233,16 @@ impl crosvm {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_extension(&mut self, extension: u32) -> result::Result<bool, c_int> {
|
||||
let mut r = MainRequest::new();
|
||||
r.mut_check_extension().extension = extension;
|
||||
let (response, _) = self.main_transaction(&r, &[])?;
|
||||
if !response.has_check_extension() {
|
||||
return Err(-EPROTO);
|
||||
}
|
||||
Ok(response.get_check_extension().has_extension)
|
||||
}
|
||||
|
||||
fn reserve_range(&mut self, space: u32, start: u64, length: u64) -> result::Result<(), c_int> {
|
||||
let mut r = MainRequest::new();
|
||||
{
|
||||
|
@ -809,6 +819,21 @@ pub unsafe extern "C" fn crosvm_get_shutdown_eventfd(self_: *mut crosvm) -> c_in
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn crosvm_check_extension(self_: *mut crosvm,
|
||||
extension: u32,
|
||||
has_extension: *mut bool)
|
||||
-> c_int {
|
||||
let self_ = &mut (*self_);
|
||||
match self_.check_extension(extension) {
|
||||
Ok(supported) => {
|
||||
*has_extension = supported;
|
||||
0
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn crosvm_reserve_range(self_: *mut crosvm,
|
||||
space: u32,
|
||||
|
|
|
@ -223,6 +223,18 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if a particular `Cap` is available.
|
||||
///
|
||||
/// This is distinct from the `Kvm` version of this method because the some extensions depend on
|
||||
/// the particular `Vm` existence. This method is encouraged by the kernel because it more
|
||||
/// accurately reflects the usable capabilities.
|
||||
pub fn check_extension(&self, c: Cap) -> bool {
|
||||
// Safe because we know that our file is a KVM fd and that the extension is one of the ones
|
||||
// defined by kernel.
|
||||
unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), c as c_ulong) == 1 }
|
||||
}
|
||||
|
||||
|
||||
/// Inserts the given `MemoryMapping` into the VM's address space at `guest_addr`.
|
||||
///
|
||||
/// The slot that was assigned the device memory mapping is returned on success. The slot can be
|
||||
|
@ -1022,6 +1034,16 @@ mod tests {
|
|||
assert!(!kvm.check_extension(Cap::S390UserSigp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vm_extension() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
|
||||
let vm = Vm::new(&kvm, gm).unwrap();
|
||||
assert!(vm.check_extension(Cap::UserMemory));
|
||||
// I assume nobody is testing this on s390
|
||||
assert!(!vm.check_extension(Cap::S390UserSigp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_memory() {
|
||||
let kvm = Kvm::new().unwrap();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "plugin_proto"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
authors = ["The Chromium OS Authors"]
|
||||
build = "build.rs"
|
||||
|
||||
|
|
|
@ -68,6 +68,10 @@ message MainRequest {
|
|||
|
||||
message GetShutdownEventfd {}
|
||||
|
||||
message CheckExtension {
|
||||
uint32 extension = 1;
|
||||
}
|
||||
|
||||
message ReserveRange {
|
||||
AddressSpace space = 1;
|
||||
uint64 start = 2;
|
||||
|
@ -126,13 +130,14 @@ message MainRequest {
|
|||
Destroy destroy = 2;
|
||||
NewConnection new_connection = 3;
|
||||
GetShutdownEventfd get_shutdown_eventfd = 4;
|
||||
ReserveRange reserve_range = 5;
|
||||
SetIrq set_irq = 6;
|
||||
SetIrqRouting set_irq_routing = 7;
|
||||
SetIdentityMapAddr set_identity_map_addr = 8;
|
||||
PauseVcpus pause_vcpus = 9;
|
||||
GetVcpus get_vcpus = 10;
|
||||
Start start = 11;
|
||||
CheckExtension check_extension = 5;
|
||||
ReserveRange reserve_range = 6;
|
||||
SetIrq set_irq = 7;
|
||||
SetIrqRouting set_irq_routing = 8;
|
||||
SetIdentityMapAddr set_identity_map_addr = 9;
|
||||
PauseVcpus pause_vcpus = 10;
|
||||
GetVcpus get_vcpus = 11;
|
||||
Start start = 12;
|
||||
// Method for a Memory type object for retrieving the dirty bitmap. Only valid if the memory
|
||||
// object was created with dirty_log set.
|
||||
MemoryDirtyLog dirty_log = 101;
|
||||
|
@ -148,6 +153,9 @@ message MainResponse {
|
|||
// requests and responses independent of the other sockets.
|
||||
message NewConnection {}
|
||||
message GetShutdownEventfd {}
|
||||
message CheckExtension {
|
||||
bool has_extension = 1;
|
||||
}
|
||||
message ReserveRange {}
|
||||
message SetIrq {}
|
||||
message SetIrqRouting {}
|
||||
|
@ -170,13 +178,14 @@ message MainResponse {
|
|||
Destroy destroy = 3;
|
||||
NewConnection new_connection = 4;
|
||||
GetShutdownEventfd get_shutdown_eventfd = 5;
|
||||
ReserveRange reserve_range = 6;
|
||||
SetIrq set_irq = 7;
|
||||
SetIrqRouting set_irq_routing = 8;
|
||||
SetIdentityMapAddr set_identity_map_addr = 9;
|
||||
PauseVcpus pause_vcpus = 10;
|
||||
GetVcpus get_vcpus = 11;
|
||||
Start start = 12;
|
||||
CheckExtension check_extension = 6;
|
||||
ReserveRange reserve_range = 7;
|
||||
SetIrq set_irq = 8;
|
||||
SetIrqRouting set_irq_routing = 9;
|
||||
SetIdentityMapAddr set_identity_map_addr = 10;
|
||||
PauseVcpus pause_vcpus = 11;
|
||||
GetVcpus get_vcpus = 12;
|
||||
Start start = 13;
|
||||
MemoryDirtyLog dirty_log = 101;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use std::collections::hash_map::{HashMap, Entry, VacantEntry};
|
||||
use std::env::set_var;
|
||||
use std::fs::File;
|
||||
use std::mem::transmute;
|
||||
use std::net::Shutdown;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::os::unix::net::UnixDatagram;
|
||||
|
@ -483,6 +484,14 @@ impl Process {
|
|||
response.mut_get_shutdown_eventfd();
|
||||
response_fds.push(self.kill_evt.as_raw_fd());
|
||||
Ok(())
|
||||
} else if request.has_check_extension() {
|
||||
// Safe because the Cap enum is not read by the check_extension method. In that method,
|
||||
// cap is cast back to an integer and fed to an ioctl. If the extension name is actually
|
||||
// invalid, the kernel will safely reject the extension under the assumption that the
|
||||
// capability is legitimately unsupported.
|
||||
let cap = unsafe { transmute(request.get_check_extension().extension) };
|
||||
response.mut_check_extension().has_extension = vm.check_extension(cap);
|
||||
Ok(())
|
||||
} else if request.has_reserve_range() {
|
||||
response.mut_reserve_range();
|
||||
self.handle_reserve_range(request.get_reserve_range())
|
||||
|
|
53
tests/plugin_extensions.c
Normal file
53
tests/plugin_extensions.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "crosvm.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
struct crosvm *crosvm;
|
||||
int ret = crosvm_connect(&crosvm);
|
||||
if (ret) {
|
||||
fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool supported;
|
||||
ret = crosvm_check_extension(crosvm, KVM_CAP_IRQCHIP, &supported);
|
||||
if (ret) {
|
||||
fprintf(stderr, "failed to check for KVM extension: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
if (!supported) {
|
||||
fprintf(stderr, "expected KVM extension to be supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Assume s390 extensions aren't supported because we shouldn't be running on one.
|
||||
ret = crosvm_check_extension(crosvm, KVM_CAP_S390_PSW, &supported);
|
||||
if (ret) {
|
||||
fprintf(stderr, "failed to check for KVM extension: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
if (supported) {
|
||||
fprintf(stderr, "unexpected KVM extension is supported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -225,6 +225,11 @@ fn test_irqfd() {
|
|||
test_plugin(include_str!("plugin_irqfd.c"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extensions() {
|
||||
test_plugin(include_str!("plugin_extensions.c"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debugregs() {
|
||||
let mini_plugin = MiniPlugin {
|
||||
|
|
Loading…
Reference in a new issue