summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crosvm_plugin/crosvm.h9
-rw-r--r--crosvm_plugin/src/lib.rs50
-rw-r--r--kvm/src/lib.rs35
-rw-r--r--kvm_sys/src/lib.rs1
-rw-r--r--protos/src/plugin.proto9
-rw-r--r--src/plugin/vcpu.rs11
-rw-r--r--tests/plugin_supported_cpuid.c96
7 files changed, 182 insertions, 29 deletions
diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h
index 63763f1..ef7759d 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 19
+#define CROSVM_API_MINOR 20
 #define CROSVM_API_PATCH 0
 
 enum crosvm_address_space {
@@ -130,6 +130,13 @@ int crosvm_get_emulated_cpuid(struct crosvm*, uint32_t __entry_count,
                               uint32_t *__out_count);
 
 /*
+ * Queries x86 hyper-v cpuid features which are emulated by kvm.
+ */
+int crosvm_get_hyperv_cpuid(struct crosvm_vcpu*, uint32_t __entry_count,
+                            struct kvm_cpuid_entry2 *__cpuid_entries,
+                            uint32_t *__out_count);
+
+/*
  * Queries kvm for list of supported MSRs.
  */
 int crosvm_get_msr_index_list(struct crosvm*, uint32_t __entry_count,
diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs
index eb30e4b..13b3d9f 100644
--- a/crosvm_plugin/src/lib.rs
+++ b/crosvm_plugin/src/lib.rs
@@ -168,6 +168,7 @@ pub enum Stat {
     CheckExtentsion,
     GetSupportedCpuid,
     GetEmulatedCpuid,
+    GetHypervCpuid,
     GetMsrIndexList,
     NetGetConfig,
     ReserveRange,
@@ -1202,6 +1203,39 @@ impl crosvm_vcpu {
         Ok(())
     }
 
+    fn get_hyperv_cpuid(
+        &mut self,
+        cpuid_entries: &mut [kvm_cpuid_entry2],
+        cpuid_count: &mut usize,
+    ) -> result::Result<(), c_int> {
+        *cpuid_count = 0;
+
+        let mut r = VcpuRequest::new();
+        r.mut_get_hyperv_cpuid();
+
+        let response = self.vcpu_transaction(&r)?;
+        if !response.has_get_hyperv_cpuid() {
+            return Err(EPROTO);
+        }
+
+        let hyperv_cpuids: &VcpuResponse_CpuidResponse = response.get_get_hyperv_cpuid();
+
+        *cpuid_count = hyperv_cpuids.get_entries().len();
+        if *cpuid_count > cpuid_entries.len() {
+            return Err(E2BIG);
+        }
+
+        for (proto_entry, kvm_entry) in hyperv_cpuids
+            .get_entries()
+            .iter()
+            .zip(cpuid_entries.iter_mut())
+        {
+            *kvm_entry = cpuid_proto_to_kvm(proto_entry);
+        }
+
+        Ok(())
+    }
+
     fn get_msrs(
         &mut self,
         msr_entries: &mut [kvm_msr_entry],
@@ -1804,6 +1838,22 @@ pub unsafe extern "C" fn crosvm_vcpu_set_xcrs(
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn crosvm_get_hyperv_cpuid(
+    this: *mut crosvm_vcpu,
+    entry_count: u32,
+    cpuid_entries: *mut kvm_cpuid_entry2,
+    out_count: *mut u32,
+) -> c_int {
+    let _u = record(Stat::GetHypervCpuid);
+    let this = &mut *this;
+    let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
+    let mut cpuid_count: usize = 0;
+    let ret = this.get_hyperv_cpuid(cpuid_entries, &mut cpuid_count);
+    *out_count = cpuid_count as u32;
+    to_crosvm_rc(ret)
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn crosvm_vcpu_get_msrs(
     this: *mut crosvm_vcpu,
     msr_count: u32,
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index c5da845..a522478 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -1502,6 +1502,24 @@ impl Vcpu {
         Ok(())
     }
 
+    /// X86 specific call to get the system emulated hyper-v CPUID values
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    pub fn get_hyperv_cpuid(&self) -> Result<CpuId> {
+        const MAX_KVM_CPUID_ENTRIES: usize = 256;
+        let mut cpuid = CpuId::new(MAX_KVM_CPUID_ENTRIES);
+
+        let ret = unsafe {
+            // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the memory
+            // allocated for the struct. The limit is read from nent, which is set to the allocated
+            // size(MAX_KVM_CPUID_ENTRIES) above.
+            ioctl_with_mut_ptr(self, KVM_GET_SUPPORTED_HV_CPUID(), cpuid.as_mut_ptr())
+        };
+        if ret < 0 {
+            return errno_result();
+        }
+        Ok(cpuid)
+    }
+
     /// X86 specific call to get the state of the "Local Advanced Programmable Interrupt Controller".
     ///
     /// See the documentation for KVM_GET_LAPIC.
@@ -2333,6 +2351,23 @@ mod tests {
 
     #[test]
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn get_hyperv_cpuid() {
+        let kvm = Kvm::new().unwrap();
+        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
+        let vm = Vm::new(&kvm, gm).unwrap();
+        let vcpu = Vcpu::new(0, &kvm, &vm).unwrap();
+        let cpuid = vcpu.get_hyperv_cpuid();
+        // Older kernels don't support so tolerate this kind of failure.
+        match cpuid {
+            Ok(_) => {}
+            Err(e) => {
+                assert_eq!(e.errno(), EINVAL);
+            }
+        }
+    }
+
+    #[test]
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
     fn mp_state() {
         let kvm = Kvm::new().unwrap();
         let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x10000)]).unwrap();
diff --git a/kvm_sys/src/lib.rs b/kvm_sys/src/lib.rs
index b9748a3..4c0324d 100644
--- a/kvm_sys/src/lib.rs
+++ b/kvm_sys/src/lib.rs
@@ -47,6 +47,7 @@ pub mod x86 {
     ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave);
     ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs);
     ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs);
+    ioctl_iowr_nr!(KVM_GET_SUPPORTED_HV_CPUID, KVMIO, 0xc1, kvm_cpuid2);
 }
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
diff --git a/protos/src/plugin.proto b/protos/src/plugin.proto
index e2838b0..783d23b 100644
--- a/protos/src/plugin.proto
+++ b/protos/src/plugin.proto
@@ -334,6 +334,9 @@ message VcpuRequest {
         bytes state = 2;
     }
 
+    message CpuidRequest {
+    }
+
     message GetMsrs {
         // The entry data will be returned in the same order as this in the
         // VcpuResponse::GetMsrs::entry_data array.
@@ -367,6 +370,7 @@ message VcpuRequest {
         SetMsrs set_msrs = 6;
         SetCpuid set_cpuid = 7;
         Shutdown shutdown = 8;
+        CpuidRequest get_hyperv_cpuid = 9;
     }
 }
 
@@ -417,6 +421,10 @@ message VcpuResponse  {
     message SetState {
     }
 
+    message CpuidResponse {
+        repeated CpuidEntry entries = 1;
+    }
+
     message GetMsrs {
         // The order of the entry_data values is the same order as the array of indices given in the
         // corresponding request.
@@ -439,5 +447,6 @@ message VcpuResponse  {
         GetMsrs get_msrs = 6;
         SetMsrs set_msrs = 7;
         SetCpuid set_cpuid = 8;
+        CpuidResponse get_hyperv_cpuid = 9;
     }
 }
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
index c623bda..6bae9c4 100644
--- a/src/plugin/vcpu.rs
+++ b/src/plugin/vcpu.rs
@@ -595,6 +595,17 @@ impl PluginVcpu {
                 response.mut_set_state();
                 let set_state = request.get_set_state();
                 set_vcpu_state(vcpu, set_state.set, set_state.get_state())
+            } else if request.has_get_hyperv_cpuid() {
+                let cpuid_response = &mut response.mut_get_hyperv_cpuid().entries;
+                match vcpu.get_hyperv_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_msrs() {
                 let entry_data = &mut response.mut_get_msrs().entry_data;
                 let entry_indices = &request.get_get_msrs().entry_indices;
diff --git a/tests/plugin_supported_cpuid.c b/tests/plugin_supported_cpuid.c
index 0acb134..7109ff3 100644
--- a/tests/plugin_supported_cpuid.c
+++ b/tests/plugin_supported_cpuid.c
@@ -12,56 +12,96 @@
 
 #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;
-    }
+typedef int (*crosvm_function)(struct crosvm*, uint32_t,
+                               struct kvm_cpuid_entry2*, uint32_t*);
+typedef int (*vcpu_function)(struct crosvm_vcpu*, uint32_t,
+                             struct kvm_cpuid_entry2*, uint32_t*);
+
+// Members of union should only differ by the pointer type of 1st arg.
+union cpuid_function {
+    crosvm_function crosvm;
+    vcpu_function vcpu;
+};
 
+int test_cpuid(void* crosvm, union cpuid_function funct, const char* name) {
     struct kvm_cpuid_entry2 cpuids[100];
-    int n_entries;
-    ret = crosvm_get_supported_cpuid(crosvm, 1, cpuids, &n_entries);
+    int n_entries = 0;
+    int ret = funct.crosvm(crosvm, 1, cpuids, &n_entries);
     if (ret >= 0) {
         fprintf(stderr,
-                "expected crosvm_get_supported_cpuids to fail with E2BIG\n");
-        return 1;
+                "expected %s to fail with E2BIG\n", name);
+        return ret;
     }
 
-    ret = crosvm_get_supported_cpuid(crosvm, 100, cpuids, &n_entries);
+    ret = funct.crosvm(crosvm, 100, cpuids, &n_entries);
     if (ret < 0) {
-        fprintf(stderr,
-                "unexpected failure of crosvm_get_supported_cpuids: %d\n", ret);
-        return 1;
+        if (ret != -EINVAL) {
+            fprintf(stderr, "unexpected failure of %s: %d\n", name, ret);
+        } else {
+            fprintf(stderr,
+                    "Query of %s failed with EINVAL (may be expected)\n",
+                    name, ret);
+        }
+        return ret;
     }
 
     if (n_entries <= 1) {
         fprintf(stderr,
-                "unexpected number of supported cpuid entries: %d\n",
-                n_entries);
+                "unexpected number of cpuid entries from %s: %d\n",
+                name, n_entries);
         return 1;
     }
+    return 0;
+}
 
-    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");
+int main(int argc, char** argv) {
+    struct crosvm* crosvm = NULL;
+    int ret = crosvm_connect(&crosvm);
+    if (ret) {
+        fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
         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);
+    struct crosvm_vcpu* vcpu = NULL;
+    ret = crosvm_get_vcpu(crosvm, 0, &vcpu);
+    if (ret) {
+        fprintf(stderr, "failed to get vcpu #0: %d\n", ret);
         return 1;
     }
 
-    if (n_entries < 1) {
-        fprintf(stderr,
-                "unexpected number of emulated cpuid entries: %d\n", n_entries);
+    union cpuid_function funct;
+    funct.crosvm = crosvm_get_supported_cpuid;
+    if (test_cpuid(crosvm, funct, "crosvm_get_supported_cpuid")) {
+        return 1;
+    }
+    funct.crosvm = crosvm_get_emulated_cpuid;
+    if (test_cpuid(crosvm, funct, "crosvm_get_emulated_cpuid")) {
+        return 1;
+    }
+
+    ret = crosvm_start(crosvm);
+    if (ret) {
+        fprintf(stderr, "failed to start vm: %d\n", ret);
         return 1;
     }
 
+    struct crosvm_vcpu_event evt = {0};
+    ret = crosvm_vcpu_wait(vcpu, &evt);
+    if (ret) {
+        fprintf(stderr, "failed to wait for vm start: %d\n", ret);
+        return 1;
+    }
+    if (evt.kind != CROSVM_VCPU_EVENT_KIND_INIT) {
+        fprintf(stderr, "Got unexpected exit type: %d\n", evt.kind);
+        return 1;
+    }
+
+    funct.vcpu = crosvm_get_hyperv_cpuid;
+    ret = test_cpuid(vcpu, funct, "crosvm_get_hyperv_cpuid");
+    // Older kernels don't support and return EINVAL, so allow this for now.
+    if (ret && ret != -EINVAL) {
+        fprintf(stderr, "Ignoring failure of crosvm_get_hyperv_cpuid\n");
+        return 1;
+    }
     return 0;
 }