diff options
author | Zach Reizner <zachr@google.com> | 2018-10-11 18:12:46 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-10-12 23:07:10 -0700 |
commit | 7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a (patch) | |
tree | 9e5907ddb9f2420e6bf76b2f4d9c0a17c499f8ca | |
parent | 73a40e37a338e43b33daf823a3dbb46444bfcdce (diff) | |
download | crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.tar crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.tar.gz crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.tar.bz2 crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.tar.lz crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.tar.xz crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.tar.zst crosvm-7c78a3c9484fe0cf5669fb28f9f7ec68f9b7550a.zip |
kvm: fix clippy error about multiple mut references
TEST=cargo test -p kvm BUG=None Change-Id: I2d4552b693f253f8411199ecf4583553c80e37a6 Reviewed-on: https://chromium-review.googlesource.com/1277874 Commit-Ready: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
-rw-r--r-- | kvm/src/lib.rs | 134 | ||||
-rw-r--r-- | kvm/tests/read_only_memory.rs | 8 | ||||
-rw-r--r-- | kvm/tests/real_run_adder.rs | 8 | ||||
-rw-r--r-- | src/linux.rs | 39 | ||||
-rw-r--r-- | src/plugin/mod.rs | 47 |
5 files changed, 186 insertions, 50 deletions
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs index 42a58f3..afde649 100644 --- a/kvm/src/lib.rs +++ b/kvm/src/lib.rs @@ -11,12 +11,14 @@ extern crate sys_util; mod cap; +use std::cmp::min; use std::collections::hash_map::Entry; use std::collections::{BinaryHeap, HashMap}; use std::fs::File; use std::mem::size_of; use std::os::raw::*; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::ptr::copy_nonoverlapping; use libc::sigset_t; use libc::{open, EINVAL, ENOENT, ENOSPC, O_CLOEXEC, O_RDWR}; @@ -865,21 +867,37 @@ impl AsRawFd for Vm { } } -/// A reason why a VCPU exited. One of these returns everytim `Vcpu::run` is called. +/// A reason why a VCPU exited. One of these returns every time `Vcpu::run` is called. #[derive(Debug)] -pub enum VcpuExit<'a> { +pub enum VcpuExit { /// An out port instruction was run on the given port with the given data. - IoOut(u16 /* port */, &'a [u8] /* data */), + IoOut { + port: u16, + size: usize, + data: [u8; 8], + }, /// An in port instruction was run on the given port. /// - /// The given slice should be filled in before `Vcpu::run` is called again. - IoIn(u16 /* port */, &'a mut [u8] /* data */), + /// The date that the instruction receives should be set with `set_data` before `Vcpu::run` is + /// called again. + IoIn { + port: u16, + size: usize, + }, /// A read instruction was run against the given MMIO address. /// - /// The given slice should be filled in before `Vcpu::run` is called again. - MmioRead(u64 /* address */, &'a mut [u8]), + /// The date that the instruction receives should be set with `set_data` before `Vcpu::run` is + /// called again. + MmioRead { + address: u64, + size: usize, + }, /// A write instruction was run against the given MMIO address with the given data. - MmioWrite(u64 /* address */, &'a [u8]), + MmioWrite { + address: u64, + size: usize, + data: [u8; 8], + }, Unknown, Exception, Hypercall, @@ -938,54 +956,110 @@ impl Vcpu { Ok(Vcpu { vcpu, run_mmap }) } - fn get_run(&self) -> &mut kvm_run { + /// Sets the data received by an mmio or ioport read/in instruction. + /// + /// This function should be called after `Vcpu::run` returns an `VcpuExit::IoIn` or + /// `Vcpu::MmioRead`. + #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))] + pub fn set_data(&self, data: &[u8]) -> Result<()> { // Safe because we know we mapped enough memory to hold the kvm_run struct because the - // kernel told us how large it was. - unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) } + // kernel told us how large it was. The pointer is page aligned so casting to a different + // type is well defined, hence the clippy allow attribute. + let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut kvm_run) }; + match run.exit_reason { + KVM_EXIT_IO => { + let run_start = run as *mut kvm_run as *mut u8; + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let io = unsafe { run.__bindgen_anon_1.io }; + if io.direction as u32 != KVM_EXIT_IO_IN { + return Err(Error::new(EINVAL)); + } + let data_size = (io.count as usize) * (io.size as usize); + if data_size != data.len() { + return Err(Error::new(EINVAL)); + } + // The data_offset is defined by the kernel to be some number of bytes into the + // kvm_run structure, which we have fully mmap'd. + unsafe { + let data_ptr = run_start.offset(io.data_offset as isize); + copy_nonoverlapping(data.as_ptr(), data_ptr, data_size); + } + Ok(()) + } + KVM_EXIT_MMIO => { + // Safe because the exit_reason (which comes from the kernel) told us which + // union field to use. + let mmio = unsafe { &mut run.__bindgen_anon_1.mmio }; + if mmio.is_write != 0 { + return Err(Error::new(EINVAL)); + } + let len = mmio.len as usize; + if len != data.len() { + return Err(Error::new(EINVAL)); + } + mmio.data[..len].copy_from_slice(data); + Ok(()) + } + _ => Err(Error::new(EINVAL)), + } } /// Runs the VCPU until it exits, returning the reason. /// /// Note that the state of the VCPU and associated VM must be setup first for this to do /// anything useful. + #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))] + // The pointer is page aligned so casting to a different type is well defined, hence the clippy + // allow attribute. pub fn run(&self) -> Result<VcpuExit> { // Safe because we know that our file is a VCPU fd and we verify the return result. let ret = unsafe { ioctl(self, KVM_RUN()) }; if ret == 0 { - let run = self.get_run(); + // Safe because we know we mapped enough memory to hold the kvm_run struct because the + // kernel told us how large it was. + let run = unsafe { &*(self.run_mmap.as_ptr() as *const kvm_run) }; match run.exit_reason { KVM_EXIT_IO => { - let run_start = run as *mut kvm_run as *mut u8; // Safe because the exit_reason (which comes from the kernel) told us which // union field to use. let io = unsafe { run.__bindgen_anon_1.io }; let port = io.port; - let data_size = io.count as usize * io.size as usize; - // The data_offset is defined by the kernel to be some number of bytes into the - // kvm_run stucture, which we have fully mmap'd. - let data_ptr = unsafe { run_start.offset(io.data_offset as isize) }; - // The slice's lifetime is limited to the lifetime of this Vcpu, which is equal - // to the mmap of the kvm_run struct that this is slicing from - let data_slice = unsafe { - std::slice::from_raw_parts_mut::<u8>(data_ptr as *mut u8, data_size) - }; + let size = (io.count as usize) * (io.size as usize); match io.direction as u32 { - KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn(port, data_slice)), - KVM_EXIT_IO_OUT => Ok(VcpuExit::IoOut(port, data_slice)), + KVM_EXIT_IO_IN => Ok(VcpuExit::IoIn { port, size }), + KVM_EXIT_IO_OUT => { + let mut data = [0; 8]; + let run_start = run as *const kvm_run as *const u8; + // The data_offset is defined by the kernel to be some number of bytes + // into the kvm_run structure, which we have fully mmap'd. + unsafe { + let data_ptr = run_start.offset(io.data_offset as isize); + copy_nonoverlapping( + data_ptr, + data.as_mut_ptr(), + min(size, data.len()), + ); + } + Ok(VcpuExit::IoOut { port, size, data }) + } _ => Err(Error::new(EINVAL)), } } KVM_EXIT_MMIO => { // Safe because the exit_reason (which comes from the kernel) told us which // union field to use. - let mmio = unsafe { &mut run.__bindgen_anon_1.mmio }; - let addr = mmio.phys_addr; - let len = mmio.len as usize; - let data_slice = &mut mmio.data[..len]; + let mmio = unsafe { &run.__bindgen_anon_1.mmio }; + let address = mmio.phys_addr; + let size = min(mmio.len as usize, mmio.data.len()); if mmio.is_write != 0 { - Ok(VcpuExit::MmioWrite(addr, data_slice)) + Ok(VcpuExit::MmioWrite { + address, + size, + data: mmio.data, + }) } else { - Ok(VcpuExit::MmioRead(addr, data_slice)) + Ok(VcpuExit::MmioRead { address, size }) } } KVM_EXIT_UNKNOWN => Ok(VcpuExit::Unknown), diff --git a/kvm/tests/read_only_memory.rs b/kvm/tests/read_only_memory.rs index 68d0c45..4bd4778 100644 --- a/kvm/tests/read_only_memory.rs +++ b/kvm/tests/read_only_memory.rs @@ -79,8 +79,12 @@ fn test_run() { loop { match vcpu.run().expect("run failed") { VcpuExit::Hlt => break, - VcpuExit::MmioWrite(addr, data) => { - assert_eq!(addr, vcpu_sregs.es.base); + VcpuExit::MmioWrite { + address, + size: 1, + data, + } => { + assert_eq!(address, vcpu_sregs.es.base); assert_eq!(data[0] as u64, vcpu_regs.rax + 1); exits += 1; } diff --git a/kvm/tests/real_run_adder.rs b/kvm/tests/real_run_adder.rs index e3ce7ac..0ca695f 100644 --- a/kvm/tests/real_run_adder.rs +++ b/kvm/tests/real_run_adder.rs @@ -55,8 +55,12 @@ fn test_run() { let mut out = String::new(); loop { match vcpu.run().expect("run failed") { - VcpuExit::IoOut(0x3f8, data) => { - assert_eq!(data.len(), 1); + VcpuExit::IoOut { + port: 0x3f8, + size, + data, + } => { + assert_eq!(size, 1); out.push(data[0] as char); } VcpuExit::Hlt => break, diff --git a/src/linux.rs b/src/linux.rs index 91ea1ae..b45b956 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -668,17 +668,40 @@ fn run_vcpu( match run_res { Ok(run) => { match run { - VcpuExit::IoIn(addr, data) => { - io_bus.read(addr as u64, data); + VcpuExit::IoIn { port, mut size } => { + let mut data = [0; 8]; + if size > data.len() { + error!("unsupported IoIn size of {} bytes", size); + size = data.len(); + } + io_bus.read(port as u64, &mut data[..size]); + if let Err(e) = vcpu.set_data(&data[..size]) { + error!("failed to set return data for IoIn: {:?}", e); + } } - VcpuExit::IoOut(addr, data) => { - io_bus.write(addr as u64, data); + VcpuExit::IoOut { + port, + mut size, + data, + } => { + if size > data.len() { + error!("unsupported IoOut size of {} bytes", size); + size = data.len(); + } + io_bus.write(port as u64, &data[..size]); } - VcpuExit::MmioRead(addr, data) => { - mmio_bus.read(addr, data); + VcpuExit::MmioRead { address, mut size } => { + let mut data = [0; 8]; + mmio_bus.read(address, &mut data[..size]); + // Setting data for mmio can not fail. + let _ = vcpu.set_data(&data[..size]); } - VcpuExit::MmioWrite(addr, data) => { - mmio_bus.write(addr, data); + VcpuExit::MmioWrite { + address, + size, + data, + } => { + mmio_bus.write(address, &data[..size]); } VcpuExit::Hlt => break, VcpuExit::Shutdown => break, diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 8cd81b4..9f229ac 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -363,17 +363,48 @@ pub fn run_vcpus( let run_res = vcpu.run(); match run_res { Ok(run) => match run { - VcpuExit::IoIn(addr, data) => { - vcpu_plugin.io_read(addr as u64, data, &vcpu); + VcpuExit::IoIn { port, mut size } => { + let mut data = [0; 8]; + if size > data.len() { + error!("unsupported IoIn size of {} bytes", size); + size = data.len(); + } + vcpu_plugin.io_read(port as u64, &mut data[..size], &vcpu); + if let Err(e) = vcpu.set_data(&data[..size]) { + error!("failed to set return data for IoIn: {:?}", e); + } } - VcpuExit::IoOut(addr, data) => { - vcpu_plugin.io_write(addr as u64, data, &vcpu); + VcpuExit::IoOut { + port, + mut size, + data, + } => { + if size > data.len() { + error!("unsupported IoOut size of {} bytes", size); + size = data.len(); + } + vcpu_plugin.io_write(port as u64, &data[..size], &vcpu); } - VcpuExit::MmioRead(addr, data) => { - vcpu_plugin.mmio_read(addr as u64, data, &vcpu); + VcpuExit::MmioRead { address, mut size } => { + let mut data = [0; 8]; + vcpu_plugin.mmio_read( + address as u64, + &mut data[..size], + &vcpu, + ); + // Setting data for mmio can not fail. + let _ = vcpu.set_data(&data[..size]); } - VcpuExit::MmioWrite(addr, data) => { - vcpu_plugin.mmio_write(addr as u64, data, &vcpu); + VcpuExit::MmioWrite { + address, + size, + data, + } => { + vcpu_plugin.mmio_write( + address as u64, + &data[..size], + &vcpu, + ); } VcpuExit::Hlt => break, VcpuExit::Shutdown => break, |