summary refs log tree commit diff
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@chromium.org>2018-02-26 15:17:53 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-02-28 21:30:20 -0800
commit9786573e07992d43bfd5304ca7490a35aca2c61f (patch)
treef1082dcd1d1121ccdaf850251384825e1baab6f3
parentd1c761354f5d2583763704c8f9a8a7dd61d1dbb7 (diff)
downloadcrosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.tar
crosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.tar.gz
crosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.tar.bz2
crosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.tar.lz
crosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.tar.xz
crosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.tar.zst
crosvm-9786573e07992d43bfd5304ca7490a35aca2c61f.zip
allow plugin to query KVM for supported/emulated CPUIDs
This plumbs calls to KVM_GET_SUPPORTED_CPUID and KVM_GET_EMULATED_CPUID
to be available to plugins.

TEST=cargo test --features plugin; cargo test -p kvm
BUG=chromium:800626

Change-Id: I98879599b5f970c6c2720772658689a505d8abe1
Signed-off-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/938674
Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r--Cargo.lock10
-rw-r--r--crosvm_plugin/Cargo.toml2
-rw-r--r--crosvm_plugin/crosvm.h17
-rw-r--r--crosvm_plugin/src/lib.rs86
-rw-r--r--plugin_proto/Cargo.toml2
-rw-r--r--plugin_proto/protos/plugin.proto38
-rw-r--r--src/plugin/mod.rs4
-rw-r--r--src/plugin/process.rs30
-rw-r--r--tests/plugin_supported_cpuid.c67
-rw-r--r--tests/plugins.rs5
10 files changed, 233 insertions, 28 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fa6f505..fda25e9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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.10.0",
+ "crosvm_plugin 0.11.0",
  "data_model 0.1.0",
  "device_manager 0.1.0",
  "devices 0.1.0",
@@ -29,7 +29,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.10.0",
+ "plugin_proto 0.11.0",
  "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "qcow 0.1.0",
  "qcow_utils 0.1.0",
@@ -42,12 +42,12 @@ dependencies = [
 
 [[package]]
 name = "crosvm_plugin"
-version = "0.10.0"
+version = "0.11.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.10.0",
+ "plugin_proto 0.11.0",
  "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "sys_util 0.1.0",
 ]
