From 7ca9f771e7f406ff95b5b554bbefacbc8f8d6e63 Mon Sep 17 00:00:00 2001 From: Zach Reizner Date: Tue, 6 Feb 2018 20:50:07 -0800 Subject: add plugin support for configuring CPUID The guest expects to be able to read the CPUID, so the plugin process needs to specify what the CPUID for each VCPU will have. TEST=cargo test --features plugin; ./build_test BUG=chromium:800626 Change-Id: I9258540ab2501126c3d8cadbd09b7fc01d19f7a9 Reviewed-on: https://chromium-review.googlesource.com/906006 Commit-Ready: Zach Reizner Tested-by: Zach Reizner Reviewed-by: Dylan Reid --- Cargo.lock | 10 ++-- crosvm_plugin/Cargo.toml | 2 +- crosvm_plugin/crosvm.h | 6 +- crosvm_plugin/src/lib.rs | 36 ++++++++++- kvm_sys/src/x86/bindings.rs | 3 + plugin_proto/Cargo.toml | 2 +- plugin_proto/protos/plugin.proto | 18 ++++++ src/plugin/vcpu.rs | 25 +++++++- tests/plugins.rs | 125 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 216 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c810954..b2bc83b 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.8.0", + "crosvm_plugin 0.9.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.8.0", + "plugin_proto 0.9.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.8.0" +version = "0.9.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.8.0", + "plugin_proto 0.9.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.8.0" +version = "0.9.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)", diff --git a/crosvm_plugin/Cargo.toml b/crosvm_plugin/Cargo.toml index b650f3a..f6aeb24 100644 --- a/crosvm_plugin/Cargo.toml +++ b/crosvm_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crosvm_plugin" -version = "0.8.0" +version = "0.9.0" authors = ["The Chromium OS Authors"] [lib] diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h index d6eafb6..1bdcec6 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 8 +#define CROSVM_API_MINOR 9 #define CROSVM_API_PATCH 0 enum crosvm_address_space { @@ -430,6 +430,10 @@ int crosvm_vcpu_get_msrs(struct crosvm_vcpu*, uint32_t __msr_count, int crosvm_vcpu_set_msrs(struct crosvm_vcpu*, uint32_t __msr_count, const struct kvm_msr_entry *__msr_entries); +/* Sets the responses to the cpuid instructions executed on this vcpu, */ +int crosvm_vcpu_set_cpuid(struct crosvm_vcpu*, uint32_t __cpuid_count, + const struct kvm_cpuid_entry2 *__cpuid_entries); + #ifdef __cplusplus } #endif diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs index e383a1b..102d129 100644 --- a/crosvm_plugin/src/lib.rs +++ b/crosvm_plugin/src/lib.rs @@ -43,7 +43,8 @@ use sys_util::Scm; use kvm::dirty_log_bitmap_size; -use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msr_entry}; +use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msr_entry, kvm_cpuid_entry2, + KVM_CPUID_FLAG_SIGNIFCANT_INDEX}; use plugin_proto::*; @@ -736,6 +737,26 @@ impl crosvm_vcpu { self.vcpu_transaction(&r)?; Ok(()) } + + fn set_cpuid(&mut self, cpuid_entries: &[kvm_cpuid_entry2]) -> result::Result<(), c_int> { + let mut r = VcpuRequest::new(); + { + let set_cpuid_entries: &mut RepeatedField = r.mut_set_cpuid().mut_entries(); + for cpuid_entry in cpuid_entries.iter() { + let mut entry = CpuidEntry::new(); + entry.function = cpuid_entry.function; + entry.has_index = cpuid_entry.flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX != 0; + entry.index = cpuid_entry.index; + entry.eax = cpuid_entry.eax; + entry.ebx = cpuid_entry.ebx; + entry.ecx = cpuid_entry.ecx; + entry.edx = cpuid_entry.edx; + set_cpuid_entries.push(entry); + } + } + self.vcpu_transaction(&r)?; + Ok(()) + } } #[no_mangle] @@ -1005,3 +1026,16 @@ pub unsafe extern "C" fn crosvm_vcpu_set_msrs(this: *mut crosvm_vcpu, Err(e) => e, } } + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_set_cpuid(this: *mut crosvm_vcpu, + cpuid_count: u32, + cpuid_entries: *const kvm_cpuid_entry2) + -> c_int { + let this = &mut *this; + let cpuid_entries = from_raw_parts(cpuid_entries, cpuid_count as usize); + match this.set_cpuid(cpuid_entries) { + Ok(_) => 0, + Err(e) => e, + } +} diff --git a/kvm_sys/src/x86/bindings.rs b/kvm_sys/src/x86/bindings.rs index fa21578..a0b5985 100644 --- a/kvm_sys/src/x86/bindings.rs +++ b/kvm_sys/src/x86/bindings.rs @@ -164,6 +164,9 @@ pub const KVM_IRQCHIP_IOAPIC: ::std::os::raw::c_uint = 2; pub const KVM_NR_IRQCHIPS: ::std::os::raw::c_uint = 3; pub const KVM_RUN_X86_SMM: ::std::os::raw::c_uint = 1; pub const KVM_APIC_REG_SIZE: ::std::os::raw::c_uint = 1024; +pub const KVM_CPUID_FLAG_SIGNIFCANT_INDEX: ::std::os::raw::c_uint = 1; +pub const KVM_CPUID_FLAG_STATEFUL_FUNC: ::std::os::raw::c_uint = 2; +pub const KVM_CPUID_FLAG_STATE_READ_NEXT: ::std::os::raw::c_uint = 4; pub const KVM_GUESTDBG_USE_SW_BP: ::std::os::raw::c_uint = 65536; pub const KVM_GUESTDBG_USE_HW_BP: ::std::os::raw::c_uint = 131072; pub const KVM_GUESTDBG_INJECT_DB: ::std::os::raw::c_uint = 262144; diff --git a/plugin_proto/Cargo.toml b/plugin_proto/Cargo.toml index d8f1509..0e5ead4 100644 --- a/plugin_proto/Cargo.toml +++ b/plugin_proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plugin_proto" -version = "0.8.0" +version = "0.9.0" authors = ["The Chromium OS Authors"] build = "build.rs" diff --git a/plugin_proto/protos/plugin.proto b/plugin_proto/protos/plugin.proto index 8bcdcc9..22f7be6 100644 --- a/plugin_proto/protos/plugin.proto +++ b/plugin_proto/protos/plugin.proto @@ -14,6 +14,16 @@ enum AddressSpace { MMIO = 1; } +message CpuidEntry { + uint32 function = 1; + bool has_index = 3; + uint32 index = 4; + uint32 eax = 5; + uint32 ebx = 6; + uint32 ecx = 7; + uint32 edx = 8; +} + // A request made to the crosvm main process that affects the global aspects of the VM. message MainRequest { // Every message under the Create namespace will instantiate an object with the given ID. The @@ -224,6 +234,10 @@ message VcpuRequest { repeated MsrEntry entries = 1; } + message SetCpuid { + repeated CpuidEntry entries = 1; + } + // The type of the message is determined by which of these oneof fields is present in the // protobuf. oneof message { @@ -233,6 +247,7 @@ message VcpuRequest { SetState set_state = 4; GetMsrs get_msrs = 5; SetMsrs set_msrs = 6; + SetCpuid set_cpuid = 7; } } @@ -285,6 +300,8 @@ message VcpuResponse { message SetMsrs {} + message SetCpuid {} + // This is zero on success, and a negative integer on failure. sint32 errno = 1; // The field present here is always the same as the one present in the corresponding @@ -296,5 +313,6 @@ message VcpuResponse { SetState set_state = 5; GetMsrs get_msrs = 6; SetMsrs set_msrs = 7; + SetCpuid set_cpuid = 8; } } diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs index fcd6543..ea830bf 100644 --- a/src/plugin/vcpu.rs +++ b/src/plugin/vcpu.rs @@ -16,8 +16,9 @@ use protobuf; use protobuf::Message; use data_model::DataInit; -use kvm::Vcpu; -use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msrs, kvm_msr_entry}; +use kvm::{Vcpu, CpuId}; +use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msrs, kvm_msr_entry, + KVM_CPUID_FLAG_SIGNIFCANT_INDEX}; use plugin_proto::*; use super::*; @@ -448,6 +449,26 @@ impl PluginVcpu { } kvm_msrs.nmsrs = request_entries.len() as u32; vcpu.set_msrs(&kvm_msrs) + } else if request.has_set_cpuid() { + response.mut_set_cpuid(); + let request_entries = &request.get_set_cpuid().entries; + let mut cpuid = CpuId::new(request_entries.len()); + { + let cpuid_entries = cpuid.mut_entries_slice(); + for (request_entry, cpuid_entry) in + request_entries.iter().zip(cpuid_entries.iter_mut()) { + cpuid_entry.function = request_entry.function; + if request_entry.has_index { + cpuid_entry.index = request_entry.index; + cpuid_entry.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + } + cpuid_entry.eax = request_entry.eax; + cpuid_entry.ebx = request_entry.ebx; + cpuid_entry.ecx = request_entry.ecx; + cpuid_entry.edx = request_entry.edx; + } + } + vcpu.set_cpuid2(&cpuid) } else { Err(SysError::new(-ENOTTY)) }; diff --git a/tests/plugins.rs b/tests/plugins.rs index 357ad9a..54c4b7d 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -374,3 +374,128 @@ fn test_msrs() { }; test_mini_plugin(&mini_plugin); } + +#[test] +fn test_cpuid() { + let mini_plugin = MiniPlugin { + assembly_src: "org 0x1000 + bits 16 + push eax + push ecx + cpuid + mov [0x0], eax + mov [0x4], ebx + mov [0x8], ecx + mov [0xc], edx + pop ecx + pop eax + add ecx, 1 + cpuid + mov [0x10], eax + mov [0x14], ebx + mov [0x18], ecx + mov [0x1c], edx + mov byte [es:0], 1", + src: r#" + #define ENTRY1_INDEX 0 + #define ENTRY1_EAX 0x40414243 + #define ENTRY1_EBX 0x50515253 + #define ENTRY1_ECX 0x60616263 + #define ENTRY1_EDX 0x71727374 + #define ENTRY2_INDEX 1 + #define ENTRY2_EAX 0xAABBCCDD + #define ENTRY2_EBX 0xEEFF0011 + #define ENTRY2_ECX 0x22334455 + #define ENTRY2_EDX 0x66778899 + #define KILL_ADDRESS 0x3000 + + int g_kill_evt; + struct kvm_msr_entry g_msr2; + + int setup_vm(struct crosvm *crosvm, void *mem) { + g_kill_evt = crosvm_get_shutdown_eventfd(crosvm); + crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1); + return 0; + } + + int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs, + struct kvm_sregs *sregs) + { + regs->rax = ENTRY1_INDEX; + regs->rcx = 0; + regs->rsp = 0x1000; + sregs->es.base = KILL_ADDRESS; + + struct kvm_cpuid_entry2 entries[2]; + entries[0].function = 0; + entries[0].index = ENTRY1_INDEX; + entries[0].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + entries[0].eax = ENTRY1_EAX; + entries[0].ebx = ENTRY1_EBX; + entries[0].ecx = ENTRY1_ECX; + entries[0].edx = ENTRY1_EDX; + entries[1].function = 0; + entries[1].index = ENTRY2_INDEX; + entries[1].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + entries[1].eax = ENTRY2_EAX; + entries[1].ebx = ENTRY2_EBX; + entries[1].ecx = ENTRY2_ECX; + entries[1].edx = ENTRY2_EDX; + return crosvm_vcpu_set_cpuid(vcpu, 2, entries); + } + + int handle_vpcu_evt(struct crosvm_vcpu *vcpu, struct crosvm_vcpu_event evt) { + if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS && + evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO && + evt.io_access.address == KILL_ADDRESS && + evt.io_access.is_write && + evt.io_access.length == 1 && + evt.io_access.data[0] == 1) + { + uint64_t dummy = 1; + write(g_kill_evt, &dummy, sizeof(dummy)); + return 1; + } + return 0; + } + + int check_result(struct crosvm *vcpu, void *memory) { + uint32_t *mem = (uint32_t*)memory; + if (mem[0] != ENTRY1_EAX) { + fprintf(stderr, "entry 1 eax has unexpected value: 0x%x\n", mem[0]); + return 1; + } + if (mem[1] != ENTRY1_EBX) { + fprintf(stderr, "entry 1 ebx has unexpected value: 0x%x\n", mem[1]); + return 1; + } + if (mem[2] != ENTRY1_ECX) { + fprintf(stderr, "entry 1 ecx has unexpected value: 0x%x\n", mem[2]); + return 1; + } + if (mem[3] != ENTRY1_EDX) { + fprintf(stderr, "entry 1 edx has unexpected value: 0x%x\n", mem[3]); + return 1; + } + if (mem[4] != ENTRY2_EAX) { + fprintf(stderr, "entry 2 eax has unexpected value: 0x%x\n", mem[4]); + return 1; + } + if (mem[5] != ENTRY2_EBX) { + fprintf(stderr, "entry 2 ebx has unexpected value: 0x%x\n", mem[5]); + return 1; + } + if (mem[6] != ENTRY2_ECX) { + fprintf(stderr, "entry 2 ecx has unexpected value: 0x%x\n", mem[6]); + return 1; + } + if (mem[7] != ENTRY2_EDX) { + fprintf(stderr, "entry 2 edx has unexpected value: 0x%x\n", mem[7]); + return 1; + } + return 0; + }"#, + ..Default::default() + }; + test_mini_plugin(&mini_plugin); +} -- cgit 1.4.1