From d1e391b8d490621974eee8b1d6464eac1f9c1bb3 Mon Sep 17 00:00:00 2001 From: Slava Malyugin Date: Fri, 6 Jul 2018 09:52:05 -0700 Subject: [PATCH] plugin: allow retrieving and setting VCPU events Add crosvm plugin API to allow fetching and setting VCPU events. BUG=b:110056268 TEST=cargo test --features plugin -p kvm Change-Id: Id66230f180f4bdb95bd1850ed050e439083701cc Reviewed-on: https://chromium-review.googlesource.com/1128045 Commit-Ready: Slava Malyugin Tested-by: Slava Malyugin Reviewed-by: Zach Reizner --- Cargo.lock | 10 ++++----- crosvm_plugin/Cargo.toml | 2 +- crosvm_plugin/crosvm.h | 10 ++++++++- crosvm_plugin/src/lib.rs | 28 ++++++++++++++++++++++++- kvm/src/lib.rs | 35 ++++++++++++++++++++++++++++++++ plugin_proto/Cargo.toml | 2 +- plugin_proto/protos/plugin.proto | 13 ++++++++---- src/plugin/vcpu.rs | 11 +++++++++- tests/plugins.rs | 13 ++++++++++++ 9 files changed, 110 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27176316a1..9a9bbe7540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ dependencies = [ "aarch64 0.1.0", "arch 0.1.0", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crosvm_plugin 0.14.0", + "crosvm_plugin 0.15.0", "data_model 0.1.0", "devices 0.1.0", "gpu_buffer 0.1.0", @@ -66,7 +66,7 @@ dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "net_util 0.1.0", "p9 0.1.0", - "plugin_proto 0.14.0", + "plugin_proto 0.15.0", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "qcow 0.1.0", "qcow_utils 0.1.0", @@ -80,12 +80,12 @@ dependencies = [ [[package]] name = "crosvm_plugin" -version = "0.14.0" +version = "0.15.0" dependencies = [ "kvm 0.1.0", "kvm_sys 0.1.0", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "plugin_proto 0.14.0", + "plugin_proto 0.15.0", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] @@ -221,7 +221,7 @@ dependencies = [ [[package]] name = "plugin_proto" -version = "0.14.0" +version = "0.15.0" dependencies = [ "cc 1.0.15 (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 3c61aa4b45..c203f4d484 100644 --- a/crosvm_plugin/Cargo.toml +++ b/crosvm_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crosvm_plugin" -version = "0.14.0" +version = "0.15.0" authors = ["The Chromium OS Authors"] [lib] diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h index 42c979a9dd..947a198849 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 13 +#define CROSVM_API_MINOR 15 #define CROSVM_API_PATCH 0 enum crosvm_address_space { @@ -525,6 +525,14 @@ int crosvm_vcpu_get_mp_state(struct crosvm_vcpu *, int crosvm_vcpu_set_mp_state(struct crosvm_vcpu *, const struct kvm_mp_state *__mp_state); +/* Gets currently pending exceptions, interrupts, NMIs, etc for VCPU. */ +int crosvm_vcpu_get_vcpu_events(struct crosvm_vcpu *, + struct kvm_vcpu_events *); + +/* Sets currently pending exceptions, interrupts, NMIs, etc for VCPU. */ +int crosvm_vcpu_set_vcpu_events(struct crosvm_vcpu *, + const struct kvm_vcpu_events *); + #ifdef __cplusplus } #endif diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs index 231ee250a2..24d99ca830 100644 --- a/crosvm_plugin/src/lib.rs +++ b/crosvm_plugin/src/lib.rs @@ -46,7 +46,7 @@ use kvm::dirty_log_bitmap_size; use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_xcrs, kvm_msr_entry, kvm_cpuid_entry2, kvm_lapic_state, kvm_mp_state, kvm_pic_state, kvm_ioapic_state, - kvm_pit_state2}; + kvm_pit_state2, kvm_vcpu_events}; use plugin_proto::*; @@ -172,6 +172,8 @@ enum Stat { VcpuSetLapicState, VcpuGetMpState, VcpuSetMpState, + VcpuGetVcpuEvents, + VcpuSetVcpuEvents, NewConnection, Count, @@ -1487,3 +1489,27 @@ pub unsafe extern "C" fn crosvm_vcpu_set_mp_state(this: *mut crosvm_vcpu, let ret = this.set_state(VcpuRequest_StateSet::MP, state); to_crosvm_rc(ret) } + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_get_vcpu_events(this: *mut crosvm_vcpu, + events: *mut kvm_vcpu_events) + -> c_int { + let _u = STATS.record(Stat::VcpuGetVcpuEvents); + let this = &mut *this; + let events = from_raw_parts_mut(events as *mut u8, + size_of::()); + let ret = this.get_state(VcpuRequest_StateSet::EVENTS, events); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_set_vcpu_events(this: *mut crosvm_vcpu, + events: *const kvm_vcpu_events) + -> c_int { + let _u = STATS.record(Stat::VcpuSetVcpuEvents); + let this = &mut *this; + let events = from_raw_parts(events as *mut u8, + size_of::()); + let ret = this.set_state(VcpuRequest_StateSet::EVENTS, events); + to_crosvm_rc(ret) +} diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs index adac9fb9ed..ea288eb702 100644 --- a/kvm/src/lib.rs +++ b/kvm/src/lib.rs @@ -1169,6 +1169,41 @@ impl Vcpu { Ok(()) } + /// Gets the vcpu's currently pending exceptions, interrupts, NMIs, etc + /// + /// See the documentation for KVM_GET_VCPU_EVENTS. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn get_vcpu_events(&self) -> Result { + // Safe because we know that our file is a VCPU fd, we know the kernel + // will only write correct amount of memory to our pointer, and we + // verify the return result. + let mut events: kvm_vcpu_events = unsafe { std::mem::zeroed() }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_VCPU_EVENTS(), + &mut events) }; + if ret < 0 { + return errno_result(); + } + Ok(events) + } + + /// Sets the vcpu's currently pending exceptions, interrupts, NMIs, etc + /// + /// See the documentation for KVM_SET_VCPU_EVENTS. + /// + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub fn set_vcpu_events(&self, events: &kvm_vcpu_events) -> Result<()> { + let ret = unsafe { + // The ioctl is safe because the kernel will only read from the + // kvm_vcpu_events. + ioctl_with_ref(self, KVM_SET_VCPU_EVENTS(), events) + }; + if ret < 0 { + return errno_result(); + } + Ok(()) + } + /// Specifies set of signals that are blocked during execution of KVM_RUN. /// Signals that are not blocked will will cause KVM_RUN to return /// with -EINTR. diff --git a/plugin_proto/Cargo.toml b/plugin_proto/Cargo.toml index f751a80a3c..441cb2a8ec 100644 --- a/plugin_proto/Cargo.toml +++ b/plugin_proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plugin_proto" -version = "0.14.0" +version = "0.15.0" authors = ["The Chromium OS Authors"] build = "build.rs" diff --git a/plugin_proto/protos/plugin.proto b/plugin_proto/protos/plugin.proto index 399e02aeff..699b4785b1 100644 --- a/plugin_proto/protos/plugin.proto +++ b/plugin_proto/protos/plugin.proto @@ -277,6 +277,8 @@ message VcpuRequest { MP = 5; // struct kvm_xcrs XCREGS = 6; + // struct kvm_vcpu_events + EVENTS = 7; } message GetState { @@ -285,7 +287,9 @@ message VcpuRequest { message SetState { StateSet set = 1; - // The in memory representation of a struct kvm_regs, struct kvm_sregs, or struct kvm_fpu, + // The in memory representation of a struct kvm_regs, struct kvm_sregs, + // struct kvm_fpu, struct kvm_debugregs, struct kvm_lapic_state, + // struct kvm_mp_state, struct kvm_xcrs or struct kvm_vcpu_events // depending on the value of the StateSet. bytes state = 2; } @@ -355,9 +359,10 @@ message VcpuResponse { message Resume {} message GetState { - // The in memory representation of a struct kvm_regs, struct kvm_sregs, struct kvm_fpu, - // struct kvm_lapic_state, or struct kvm_mp_state, depending on what StateSet was - // requested in GetState. + // The in memory representation of a struct kvm_regs, struct kvm_sregs, + // struct kvm_fpu, struct kvm_debugregs, struct kvm_lapic_state, + // struct kvm_mp_state, struct kvm_xcrs or struct kvm_vcpu_events + // depending on the value of the StateSet. bytes state = 1; } diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs index 4ea6482a65..5ff5366f5b 100644 --- a/src/plugin/vcpu.rs +++ b/src/plugin/vcpu.rs @@ -18,7 +18,7 @@ use protobuf::Message; use data_model::DataInit; use kvm::{Vcpu, CpuId}; use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_xcrs, kvm_msrs, kvm_msr_entry, - KVM_CPUID_FLAG_SIGNIFCANT_INDEX, kvm_lapic_state, kvm_mp_state}; + KVM_CPUID_FLAG_SIGNIFCANT_INDEX, kvm_lapic_state, kvm_mp_state, kvm_vcpu_events}; use plugin_proto::*; use super::*; @@ -75,6 +75,9 @@ unsafe impl DataInit for VcpuLapicState {} #[derive(Copy, Clone)] struct VcpuMpState(kvm_mp_state); unsafe impl DataInit for VcpuMpState {} +#[derive(Copy, Clone)] +struct VcpuEvents(kvm_vcpu_events); +unsafe impl DataInit for VcpuEvents {} fn get_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet) -> SysResult> { Ok(match state_set { @@ -87,6 +90,7 @@ fn get_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet) -> SysResult VcpuXcregs(vcpu.get_xcrs()?).as_slice().to_vec(), VcpuRequest_StateSet::LAPIC => VcpuLapicState(vcpu.get_lapic()?).as_slice().to_vec(), VcpuRequest_StateSet::MP => VcpuMpState(vcpu.get_mp_state()?).as_slice().to_vec(), + VcpuRequest_StateSet::EVENTS => VcpuEvents(vcpu.get_vcpu_events()?).as_slice().to_vec(), }) } @@ -127,6 +131,11 @@ fn set_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet, state: &[u8]) -> .ok_or(SysError::new(EINVAL))? .0) } + VcpuRequest_StateSet::EVENTS => { + vcpu.set_vcpu_events(&VcpuEvents::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } } } diff --git a/tests/plugins.rs b/tests/plugins.rs index b1d8e4d9ad..c760875ca3 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -644,6 +644,19 @@ fn test_vcpu_state_manipulation() { return 1; } + struct kvm_vcpu_events events; + ret = crosvm_vcpu_get_vcpu_events(vcpu, &events); + if (ret < 0) { + fprintf(stderr, "failed to get VCPU events: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_set_vcpu_events(vcpu, &events); + if (ret < 0) { + fprintf(stderr, "failed to set VCPU events: %d\n", ret); + return 1; + } + success = true; return 0; }