@@ -173,7 +173,7 @@ dependencies = [
 
 [[package]]
 name = "plugin_proto"
-version = "0.10.0"
+version = "0.11.0"
 dependencies = [
  "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
  "kvm_sys 0.1.0",
diff --git a/crosvm_plugin/Cargo.toml b/crosvm_plugin/Cargo.toml
index 4020f3a..d34753a 100644
--- a/crosvm_plugin/Cargo.toml
+++ b/crosvm_plugin/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "crosvm_plugin"
-version = "0.10.0"
+version = "0.11.0"
 authors = ["The Chromium OS Authors"]
 
 [lib]
diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h
index 454ba34..0dc2a60 100644
--- a/crosvm_plugin/crosvm.h
+++ b/crosvm_plugin/crosvm.h
@@ -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 10
+#define CROSVM_API_MINOR 11
 #define CROSVM_API_PATCH 0
 
 enum crosvm_address_space {
@@ -115,6 +115,21 @@ int crosvm_check_extension(struct crosvm*, uint32_t __extension,
                            bool *__has_extension);
 
 /*
+ * Queries x86 cpuid features which are supported by the hardware and
+ * kvm.
+ */
+int crosvm_get_supported_cpuid(struct crosvm*, uint32_t __entry_count,
+                               struct kvm_cpuid_entry2 *__cpuid_entries,
+                               uint32_t *__out_count);
+
+/*
+ * Queries x86 cpuid features which are emulated by kvm.
+ */
+int crosvm_get_emulated_cpuid(struct crosvm*, uint32_t __entry_count,
+                              struct kvm_cpuid_entry2 *__cpuid_entries,
+                              uint32_t *__out_count);
+
+/*
  * Registers a range in the given address space that, when accessed, will block
  * and wait for a crosvm_vcpu_resume call.
  *
diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs
index 250692a..a794d40 100644
--- a/crosvm_plugin/src/lib.rs
+++ b/crosvm_plugin/src/lib.rs
@@ -35,7 +35,7 @@ use std::slice;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 
-use libc::{ENOTCONN, EINVAL, EPROTO, ENOENT};
+use libc::{E2BIG, ENOTCONN, EINVAL, EPROTO, ENOENT};
 
 use protobuf::{Message, ProtobufEnum, RepeatedField, parse_from_bytes};
 
@@ -242,6 +242,54 @@ impl crosvm {
         Ok(response.get_check_extension().has_extension)
     }
 
+    fn get_supported_cpuid(&mut self, cpuid_entries: &mut [kvm_cpuid_entry2])
+                           -> result::Result<usize, c_int> {
+        let mut r = MainRequest::new();
+        r.mut_get_supported_cpuid();
+
+        let (response, _) = self.main_transaction(&r, &[])?;
+        if !response.has_get_supported_cpuid() {
+            return Err(-EPROTO);
+        }
+
+        let supported_cpuids: &MainResponse_CpuidResponse = response.get_get_supported_cpuid();
+        if supported_cpuids.get_entries().len() > cpuid_entries.len() {
+            return Err(-E2BIG);
+        }
+
+        for (proto_entry, kvm_entry) in
+            supported_cpuids.get_entries().iter()
+                .zip(cpuid_entries.iter_mut()) {
+            *kvm_entry = cpuid_proto_to_kvm(proto_entry);
+        }
+
+        Ok(supported_cpuids.get_entries().len())
+    }
+
+    fn get_emulated_cpuid(&mut self, cpuid_entries: &mut [kvm_cpuid_entry2])
+                           -> result::Result<usize, c_int> {
+        let mut r = MainRequest::new();
+        r.mut_get_emulated_cpuid();
+
+        let (response, _) = self.main_transaction(&r, &[])?;
+        if !response.has_get_emulated_cpuid() {
+            return Err(-EPROTO);
+        }
+
+        let emulated_cpuids: &MainResponse_CpuidResponse = response.get_get_emulated_cpuid();
+        if emulated_cpuids.get_entries().len() > cpuid_entries.len() {
+            return Err(-E2BIG);
+        }
+
+        for (proto_entry, kvm_entry) in
+            emulated_cpuids.get_entries().iter()
+                .zip(cpuid_entries.iter_mut()) {
+            *kvm_entry = cpuid_proto_to_kvm(proto_entry);
+        }
+
+        Ok(emulated_cpuids.get_entries().len())
+    }
+
     fn reserve_range(&mut self, space: u32, start: u64, length: u64) -> result::Result<(), c_int> {
         let mut r = MainRequest::new();
         {
@@ -826,6 +874,42 @@ pub unsafe extern "C" fn crosvm_check_extension(self_: *mut crosvm,
 }
 
 #[no_mangle]
+pub unsafe extern "C"
+fn crosvm_get_supported_cpuid(this: *mut crosvm,
+                              entry_count: u32,
+                              cpuid_entries: *mut kvm_cpuid_entry2,
+                              out_count: *mut u32)
+                              -> c_int {
+    let this = &mut *this;
+    let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
+    match this.get_supported_cpuid(cpuid_entries) {
+        Ok(num) => {
+            *out_count = num as u32;
+            0
+        },
+        Err(e) => e,
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C"
+fn crosvm_get_emulated_cpuid(this: *mut crosvm,
+                             entry_count: u32,
+                             cpuid_entries: *mut kvm_cpuid_entry2,
+                             out_count: *mut u32)
+                             -> c_int {
+    let this = &mut *this;
+    let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
+    match this.get_emulated_cpuid(cpuid_entries) {
+        Ok(num) => {
+            *out_count = num as u32;
+            0
+        },
+        Err(e) => e,
+    }
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn crosvm_reserve_range(self_: *mut crosvm,
                                               space: u32,
                                               start: u64,
diff --git a/plugin_proto/Cargo.toml b/plugin_proto/Cargo.toml
index bb43452..9ddf1a4 100644
--- a/plugin_proto/Cargo.toml
+++ b/plugin_proto/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "plugin_proto"
-version = "0.10.0"
+version = "0.11.0"
 authors = ["The Chromium OS Authors"]
 build = "build.rs"
 
diff --git a/plugin_proto/protos/plugin.proto b/plugin_proto/protos/plugin.proto
index 194b315..0e6f883 100644
--- a/plugin_proto/protos/plugin.proto
+++ b/plugin_proto/protos/plugin.proto
@@ -72,6 +72,9 @@ message MainRequest {
         uint32 extension = 1;
     }
 
+    message CpuidRequest {
+    }
+
     message ReserveRange {
         AddressSpace space = 1;
         uint64 start = 2;
@@ -131,13 +134,15 @@ message MainRequest {
         NewConnection new_connection = 3;
         GetShutdownEventfd get_shutdown_eventfd = 4;
         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;
+        CpuidRequest get_supported_cpuid = 6;
+        CpuidRequest get_emulated_cpuid = 7;
+        ReserveRange reserve_range = 8;
+        SetIrq set_irq = 9;
+        SetIrqRouting set_irq_routing = 10;
+        SetIdentityMapAddr set_identity_map_addr = 11;
+        PauseVcpus pause_vcpus = 12;
+        GetVcpus get_vcpus = 13;
+        Start start = 14;
         // 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;
@@ -156,6 +161,9 @@ message MainResponse {
     message CheckExtension {
         bool has_extension = 1;
     }
+    message CpuidResponse {
+        repeated CpuidEntry entries = 1;
+    }
     message ReserveRange {}
     message SetIrq {}
     message SetIrqRouting {}
@@ -179,13 +187,15 @@ message MainResponse {
         NewConnection new_connection = 4;
         GetShutdownEventfd get_shutdown_eventfd = 5;
         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;
+        CpuidResponse get_supported_cpuid = 7;
+        CpuidResponse get_emulated_cpuid = 8;
+        ReserveRange reserve_range = 9;
+        SetIrq set_irq = 10;
+        SetIrqRouting set_irq_routing = 11;
+        SetIdentityMapAddr set_identity_map_addr = 12;
+        PauseVcpus pause_vcpus = 13;
+        GetVcpus get_vcpus = 14;
+        Start start = 15;
         MemoryDirtyLog dirty_log = 101;
     }
 }
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index 44cba39..4d164a4 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -405,7 +405,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
     let kvm = Kvm::new().map_err(Error::CreateKvm)?;
     let mut vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?;
     vm.create_irq_chip().map_err(Error::CreateIrqChip)?;
-    let mut plugin = Process::new(vcpu_count, &mut vm, plugin_path, &plugin_args, jail)?;
+    let mut plugin = Process::new(vcpu_count, &kvm, &mut vm, plugin_path, &plugin_args, jail)?;
 
     let mut res = Ok(());
     // If Some, we will exit after enough time is passed to shutdown cleanly.
@@ -530,7 +530,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
                 }
                 t if t >= PLUGIN_BASE && t < PLUGIN_BASE + (plugin.sockets().len() as u32) => {
                     let socket_index = (t - PLUGIN_BASE) as usize;
-                    match plugin.handle_socket(socket_index, &mut vm, &vcpu_handles) {
+                    match plugin.handle_socket(socket_index, &kvm, &mut vm, &vcpu_handles) {
                         Ok(_) => {}
                         // A HUP is an expected event for a socket, so don't bother warning about
                         // it.
diff --git a/src/plugin/process.rs b/src/plugin/process.rs
index aa4a907..89c201c 100644
--- a/src/plugin/process.rs
+++ b/src/plugin/process.rs
@@ -73,6 +73,7 @@ impl Process {
     /// Due to an API limitation in libminijail necessitating that this function set an environment
     /// variable, this function is not thread-safe.
     pub fn new(cpu_count: u32,
+               kvm: &Kvm,
                vm: &mut Vm,
                cmd: &Path,
                args: &[&str],
@@ -125,13 +126,13 @@ impl Process {
             response_buffer: Vec::new(),
         };
 
-        plugin.run_until_started(vm)?;
+        plugin.run_until_started(kvm, vm)?;
 
         Ok(plugin)
     }
 
 
-    fn run_until_started(&mut self, vm: &mut Vm) -> Result<()> {
+    fn run_until_started(&mut self, kvm: &Kvm, vm: &mut Vm) -> Result<()> {
         let mut sockets_to_drop = Vec::new();
         let mut poller = Poller::new(1);
         while !self.started {
@@ -150,7 +151,7 @@ impl Process {
             };
 
             for &token in tokens {
-                match self.handle_socket(token as usize, vm, &[]) {
+                match self.handle_socket(token as usize, kvm, vm, &[]) {
                     Ok(_) => {}
                     Err(Error::PluginSocketHup) => sockets_to_drop.push(token as usize),
                     r => return r,
@@ -388,6 +389,7 @@ impl Process {
     /// interrupt a VCPU thread currently running in the VM if the socket request it.
     pub fn handle_socket(&mut self,
                          index: usize,
+                         kvm: &Kvm,
                          vm: &mut Vm,
                          vcpu_handles: &[JoinHandle<()>])
                          -> Result<()> {
@@ -533,6 +535,28 @@ impl Process {
                 }
                 _ => Err(SysError::new(-ENOENT)),
             }
+        } else if request.has_get_supported_cpuid() {
+            let cpuid_response = &mut response.mut_get_supported_cpuid().entries;
+            match kvm.get_supported_cpuid() {
+                Ok(mut cpuid) => {
+                    for entry in cpuid.mut_entries_slice() {
+                        cpuid_response.push(cpuid_kvm_to_proto(entry));
+                    }
+                    Ok(())
+                }
+                Err(e) => Err(e)
+            }
+        } else if request.has_get_emulated_cpuid() {
+            let cpuid_response = &mut response.mut_get_emulated_cpuid().entries;
+            match kvm.get_emulated_cpuid() {
+                Ok(mut cpuid) => {
+                    for entry in cpuid.mut_entries_slice() {
+                        cpuid_response.push(cpuid_kvm_to_proto(entry));
+                    }
+                    Ok(())
+                }
+                Err(e) => Err(e)
+            }
         } else {
             Err(SysError::new(-ENOTTY))
         };
diff --git a/tests/plugin_supported_cpuid.c b/tests/plugin_supported_cpuid.c
new file mode 100644
index 0000000..0acb134
--- /dev/null
+++ b/tests/plugin_supported_cpuid.c
@@ -0,0 +1,67 @@
+/*
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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;
+    }
+
+    struct kvm_cpuid_entry2 cpuids[100];
+    int n_entries;
+    ret = crosvm_get_supported_cpuid(crosvm, 1, cpuids, &n_entries);
+    if (ret >= 0) {
+        fprintf(stderr,
+                "expected crosvm_get_supported_cpuids to fail with E2BIG\n");
+        return 1;
+    }
+
+    ret = crosvm_get_supported_cpuid(crosvm, 100, cpuids, &n_entries);
+    if (ret < 0) {
+        fprintf(stderr,
+                "unexpected failure of crosvm_get_supported_cpuids: %d\n", ret);
+        return 1;
+    }
+
+    if (n_entries <= 1) {
+        fprintf(stderr,
+                "unexpected number of supported cpuid entries: %d\n",
+                n_entries);
+        return 1;
+    }
+
+    ret = crosvm_get_emulated_cpuid(crosvm, 1, cpuids, &n_entries);
+    if (ret >= 0) {
+        fprintf(stderr,
+                "expected crosvm_get_emulated_cpuids to fail with E2BIG\n");
+        return 1;
+    }
+
+    ret = crosvm_get_emulated_cpuid(crosvm, 100, cpuids, &n_entries);
+    if (ret < 0) {
+        fprintf(stderr,
+                "unexpected failure of crosvm_get_emulated_cpuid: %d\n", ret);
+        return 1;
+    }
+
+    if (n_entries < 1) {
+        fprintf(stderr,
+                "unexpected number of emulated cpuid entries: %d\n", n_entries);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/tests/plugins.rs b/tests/plugins.rs
index 1a99a43..d036e97 100644
--- a/tests/plugins.rs
+++ b/tests/plugins.rs
@@ -231,6 +231,11 @@ fn test_extensions() {
 }
 
 #[test]
+fn test_supported_cpuid() {
+    test_plugin(include_str!("plugin_supported_cpuid.c"));
+}
+
+#[test]
 fn test_vcpu_pause() {
     test_plugin(include_str!("plugin_vcpu_pause.c"));
 }