diff options
-rw-r--r-- | crosvm_plugin/crosvm.h | 9 | ||||
-rw-r--r-- | crosvm_plugin/src/lib.rs | 50 | ||||
-rw-r--r-- | kvm/src/lib.rs | 35 | ||||
-rw-r--r-- | kvm_sys/src/lib.rs | 1 | ||||
-rw-r--r-- | protos/src/plugin.proto | 9 | ||||
-rw-r--r-- | src/plugin/vcpu.rs | 11 | ||||
-rw-r--r-- | tests/plugin_supported_cpuid.c | 96 |
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; } |