diff options
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | crosvm_plugin/Cargo.toml | 2 | ||||
-rw-r--r-- | crosvm_plugin/crosvm.h | 9 | ||||
-rw-r--r-- | crosvm_plugin/src/lib.rs | 70 | ||||
-rw-r--r-- | kvm/src/lib.rs | 53 | ||||
-rw-r--r-- | plugin_proto/Cargo.toml | 2 | ||||
-rw-r--r-- | plugin_proto/protos/plugin.proto | 27 | ||||
-rw-r--r-- | src/plugin/vcpu.rs | 46 | ||||
-rw-r--r-- | tests/plugins.rs | 79 |
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); +} |