summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock10
-rw-r--r--crosvm_plugin/Cargo.toml2
-rw-r--r--crosvm_plugin/crosvm.h39
-rw-r--r--crosvm_plugin/src/lib.rs138
-rw-r--r--plugin_proto/Cargo.toml2
-rw-r--r--plugin_proto/protos/plugin.proto63
-rw-r--r--src/plugin/process.rs72
-rw-r--r--src/plugin/vcpu.rs21
-rw-r--r--tests/plugin_vm_state.c119
-rw-r--r--tests/plugins.rs91
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);
+}