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.h9
-rw-r--r--crosvm_plugin/src/lib.rs70
-rw-r--r--kvm/src/lib.rs53
-rw-r--r--plugin_proto/Cargo.toml2
-rw-r--r--plugin_proto/protos/plugin.proto27
-rw-r--r--src/plugin/vcpu.rs46
-rw-r--r--tests/plugins.rs79
9 files changed, 288 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 216fdc8..472f529 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.6.0",
+ "crosvm_plugin 0.7.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.6.0",
+ "plugin_proto 0.7.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.6.0"
+version = "0.7.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.6.0",
+ "plugin_proto 0.7.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.6.0"
+version = "0.7.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 6b37961..5b6833a 100644
--- a/crosvm_plugin/Cargo.toml
+++ b/crosvm_plugin/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "crosvm_plugin"
-version = "0.6.0"
+version = "0.7.0"
 authors = ["The Chromium OS Authors"]
 
 [lib]
diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h
index 16dc3a5..47f3eb9 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 6
+#define CROSVM_API_MINOR 7
 #define CROSVM_API_PATCH 0
 
 enum crosvm_address_space {
@@ -419,6 +419,13 @@ int crosvm_vcpu_get_debugregs(struct crosvm_vcpu*, struct kvm_debugregs*);
 /* Sets the state of the vcpu's debug registers */
 int crosvm_vcpu_set_debugregs(struct crosvm_vcpu*, const struct kvm_debugregs*);
 
+/* Gets the MSRs of the vcpu indicated by the index field of each entry. */
+int crosvm_vcpu_get_msrs(struct crosvm_vcpu*, uint32_t __msr_count,
+                         struct kvm_msr_entry *__msr_entries);
+/* Sets the MSRs of the vcpu indicated by the index field of each entry. */
+int crosvm_vcpu_set_msrs(struct crosvm_vcpu*, uint32_t __msr_count,
+                         const struct kvm_msr_entry *__msr_entries);
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs
index 1482853..91335d7 100644
--- a/crosvm_plugin/src/lib.rs
+++ b/crosvm_plugin/src/lib.rs
@@ -43,7 +43,7 @@ use sys_util::Scm;
 
 use kvm::dirty_log_bitmap_size;
 
-use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs};
+use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msr_entry};
 
 use plugin_proto::*;
 
@@ -692,6 +692,48 @@ impl crosvm_vcpu {
         self.vcpu_transaction(&r)?;
         Ok(())
     }
+
+    fn get_msrs(&mut self, msr_entries: &mut [kvm_msr_entry]) -> result::Result<(), c_int> {
+        let mut r = VcpuRequest::new();
+        {
+            let entry_indices: &mut Vec<u32> = r.mut_get_msrs().mut_entry_indices();
+            for entry in msr_entries.iter() {
+                entry_indices.push(entry.index);
+            }
+        }
+        let response = self.vcpu_transaction(&r)?;
+        if !response.has_get_msrs() {
+            return Err(-EPROTO);
+        }
+        let get_msrs: &VcpuResponse_GetMsrs = response.get_get_msrs();
+        if get_msrs.get_entry_data().len() != msr_entries.len() {
+            return Err(-EPROTO);
+        }
+        for (&msr_data, msr_entry) in
+            get_msrs
+                .get_entry_data()
+                .iter()
+                .zip(msr_entries.iter_mut()) {
+            msr_entry.data = msr_data;
+        }
+        Ok(())
+    }
+
+    fn set_msrs(&mut self, msr_entries: &[kvm_msr_entry]) -> result::Result<(), c_int> {
+        let mut r = VcpuRequest::new();
+        {
+            let set_msrs_entries: &mut RepeatedField<VcpuRequest_MsrEntry> = r.mut_set_msrs()
+                .mut_entries();
+            for msr_entry in msr_entries.iter() {
+                let mut entry = VcpuRequest_MsrEntry::new();
+                entry.index = msr_entry.index;
+                entry.data = msr_entry.data;
+                set_msrs_entries.push(entry);
+            }
+        }
+        self.vcpu_transaction(&r)?;
+        Ok(())
+    }
 }
 
 #[no_mangle]
