diff options
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | crosvm_plugin/Cargo.toml | 2 | ||||
-rw-r--r-- | crosvm_plugin/crosvm.h | 39 | ||||
-rw-r--r-- | crosvm_plugin/src/lib.rs | 138 | ||||
-rw-r--r-- | plugin_proto/Cargo.toml | 2 | ||||
-rw-r--r-- | plugin_proto/protos/plugin.proto | 63 | ||||
-rw-r--r-- | src/plugin/process.rs | 72 | ||||
-rw-r--r-- | src/plugin/vcpu.rs | 21 | ||||
-rw-r--r-- | tests/plugin_vm_state.c | 119 | ||||
-rw-r--r-- | tests/plugins.rs | 91 |
10 files changed, 533 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock index a911969..9b523f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,7 +31,7 @@ version = "0.1.0" dependencies = [ "arch 0.1.0", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crosvm_plugin 0.12.0", + "crosvm_plugin 0.13.0", "data_model 0.1.0", "device_manager 0.1.0", "devices 0.1.0", @@ -42,7 +42,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.12.0", + "plugin_proto 0.13.0", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "qcow 0.1.0", "qcow_utils 0.1.0", @@ -55,12 +55,12 @@ dependencies = [ [[package]] name = "crosvm_plugin" -version = "0.12.0" +version = "0.13.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.12.0", + "plugin_proto 0.13.0", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] @@ -186,7 +186,7 @@ dependencies = [ [[package]] name = "plugin_proto" -version = "0.12.0" +version = "0.13.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 42516ad..5b775e3 100644 --- a/crosvm_plugin/Cargo.toml +++ b/crosvm_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crosvm_plugin" -version = "0.12.0" +version = "0.13.0" authors = ["The Chromium OS Authors"] [lib] diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h index 30b32c1..c7589e2 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 12 +#define CROSVM_API_MINOR 13 #define CROSVM_API_PATCH 0 enum crosvm_address_space { @@ -222,6 +222,29 @@ static_assert(sizeof(struct crosvm_irq_route) == 24, int crosvm_set_irq_routing(struct crosvm*, uint32_t __route_count, const struct crosvm_irq_route* __routes); +/* Gets the state of interrupt controller in a VM. */ +int crosvm_get_pic_state(struct crosvm *, bool __primary, + struct kvm_pic_state *__pic_state); + +/* Sets the state of interrupt controller in a VM. */ +int crosvm_set_pic_state(struct crosvm *, bool __primary, + struct kvm_pic_state *__pic_state); + +/* Gets the state of IOAPIC in a VM. */ +int crosvm_get_ioapic_state(struct crosvm *, + struct kvm_ioapic_state *__ioapic_state); + +/* Sets the state of IOAPIC in a VM. */ +int crosvm_set_ioapic_state(struct crosvm *, + struct kvm_ioapic_state *__ioapic_state); + +/* Gets the state of interrupt controller in a VM. */ +int crosvm_get_pit_state(struct crosvm *, struct kvm_pit_state2 *__pit_state); + +/* Sets the state of interrupt controller in a VM. */ +int crosvm_set_pit_state(struct crosvm *, + const struct kvm_pit_state2 *__pit_state); + /* Sets the identity map address as in the KVM_SET_IDENTITY_MAP_ADDR ioctl. */ int crosvm_set_identity_map_addr(struct crosvm*, uint32_t __addr); @@ -483,6 +506,20 @@ int crosvm_vcpu_set_msrs(struct crosvm_vcpu*, uint32_t __msr_count, int crosvm_vcpu_set_cpuid(struct crosvm_vcpu*, uint32_t __cpuid_count, const struct kvm_cpuid_entry2 *__cpuid_entries); +/* Gets state of LAPIC of the VCPU. */ +int crosvm_vcpu_get_lapic_state(struct crosvm_vcpu *, + struct kvm_lapic_state *__lapic_state); +/* Sets state of LAPIC of the VCPU. */ +int crosvm_vcpu_set_lapic_state(struct crosvm_vcpu *, + const struct kvm_lapic_state *__lapic_state); + +/* Gets the "multiprocessor state" of given VCPU. */ +int crosvm_vcpu_get_mp_state(struct crosvm_vcpu *, + struct kvm_mp_state *__mp_state); +/* Sets the "multiprocessor state" of given VCPU. */ +int crosvm_vcpu_set_mp_state(struct crosvm_vcpu *, + const struct kvm_mp_state *__mp_state); + #ifdef __cplusplus } #endif diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs index e5c9844..3fa4338 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, kvm_cpuid_entry2}; +use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msr_entry, kvm_cpuid_entry2, + kvm_lapic_state, kvm_mp_state, kvm_pic_state, kvm_ioapic_state, kvm_pit_state2}; use plugin_proto::*; @@ -354,6 +355,34 @@ impl crosvm { Ok(()) } + fn get_state(&mut self, state_set: MainRequest_StateSet, out: &mut [u8]) + -> result::Result<(), c_int> { + let mut r = MainRequest::new(); + r.mut_get_state().set = state_set; + let (response, _) = self.main_transaction(&r, &[])?; + if !response.has_get_state() { + return Err(EPROTO); + } + let get_state: &MainResponse_GetState = response.get_get_state(); + if get_state.state.len() != out.len() { + return Err(EPROTO); + } + out.copy_from_slice(&get_state.state); + Ok(()) + } + + fn set_state(&mut self, state_set: MainRequest_StateSet, new_state: &[u8]) + -> result::Result<(), c_int> { + let mut r = MainRequest::new(); + { + let set_state: &mut MainRequest_SetState = r.mut_set_state(); + set_state.set = state_set; + set_state.state = new_state.to_vec(); + } + self.main_transaction(&r, &[])?; + Ok(()) + } + fn set_identity_map_addr(&mut self, addr: u32) -> result::Result<(), c_int> { let mut r = MainRequest::new(); r.mut_set_identity_map_addr().address = addr; @@ -1002,6 +1031,71 @@ pub unsafe extern "C" fn crosvm_set_irq_routing(self_: *mut crosvm, } #[no_mangle] +pub unsafe extern "C" fn crosvm_get_pic_state(this: *mut crosvm, + primary: bool, + state: *mut kvm_pic_state) + -> c_int { + let this = &mut *this; + let state_set = if primary { MainRequest_StateSet::PIC0 } else { MainRequest_StateSet::PIC1 }; + let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_pic_state>()); + let ret = this.get_state(state_set, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_set_pic_state(this: *mut crosvm, + primary: bool, + state: *mut kvm_pic_state) + -> c_int { + let this = &mut *this; + let state_set = if primary { MainRequest_StateSet::PIC0 } else { MainRequest_StateSet::PIC1 }; + let state = from_raw_parts(state as *mut u8, size_of::<kvm_pic_state>()); + let ret = this.set_state(state_set, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_get_ioapic_state(this: *mut crosvm, + state: *mut kvm_ioapic_state) + -> c_int { + let this = &mut *this; + let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_ioapic_state>()); + let ret = this.get_state(MainRequest_StateSet::IOAPIC, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_set_ioapic_state(this: *mut crosvm, + state: *const kvm_ioapic_state) + -> c_int { + + let this = &mut *this; + let state = from_raw_parts(state as *mut u8, size_of::<kvm_ioapic_state>()); + let ret = this.set_state(MainRequest_StateSet::IOAPIC, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_get_pit_state(this: *mut crosvm, + state: *mut kvm_pit_state2) + -> c_int { + let this = &mut *this; + let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_pit_state2>()); + let ret = this.get_state(MainRequest_StateSet::PIT, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_set_pit_state(this: *mut crosvm, + state: *const kvm_pit_state2) + -> c_int { + let this = &mut *this; + let state = from_raw_parts(state as *mut u8, size_of::<kvm_pit_state2>()); + let ret = this.set_state(MainRequest_StateSet::PIT, state); + to_crosvm_rc(ret) +} + +#[no_mangle] pub unsafe extern "C" fn crosvm_set_identity_map_addr(self_: *mut crosvm, addr: u32) -> c_int { let self_ = &mut (*self_); let ret = self_.set_identity_map_addr(addr); @@ -1164,3 +1258,45 @@ pub unsafe extern "C" fn crosvm_vcpu_set_cpuid(this: *mut crosvm_vcpu, let ret = this.set_cpuid(cpuid_entries); to_crosvm_rc(ret) } + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_get_lapic_state(this: *mut crosvm_vcpu, + state: *mut kvm_lapic_state) + -> c_int { + let this = &mut *this; + let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_lapic_state>()); + let ret = this.get_state(VcpuRequest_StateSet::LAPIC, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_set_lapic_state(this: *mut crosvm_vcpu, + state: *const kvm_lapic_state) + -> c_int { + + let this = &mut *this; + let state = from_raw_parts(state as *mut u8, size_of::<kvm_lapic_state>()); + let ret = this.set_state(VcpuRequest_StateSet::LAPIC, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_get_mp_state(this: *mut crosvm_vcpu, + state: *mut kvm_mp_state) + -> c_int { + let this = &mut *this; + let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_mp_state>()); + let ret = this.get_state(VcpuRequest_StateSet::MP, state); + to_crosvm_rc(ret) +} + +#[no_mangle] +pub unsafe extern "C" fn crosvm_vcpu_set_mp_state(this: *mut crosvm_vcpu, + state: *const kvm_mp_state) + -> c_int { + + let this = &mut *this; + let state = from_raw_parts(state as *mut u8, size_of::<kvm_mp_state>()); + let ret = this.set_state(VcpuRequest_StateSet::MP, state); + to_crosvm_rc(ret) +} diff --git a/plugin_proto/Cargo.toml b/plugin_proto/Cargo.toml index 534358d..381a0ea 100644 --- a/plugin_proto/Cargo.toml +++ b/plugin_proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plugin_proto" -version = "0.12.0" +version = "0.13.0" authors = ["The Chromium OS Authors"] build = "build.rs" diff --git a/plugin_proto/protos/plugin.proto b/plugin_proto/protos/plugin.proto index c67872c..58d0b40 100644 --- a/plugin_proto/protos/plugin.proto +++ b/plugin_proto/protos/plugin.proto @@ -110,6 +110,30 @@ message MainRequest { repeated Route routes = 1; } + // Each type refers to certain piece of VM state (such as PIT state). + // The structure of the data corresponds to the kvm structure. + enum StateSet { + // struct kvm_pic_state + PIC0 = 0; + // struct kvm_pic_state + PIC1 = 1; + // struct kvm_ioapic_state + IOAPIC = 2; + // struct kvm_pit_state2 + PIT = 3; + } + + message GetState { + StateSet set = 1; + } + + message SetState { + StateSet set = 1; + // The in memory representation of certain state, depending on the value + // of the StateSet. + bytes state = 2; + } + message SetIdentityMapAddr { uint32 address = 1; } @@ -142,10 +166,12 @@ message MainRequest { 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; + GetState get_state = 12; + SetState set_state = 13; + SetIdentityMapAddr set_identity_map_addr = 14; + PauseVcpus pause_vcpus = 15; + GetVcpus get_vcpus = 16; + Start start = 17; // 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; @@ -178,6 +204,12 @@ message MainResponse { message ReserveRange {} message SetIrq {} message SetIrqRouting {} + message GetState { + // The in memory representation of a state, depending on what StateSet was + // requested in GetState. + bytes state = 1; + } + message SetState {} message SetIdentityMapAddr {} message PauseVcpus {} // This message should also receive a socket fd per VCPU along with the data from reading this @@ -204,10 +236,12 @@ message MainResponse { ReserveRange reserve_range = 10; SetIrq set_irq = 11; SetIrqRouting set_irq_routing = 12; - SetIdentityMapAddr set_identity_map_addr = 13; - PauseVcpus pause_vcpus = 14; - GetVcpus get_vcpus = 15; - Start start = 16; + GetState get_state = 13; + SetState set_state = 14; + SetIdentityMapAddr set_identity_map_addr = 15; + PauseVcpus pause_vcpus = 16; + GetVcpus get_vcpus = 17; + Start start = 18; MemoryDirtyLog dirty_log = 101; } } @@ -226,8 +260,8 @@ message VcpuRequest { bytes data = 1; } - // Each type refers to a set of KVM VCPU registers. The structure of the data corresponds to the - // kvm structure. + // Each type refers to certain piece of VCPU state (a set registers, or something else). + // The structure of the data corresponds to the kvm structure. enum StateSet { // struct kvm_regs REGS = 0; @@ -237,6 +271,10 @@ message VcpuRequest { FPU = 2; // struct kvm_debugregs DEBUGREGS = 3; + // struct kvm_lapic_state + LAPIC = 4; + // struct kvm_mp_state + MP = 5; } message GetState { @@ -315,8 +353,9 @@ message VcpuResponse { message Resume {} message GetState { - // The in memory representation of a struct kvm_regs, struct kvm_sregs, or struct kvm_fpu, - // 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_lapic_state, or struct kvm_mp_state, depending on what StateSet was + // requested in GetState. bytes state = 1; } diff --git a/src/plugin/process.rs b/src/plugin/process.rs index a12c214..67f1b87 100644 --- a/src/plugin/process.rs +++ b/src/plugin/process.rs @@ -23,13 +23,70 @@ use protobuf; use protobuf::Message; use io_jail::Minijail; -use kvm::{Vm, IoeventAddress, NoDatamatch, IrqSource, IrqRoute, dirty_log_bitmap_size}; +use kvm::{Vm, IoeventAddress, NoDatamatch, IrqSource, IrqRoute, PicId, dirty_log_bitmap_size}; +use kvm_sys::{kvm_pic_state, kvm_ioapic_state, kvm_pit_state2}; use sys_util::{EventFd, MemoryMapping, Killable, Scm, SharedMemory, GuestAddress, Result as SysResult, Error as SysError, SIGRTMIN}; use plugin_proto::*; use super::*; +// Wrapper types to make the kvm state structs DataInit +use data_model::DataInit; +#[derive(Copy, Clone)] +struct VmPicState(kvm_pic_state); +unsafe impl DataInit for VmPicState {} +#[derive(Copy, Clone)] +struct VmIoapicState(kvm_ioapic_state); +unsafe impl DataInit for VmIoapicState {} +#[derive(Copy, Clone)] +struct VmPitState(kvm_pit_state2); +unsafe impl DataInit for VmPitState {} + +fn get_vm_state(vm: &Vm, state_set: MainRequest_StateSet) -> SysResult<Vec<u8>> { + Ok(match state_set { + MainRequest_StateSet::PIC0 => { + VmPicState(vm.get_pic_state(PicId::Primary)?).as_slice().to_vec() + } + MainRequest_StateSet::PIC1 => { + VmPicState(vm.get_pic_state(PicId::Secondary)?).as_slice().to_vec() + } + MainRequest_StateSet::IOAPIC => { + VmIoapicState(vm.get_ioapic_state()?).as_slice().to_vec() + } + MainRequest_StateSet::PIT => { + VmPitState(vm.get_pit_state()?).as_slice().to_vec() + } + }) +} + +fn set_vm_state(vm: &Vm, state_set: MainRequest_StateSet, state: &[u8]) -> SysResult<()> { + match state_set { + MainRequest_StateSet::PIC0 => { + vm.set_pic_state(PicId::Primary, + &VmPicState::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } + MainRequest_StateSet::PIC1 => { + vm.set_pic_state(PicId::Secondary, + &VmPicState::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } + MainRequest_StateSet::IOAPIC => { + vm.set_ioapic_state(&VmIoapicState::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } + MainRequest_StateSet::PIT => { + vm.set_pit_state(&VmPitState::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } + } +} + /// The status of a process, either that it is running, or that it exited under some condition. pub enum ProcessStatus { /// The process is running and therefore has no information about its result. @@ -487,6 +544,19 @@ impl Process { } else if request.has_set_irq_routing() { response.mut_set_irq_routing(); Self::handle_set_irq_routing(vm, request.get_set_irq_routing()) + } else if request.has_get_state() { + let response_state = response.mut_get_state(); + match get_vm_state(vm, request.get_get_state().set) { + Ok(state) => { + response_state.state = state; + Ok(()) + } + Err(e) => Err(e), + } + } else if request.has_set_state() { + response.mut_set_state(); + let set_state = request.get_set_state(); + set_vm_state(vm, set_state.set, set_state.get_state()) } else if request.has_set_identity_map_addr() { response.mut_set_identity_map_addr(); let addr = request.get_set_identity_map_addr().address; diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs index fe59dd6..bf40ccc 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_msrs, kvm_msr_entry, - KVM_CPUID_FLAG_SIGNIFCANT_INDEX}; + KVM_CPUID_FLAG_SIGNIFCANT_INDEX, kvm_lapic_state, kvm_mp_state}; use plugin_proto::*; use super::*; @@ -66,7 +66,12 @@ unsafe impl DataInit for VcpuFpu {} #[derive(Copy, Clone)] struct VcpuDebugregs(kvm_debugregs); unsafe impl DataInit for VcpuDebugregs {} - +#[derive(Copy, Clone)] +struct VcpuLapicState(kvm_lapic_state); +unsafe impl DataInit for VcpuLapicState {} +#[derive(Copy, Clone)] +struct VcpuMpState(kvm_mp_state); +unsafe impl DataInit for VcpuMpState {} fn get_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet) -> SysResult<Vec<u8>> { Ok(match state_set { @@ -76,6 +81,8 @@ fn get_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet) -> SysResult<Vec VcpuRequest_StateSet::DEBUGREGS => { VcpuDebugregs(vcpu.get_debugregs()?).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(), }) } @@ -101,6 +108,16 @@ fn set_vcpu_state(vcpu: &Vcpu, state_set: VcpuRequest_StateSet, state: &[u8]) -> .ok_or(SysError::new(EINVAL))? .0) } + VcpuRequest_StateSet::LAPIC => { + vcpu.set_lapic(&VcpuLapicState::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } + VcpuRequest_StateSet::MP => { + vcpu.set_mp_state(&VcpuMpState::from_slice(state) + .ok_or(SysError::new(EINVAL))? + .0) + } } } diff --git a/tests/plugin_vm_state.c b/tests/plugin_vm_state.c new file mode 100644 index 0000000..6026e29 --- /dev/null +++ b/tests/plugin_vm_state.c @@ -0,0 +1,119 @@ +/* + * 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 <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_pic_state pic_state; + ret = crosvm_get_pic_state(crosvm, false, &pic_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial PIC1 state: %d\n", ret); + return 1; + } + + if (pic_state.auto_eoi) { + fprintf(stderr, "unexpected value of auto_eoi flag\n"); + return 1; + } + + pic_state.auto_eoi = true; + ret = crosvm_set_pic_state(crosvm, false, &pic_state); + if (ret < 0) { + fprintf(stderr, "failed to update PIC1 state: %d\n", ret); + return 1; + } + + ret = crosvm_get_pic_state(crosvm, false, &pic_state); + if (ret < 0) { + fprintf(stderr, "failed to get updated PIC1 state: %d\n", ret); + return 1; + } + + if (!pic_state.auto_eoi) { + fprintf(stderr, "unexpected value of auto_eoi flag after update\n"); + return 1; + } + + // Test retrieving and setting IOAPIC state. + struct kvm_ioapic_state ioapic_state; + ret = crosvm_get_ioapic_state(crosvm, &ioapic_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial PIC1 state: %d\n", ret); + return 1; + } + + fprintf(stderr, "IOAPIC ID: %d\n", ioapic_state.id); + + if (ioapic_state.id != 0) { + fprintf(stderr, "unexpected value of IOAPIC ID: %d\n", ioapic_state.id); + return 1; + } + + ioapic_state.id = 1; + ret = crosvm_set_ioapic_state(crosvm, &ioapic_state); + if (ret < 0) { + fprintf(stderr, "failed to update PIC1 state: %d\n", ret); + return 1; + } + + ret = crosvm_get_ioapic_state(crosvm, &ioapic_state); + if (ret < 0) { + fprintf(stderr, "failed to get updated PIC1 state: %d\n", ret); + return 1; + } + + if (ioapic_state.id != 1) { + fprintf(stderr, "unexpected value of IOAPIC ID after update: %d\n", + ioapic_state.id); + return 1; + } + + // Test retrieving and setting PIT state. + struct kvm_pit_state2 pit_state; + ret = crosvm_get_pit_state(crosvm, &pit_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial PIT state: %d\n", ret); + return 1; + } + + if (pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY) { + fprintf(stderr, "unexpected value of KVM_PIT_FLAGS_HPET_LEGACY flag\n"); + return 1; + } + + pit_state.flags |= KVM_PIT_FLAGS_HPET_LEGACY; + ret = crosvm_set_pit_state(crosvm, &pit_state); + if (ret < 0) { + fprintf(stderr, "failed to update PIT state: %d\n", ret); + return 1; + } + + ret = crosvm_get_pit_state(crosvm, &pit_state); + if (ret < 0) { + fprintf(stderr, "failed to get updated PIT state: %d\n", ret); + return 1; + } + + if (!(pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY)) { + fprintf(stderr, + "unexpected value of KVM_PIT_FLAGS_HPET_LEGACY after update\n"); + return 1; + } + + return 0; +} diff --git a/tests/plugins.rs b/tests/plugins.rs index e858c9a..cb956bb 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -242,6 +242,11 @@ fn test_supported_cpuid() { } #[test] +fn test_vm_state_manipulation() { + test_plugin(include_str!("plugin_vm_state.c")); +} + +#[test] fn test_vcpu_pause() { test_plugin(include_str!("plugin_vcpu_pause.c")); } @@ -525,3 +530,89 @@ fn test_cpuid() { }; test_mini_plugin(&mini_plugin); } + +#[test] +fn test_vcpu_state_manipulation() { + let mini_plugin = MiniPlugin { + assembly_src: "org 0x1000 + bits 16 + mov byte [0x3000], 1", + src: r#" + #define KILL_ADDRESS 0x3000 + + int g_kill_evt; + bool success = false; + + 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) + { + int ret; + + struct kvm_lapic_state lapic; + ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic); + if (ret < 0) { + fprintf(stderr, "failed to get initial LAPIC state: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_set_lapic_state(vcpu, &lapic); + if (ret < 0) { + fprintf(stderr, "failed to update LAPIC state: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_get_lapic_state(vcpu, &lapic); + if (ret < 0) { + fprintf(stderr, "failed to get updated LAPIC state: %d\n", ret); + return 1; + } + + struct kvm_mp_state mp_state; + ret = crosvm_vcpu_get_mp_state(vcpu, &mp_state); + if (ret < 0) { + fprintf(stderr, "failed to get initial MP state: %d\n", ret); + return 1; + } + + ret = crosvm_vcpu_set_mp_state(vcpu, &mp_state); + if (ret < 0) { + fprintf(stderr, "failed to update MP state: %d\n", ret); + return 1; + } + + success = true; + return 0; + } + + 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 *mem) { + if (!success) { + fprintf(stderr, "test failed\n"); + return 1; + } + return 0; + }"#, + ..Default::default() + }; + test_mini_plugin(&mini_plugin); +} |