@@ -935,3 +977,29 @@ pub unsafe extern "C" fn crosvm_vcpu_set_debugregs(this: *mut crosvm_vcpu,
         Err(e) => e,
     }
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_get_msrs(this: *mut crosvm_vcpu,
+                                              msr_count: u32,
+                                              msr_entries: *mut kvm_msr_entry)
+                                              -> c_int {
+    let this = &mut *this;
+    let msr_entries = from_raw_parts_mut(msr_entries, msr_count as usize);
+    match this.get_msrs(msr_entries) {
+        Ok(_) => 0,
+        Err(e) => e,
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_vcpu_set_msrs(this: *mut crosvm_vcpu,
+                                              msr_count: u32,
+                                              msr_entries: *const kvm_msr_entry)
+                                              -> c_int {
+    let this = &mut *this;
+    let msr_entries = from_raw_parts(msr_entries, msr_count as usize);
+    match this.set_msrs(msr_entries) {
+        Ok(_) => 0,
+        Err(e) => e,
+    }
+}
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index 82e8939..1c1ce2e 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -830,6 +830,41 @@ impl Vcpu {
         Ok(())
     }
 
+    /// X86 specific call to get the MSRS
+    ///
+    /// See the documentation for KVM_SET_MSRS.
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    pub fn get_msrs(&self, msr_entries: &mut [kvm_msr_entry]) -> Result<()> {
+        let vec_size_bytes = size_of::<kvm_msrs>() +
+                             (msr_entries.len() * size_of::<kvm_msr_entry>());
+        let vec: Vec<u8> = vec![0; vec_size_bytes];
+        let msrs: &mut kvm_msrs = unsafe {
+            // Converting the vector's memory to a struct is unsafe.  Carefully using the read-only
+            // vector to size and set the members ensures no out-of-bounds erros below.
+            &mut *(vec.as_ptr() as *mut kvm_msrs)
+        };
+        unsafe {
+            // Mapping the unsized array to a slice is unsafe becase the length isn't known.
+            // Providing the length used to create the struct guarantees the entire slice is valid.
+            let entries: &mut [kvm_msr_entry] = msrs.entries.as_mut_slice(msr_entries.len());
+            entries.copy_from_slice(&msr_entries);
+        }
+        msrs.nmsrs = msr_entries.len() as u32;
+        let ret = unsafe {
+            // Here we trust the kernel not to read or write past the end of the kvm_msrs struct.
+            ioctl_with_ref(self, KVM_GET_MSRS(), msrs)
+        };
+        if ret < 0 {
+            // KVM_SET_MSRS actually returns the number of msr entries written.
+            return errno_result();
+        }
+        unsafe {
+            let entries: &mut [kvm_msr_entry] = msrs.entries.as_mut_slice(msr_entries.len());
+            msr_entries.copy_from_slice(&entries);
+        }
+        Ok(())
+    }
+
     /// X86 specific call to setup the MSRS
     ///
     /// See the documentation for KVM_SET_MSRS.
@@ -1184,6 +1219,24 @@ mod tests {
     }
 
     #[test]
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn get_msrs() {
+        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();
+        vcpu.get_msrs(&mut [kvm_msr_entry {
+                                index: 0x0000011e,
+                                ..Default::default()
+                            },
+                            kvm_msr_entry {
+                                index: 0x000003f1,
+                                ..Default::default()
+                            }])
+            .unwrap();
+    }
+
+    #[test]
     fn vcpu_mmap_size() {
         let kvm = Kvm::new().unwrap();
         let mmap_size = kvm.get_vcpu_mmap_size().unwrap();
diff --git a/plugin_proto/Cargo.toml b/plugin_proto/Cargo.toml
index 74082b6..c9d771d 100644
--- a/plugin_proto/Cargo.toml
+++ b/plugin_proto/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "plugin_proto"
-version = "0.6.0"
+version = "0.7.0"
 authors = ["The Chromium OS Authors"]
 build = "build.rs"
 
diff --git a/plugin_proto/protos/plugin.proto b/plugin_proto/protos/plugin.proto
index e287581..7e7f137 100644
--- a/plugin_proto/protos/plugin.proto
+++ b/plugin_proto/protos/plugin.proto
@@ -206,6 +206,21 @@ message VcpuRequest {
         bytes state = 2;
     }
 
+    message GetMsrs {
+        // The entry data will be returned in the same order as this in the
+        // VcpuResponse::GetMsrs::entry_data array.
+        repeated uint32 entry_indices = 1;
+    }
+
+    message MsrEntry {
+        uint32 index = 1;
+        uint64 data = 2;
+    }
+
+    message SetMsrs {
+        repeated MsrEntry entries = 1;
+    }
+
     // The type of the message is determined by which of these oneof fields is present in the
     // protobuf.
     oneof message {
@@ -213,6 +228,8 @@ message VcpuRequest {
         Resume resume = 2;
         GetState get_state = 3;
         SetState set_state = 4;
+        GetMsrs get_msrs = 5;
+        SetMsrs set_msrs = 6;
     }
 }
 
@@ -257,6 +274,14 @@ message VcpuResponse  {
     message SetState {
     }
 
+    message GetMsrs {
+        // The order of the entry_data values is the same order as the array of indices given in the
+        // corresponding request.
+        repeated uint64 entry_data = 1;
+    }
+
+    message SetMsrs {}
+
     // 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
@@ -266,5 +291,7 @@ message VcpuResponse  {
         Resume resume = 3;
         GetState get_state = 4;
         SetState set_state = 5;
+        GetMsrs get_msrs = 6;
+        SetMsrs set_msrs = 7;
     }
 }
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
index ce8b67d..fcd6543 100644
--- a/src/plugin/vcpu.rs
+++ b/src/plugin/vcpu.rs
@@ -6,6 +6,7 @@ use std::cell::{Cell, RefCell};
 use std::cmp::min;
 use std::cmp::{self, Ord, PartialOrd, PartialEq};
 use std::collections::btree_set::BTreeSet;
+use std::mem::size_of;
 use std::os::unix::net::UnixDatagram;
 use std::sync::{Arc, Mutex, RwLock};
 
@@ -16,7 +17,7 @@ use protobuf::Message;
 
 use data_model::DataInit;
 use kvm::Vcpu;
-use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs};
+use kvm_sys::{kvm_regs, kvm_sregs, kvm_fpu, kvm_debugregs, kvm_msrs, kvm_msr_entry};
 use plugin_proto::*;
 
 use super::*;
@@ -404,6 +405,49 @@ 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_msrs() {
+            let entry_data = &mut response.mut_get_msrs().entry_data;
+            let entry_indices = &request.get_get_msrs().entry_indices;
+            let mut msr_entries = Vec::with_capacity(entry_indices.len());
+            for &index in entry_indices {
+                msr_entries.push(kvm_msr_entry {
+                                     index,
+                                     ..Default::default()
+                                 });
+            }
+            match vcpu.get_msrs(&mut msr_entries) {
+                Ok(()) => {
+                    for msr_entry in msr_entries {
+                        entry_data.push(msr_entry.data);
+                    }
+                    Ok(())
+                }
+                Err(e) => Err(e),
+            }
+        } else if request.has_set_msrs() {
+            response.mut_set_msrs();
+            let request_entries = &request.get_set_msrs().entries;
+            let vec_size_bytes = size_of::<kvm_msrs>() +
+                                 (request_entries.len() * size_of::<kvm_msr_entry>());
+            let vec: Vec<u8> = vec![0; vec_size_bytes];
+            let kvm_msrs: &mut kvm_msrs = unsafe {
+                // Converting the vector's memory to a struct is unsafe.  Carefully using the read-
+                // only vector to size and set the members ensures no out-of-bounds erros below.
+                &mut *(vec.as_ptr() as *mut kvm_msrs)
+            };
+            unsafe {
+                // Mapping the unsized array to a slice is unsafe becase the length isn't known.
+                // Providing the length used to create the struct guarantees the entire slice is
+                // valid.
+                let kvm_msr_entries: &mut [kvm_msr_entry] =
+                    kvm_msrs.entries.as_mut_slice(request_entries.len());
+                for (msr_entry, entry) in kvm_msr_entries.iter_mut().zip(request_entries.iter()) {
+                    msr_entry.index = entry.index;
+                    msr_entry.data = entry.data;
+                }
+            }
+            kvm_msrs.nmsrs = request_entries.len() as u32;
+            vcpu.set_msrs(&kvm_msrs)
         } else {
             Err(SysError::new(-ENOTTY))
         };
diff --git a/tests/plugins.rs b/tests/plugins.rs
index bdefff9..357ad9a 100644
--- a/tests/plugins.rs
+++ b/tests/plugins.rs
@@ -295,3 +295,82 @@ fn test_debugregs() {
     };
     test_mini_plugin(&mini_plugin);
 }
+
+#[test]
+fn test_msrs() {
+    let mini_plugin = MiniPlugin {
+        assembly_src: "org 0x1000
+             bits 16
+             rdmsr
+             mov [0x0], eax
+             mov [0x4], edx
+             mov ecx, ebx
+             mov eax, [0x8]
+             mov edx, [0xc]
+             wrmsr
+             mov byte [es:0], 1",
+        src: r#"
+            #define MSR1_INDEX 0x00000174
+            #define MSR1_DATA 1
+            #define MSR2_INDEX 0x00000175
+            #define MSR2_DATA 2
+            #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);
+                ((uint64_t*)mem)[1] = MSR2_DATA;
+                return 0;
+            }
+
+            int handle_vpcu_init(struct crosvm_vcpu *vcpu, struct kvm_regs *regs,
+                                 struct kvm_sregs *sregs)
+            {
+                regs->rcx = MSR1_INDEX;
+                regs->rbx = MSR2_INDEX;
+                sregs->es.base = KILL_ADDRESS;
+
+                struct kvm_msr_entry msr1 = {0};
+                msr1.index = MSR1_INDEX;
+                msr1.data = MSR1_DATA;
+                crosvm_vcpu_set_msrs(vcpu, 1, &msr1);
+
+                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;
+                    g_msr2.index = MSR2_INDEX;
+                    crosvm_vcpu_get_msrs(vcpu, 1, &g_msr2);
+                    write(g_kill_evt, &dummy, sizeof(dummy));
+                    return 1;
+                }
+                return 0;
+            }
+
+            int check_result(struct crosvm *vcpu, void *mem) {
+                uint64_t msr1_data = ((uint64_t*)mem)[0];
+                if (msr1_data != MSR1_DATA) {
+                    fprintf(stderr, "msr1 has unexpected value: 0x%x\n", msr1_data);
+                    return 1;
+                }
+                if (g_msr2.data != MSR2_DATA) {
+                    fprintf(stderr, "msr2 has unexpected value: 0x%x\n", g_msr2.data);
+                    return 1;
+                }
+                return 0;
+            }"#,
+        ..Default::default()
+    };
+    test_mini_plugin(&mini_plugin);
+}