summary refs log tree commit diff
path: root/data_model
diff options
context:
space:
mode:
authorChirantan Ekbote <chirantan@chromium.org>2020-05-15 20:06:58 +0900
committerCommit Bot <commit-bot@chromium.org>2020-05-25 19:14:07 +0000
commite7d1221c9d5a4e23b6142ef466892ccf38cfde9c (patch)
tree62345d17a52b48aac24c125fab25ef33cccf2a1e /data_model
parentbe5824412cec9e55fdfb523c80e33393e1054140 (diff)
downloadcrosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.tar
crosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.tar.gz
crosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.tar.bz2
crosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.tar.lz
crosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.tar.xz
crosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.tar.zst
crosvm-e7d1221c9d5a4e23b6142ef466892ccf38cfde9c.zip
Make VolatileSlice ABI-compatible with iovec
Change VolatileSlice so that it is ABI-compatible with iovec.  This
allows us to directly pass in a VolatileSlice for a C function that
expects an iovec without having to create temporaries that convert from
one to the other.

Also change all the parameters from u64 to usize.  It's not possible to
address more memory than fits into a usize so having u64 here didn't
really provide much benefit and led to a lot of tedious casting back and
forth all over the place.

BUG=none
TEST=unit tests

Cq-Depend: chromium:2206621
Change-Id: I258f9123c603d9a4c6c5e2d4d10eb4aedf74466d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2203998
Tested-by: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Chirantan Ekbote <chirantan@chromium.org>
Diffstat (limited to 'data_model')
-rw-r--r--data_model/Cargo.toml1
-rw-r--r--data_model/src/volatile_memory.rs252
2 files changed, 149 insertions, 104 deletions
diff --git a/data_model/Cargo.toml b/data_model/Cargo.toml
index b1447a6..3909956 100644
--- a/data_model/Cargo.toml
+++ b/data_model/Cargo.toml
@@ -7,5 +7,6 @@ include = ["src/**/*", "Cargo.toml"]
 
 [dependencies]
 assertions = { path = "../assertions" } # provided by ebuild
+libc = "*"
 
 [workspace]
diff --git a/data_model/src/volatile_memory.rs b/data_model/src/volatile_memory.rs
index d834f0b..026be71 100644
--- a/data_model/src/volatile_memory.rs
+++ b/data_model/src/volatile_memory.rs
@@ -20,21 +20,25 @@
 //! not reordered or elided the access.
 
 use std::cmp::min;
-use std::fmt::{self, Display};
+use std::ffi::c_void;
+use std::fmt::{self, Debug, Display};
 use std::marker::PhantomData;
 use std::mem::size_of;
 use std::ptr::{copy, null_mut, read_volatile, write_bytes, write_volatile};
 use std::result;
+use std::slice;
 use std::usize;
 
+use libc::iovec;
+
 use crate::DataInit;
 
 #[derive(Eq, PartialEq, Debug)]
 pub enum VolatileMemoryError {
     /// `addr` is out of bounds of the volatile memory slice.
-    OutOfBounds { addr: u64 },
-    /// Taking a slice at `base` with `offset` would overflow `u64`.
-    Overflow { base: u64, offset: u64 },
+    OutOfBounds { addr: usize },
+    /// Taking a slice at `base` with `offset` would overflow `usize`.
+    Overflow { base: usize, offset: usize },
 }
 
 impl Display for VolatileMemoryError {
@@ -65,7 +69,7 @@ type Result<T> = VolatileMemoryResult<T>;
 ///
 /// ```
 /// # use data_model::*;
-/// # fn get_slice(offset: u64, count: u64) -> VolatileMemoryResult<()> {
+/// # fn get_slice(offset: usize, count: usize) -> VolatileMemoryResult<()> {
 ///   let mem_end = calc_offset(offset, count)?;
 ///   if mem_end > 100 {
 ///       return Err(VolatileMemoryError::OutOfBounds{addr: mem_end});
@@ -73,7 +77,7 @@ type Result<T> = VolatileMemoryResult<T>;
 /// # Ok(())
 /// # }
 /// ```
-pub fn calc_offset(base: u64, offset: u64) -> Result<u64> {
+pub fn calc_offset(base: usize, offset: usize) -> Result<usize> {
     match base.checked_add(offset) {
         None => Err(Error::Overflow { base, offset }),
         Some(m) => Ok(m),
@@ -84,109 +88,149 @@ pub fn calc_offset(base: u64, offset: u64) -> Result<u64> {
 pub trait VolatileMemory {
     /// Gets a slice of memory at `offset` that is `count` bytes in length and supports volatile
     /// access.
-    fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice>;
+    fn get_slice(&self, offset: usize, count: usize) -> Result<VolatileSlice>;
 
     /// Gets a `VolatileRef` at `offset`.
-    fn get_ref<T: DataInit>(&self, offset: u64) -> Result<VolatileRef<T>> {
-        let slice = self.get_slice(offset, size_of::<T>() as u64)?;
+    fn get_ref<T: DataInit>(&self, offset: usize) -> Result<VolatileRef<T>> {
+        let slice = self.get_slice(offset, size_of::<T>())?;
         Ok(VolatileRef {
-            addr: slice.addr as *mut T,
+            addr: slice.as_mut_ptr() as *mut T,
             phantom: PhantomData,
         })
     }
 }
 
-impl<'a> VolatileMemory for &'a mut [u8] {
-    fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice> {
-        let mem_end = calc_offset(offset, count)?;
-        if mem_end > self.len() as u64 {
-            return Err(Error::OutOfBounds { addr: mem_end });
-        }
-        Ok(unsafe { VolatileSlice::new((self.as_ptr() as u64 + offset) as *mut _, count) })
-    }
-}
-
-/// A slice of raw memory that supports volatile access.
-#[derive(Copy, Clone, Debug)]
+/// A slice of raw memory that supports volatile access. Like `std::io::IoSliceMut`, this type is
+/// guaranteed to be ABI-compatible with `libc::iovec` but unlike `IoSliceMut`, it doesn't
+/// automatically deref to `&mut [u8]`.
+#[derive(Copy, Clone)]
+#[repr(transparent)]
 pub struct VolatileSlice<'a> {
-    addr: *mut u8,
-    size: u64,
-    phantom: PhantomData<&'a u8>,
+    iov: iovec,
+    phantom: PhantomData<&'a mut [u8]>,
 }
 
 impl<'a> Default for VolatileSlice<'a> {
     fn default() -> VolatileSlice<'a> {
         VolatileSlice {
-            addr: null_mut(),
-            size: 0,
+            iov: iovec {
+                iov_base: null_mut(),
+                iov_len: 0,
+            },
             phantom: PhantomData,
         }
     }
 }
 
+struct DebugIovec(iovec);
+impl Debug for DebugIovec {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("iovec")
+            .field("iov_base", &self.0.iov_base)
+            .field("iov_len", &self.0.iov_len)
+            .finish()
+    }
+}
+
+impl<'a> Debug for VolatileSlice<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("VolatileSlice")
+            .field("iov", &DebugIovec(self.iov))
+            .field("phantom", &self.phantom)
+            .finish()
+    }
+}
+
 impl<'a> VolatileSlice<'a> {
     /// Creates a slice of raw memory that must support volatile access.
+    pub fn new(buf: &mut [u8]) -> VolatileSlice {
+        VolatileSlice {
+            iov: iovec {
+                iov_base: buf.as_mut_ptr() as *mut c_void,
+                iov_len: buf.len(),
+            },
+            phantom: PhantomData,
+        }
+    }
+
+    /// Creates a `VolatileSlice` from a pointer and a length.
     ///
-    /// To use this safely, the caller must guarantee that the memory at `addr` is `size` bytes long
-    /// and is available for the duration of the lifetime of the new `VolatileSlice`. The caller
-    /// must also guarantee that all other users of the given chunk of memory are using volatile
-    /// accesses.
-    pub unsafe fn new(addr: *mut u8, size: u64) -> VolatileSlice<'a> {
+    /// # Safety
+    ///
+    /// In order to use this method safely, `addr` must be valid for reads and writes of `len` bytes
+    /// and should live for the entire duration of lifetime `'a`.
+    pub unsafe fn from_raw_parts(addr: *mut u8, len: usize) -> VolatileSlice<'a> {
         VolatileSlice {
-            addr,
-            size,
+            iov: iovec {
+                iov_base: addr as *mut c_void,
+                iov_len: len,
+            },
             phantom: PhantomData,
         }
     }
 
-    /// Gets the address of this slice's memory.
-    pub fn as_ptr(&self) -> *mut u8 {
-        self.addr
+    /// Gets a const pointer to this slice's memory.
+    pub fn as_ptr(&self) -> *const u8 {
+        self.iov.iov_base as *const u8
+    }
+
+    /// Gets a mutable pointer to this slice's memory.
+    pub fn as_mut_ptr(&self) -> *mut u8 {
+        self.iov.iov_base as *mut u8
     }
 
     /// Gets the size of this slice.
-    pub fn size(&self) -> u64 {
-        self.size
+    pub fn size(&self) -> usize {
+        self.iov.iov_len
+    }
+
+    /// Returns this `VolatileSlice` as an iovec.
+    pub fn as_iovec(&self) -> iovec {
+        self.iov
+    }
+
+    /// Converts a slice of `VolatileSlice`s into a slice of `iovec`s.
+    pub fn as_iovecs<'slice>(iovs: &'slice [VolatileSlice<'_>]) -> &'slice [iovec] {
+        // Safe because `VolatileSlice` is ABI-compatible with `iovec`.
+        unsafe { slice::from_raw_parts(iovs.as_ptr() as *const iovec, iovs.len()) }
     }
 
     /// Creates a copy of this slice with the address increased by `count` bytes, and the size
     /// reduced by `count` bytes.
-    pub fn offset(self, count: u64) -> Result<VolatileSlice<'a>> {
-        let new_addr =
-            (self.addr as u64)
-                .checked_add(count)
-                .ok_or(VolatileMemoryError::Overflow {
-                    base: self.addr as u64,
-                    offset: count,
-                })?;
-        if new_addr > usize::MAX as u64 {
-            return Err(VolatileMemoryError::Overflow {
-                base: self.addr as u64,
+    pub fn offset(self, count: usize) -> Result<VolatileSlice<'a>> {
+        let new_addr = (self.as_mut_ptr() as usize).checked_add(count).ok_or(
+            VolatileMemoryError::Overflow {
+                base: self.as_mut_ptr() as usize,
                 offset: count,
-            });
-        }
+            },
+        )?;
         let new_size = self
-            .size
+            .size()
             .checked_sub(count)
             .ok_or(VolatileMemoryError::OutOfBounds { addr: new_addr })?;
+
         // Safe because the memory has the same lifetime and points to a subset of the memory of the
         // original slice.
-        unsafe { Ok(VolatileSlice::new(new_addr as *mut u8, new_size)) }
+        unsafe { Ok(VolatileSlice::from_raw_parts(new_addr as *mut u8, new_size)) }
     }
 
     /// Similar to `get_slice` but the returned slice outlives this slice.
     ///
     /// The returned slice's lifetime is still limited by the underlying data's lifetime.
-    pub fn sub_slice(self, offset: u64, count: u64) -> Result<VolatileSlice<'a>> {
+    pub fn sub_slice(self, offset: usize, count: usize) -> Result<VolatileSlice<'a>> {
         let mem_end = calc_offset(offset, count)?;
-        if mem_end > self.size {
+        if mem_end > self.size() {
             return Err(Error::OutOfBounds { addr: mem_end });
         }
-        Ok(VolatileSlice {
-            addr: (self.addr as u64 + offset) as *mut _,
-            size: count,
-            phantom: PhantomData,
-        })
+        let new_addr = (self.as_mut_ptr() as usize).checked_add(offset).ok_or(
+            VolatileMemoryError::Overflow {
+                base: self.as_mut_ptr() as usize,
+                offset,
+            },
+        )?;
+
+        // Safe because we have verified that the new memory is a subset of the original slice.
+        Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
     }
 
     /// Sets each byte of this slice with the given byte, similar to `memset`.
@@ -196,13 +240,12 @@ impl<'a> VolatileSlice<'a> {
     /// # Examples
     ///
     /// ```
-    /// # use data_model::VolatileMemory;
+    /// # use data_model::VolatileSlice;
     /// # fn test_write_45() -> Result<(), ()> {
     /// let mut mem = [0u8; 32];
-    /// let mem_ref = &mut mem[..];
-    /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+    /// let vslice = VolatileSlice::new(&mut mem[..]);
     /// vslice.write_bytes(45);
-    /// for &mut v in mem_ref {
+    /// for &v in &mem[..] {
     ///     assert_eq!(v, 45);
     /// }
     /// # Ok(())
@@ -210,7 +253,7 @@ impl<'a> VolatileSlice<'a> {
     pub fn write_bytes(&self, value: u8) {
         // Safe because the memory is valid and needs only byte alignment.
         unsafe {
-            write_bytes(self.as_ptr(), value, self.size as usize);
+            write_bytes(self.as_mut_ptr(), value, self.size());
         }
     }
 
@@ -224,11 +267,10 @@ impl<'a> VolatileSlice<'a> {
     /// ```
     /// # use std::fs::File;
     /// # use std::path::Path;
-    /// # use data_model::VolatileMemory;
+    /// # use data_model::VolatileSlice;
     /// # fn test_write_null() -> Result<(), ()> {
     /// let mut mem = [0u8; 32];
-    /// let mem_ref = &mut mem[..];
-    /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+    /// let vslice = VolatileSlice::new(&mut mem[..]);
     /// let mut buf = [5u8; 16];
     /// vslice.copy_to(&mut buf[..]);
     /// for v in &buf[..] {
@@ -241,8 +283,8 @@ impl<'a> VolatileSlice<'a> {
     where
         T: DataInit,
     {
-        let mut addr = self.addr;
-        for v in buf.iter_mut().take(self.size as usize / size_of::<T>()) {
+        let mut addr = self.as_mut_ptr() as *const u8;
+        for v in buf.iter_mut().take(self.size() / size_of::<T>()) {
             unsafe {
                 *v = read_volatile(addr as *const T);
                 addr = addr.add(size_of::<T>());
@@ -256,18 +298,21 @@ impl<'a> VolatileSlice<'a> {
     /// # Examples
     ///
     /// ```
-    /// # use data_model::VolatileMemory;
+    /// # use data_model::{VolatileMemory, VolatileSlice};
     /// # fn test_write_null() -> Result<(), ()> {
     /// let mut mem = [0u8; 32];
-    /// let mem_ref = &mut mem[..];
-    /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+    /// let vslice = VolatileSlice::new(&mut mem[..]);
     /// vslice.copy_to_volatile_slice(vslice.get_slice(16, 16).map_err(|_| ())?);
     /// # Ok(())
     /// # }
     /// ```
     pub fn copy_to_volatile_slice(&self, slice: VolatileSlice) {
         unsafe {
-            copy(self.addr, slice.addr, min(self.size, slice.size) as usize);
+            copy(
+                self.as_mut_ptr() as *const u8,
+                slice.as_mut_ptr(),
+                min(self.size(), slice.size()),
+            );
         }
     }
 
@@ -281,11 +326,10 @@ impl<'a> VolatileSlice<'a> {
     /// ```
     /// # use std::fs::File;
     /// # use std::path::Path;
-    /// # use data_model::VolatileMemory;
+    /// # use data_model::{VolatileMemory, VolatileSlice};
     /// # fn test_write_null() -> Result<(), ()> {
     /// let mut mem = [0u8; 32];
-    /// let mem_ref = &mut mem[..];
-    /// let vslice = mem_ref.get_slice(0, 32).map_err(|_| ())?;
+    /// let vslice = VolatileSlice::new(&mut mem[..]);
     /// let buf = [5u8; 64];
     /// vslice.copy_from(&buf[..]);
     /// for i in 0..4 {
@@ -298,8 +342,8 @@ impl<'a> VolatileSlice<'a> {
     where
         T: DataInit,
     {
-        let mut addr = self.addr;
-        for &v in buf.iter().take(self.size as usize / size_of::<T>()) {
+        let mut addr = self.as_mut_ptr();
+        for &v in buf.iter().take(self.size() / size_of::<T>()) {
             unsafe {
                 write_volatile(addr as *mut T, v);
                 addr = addr.add(size_of::<T>());
@@ -309,16 +353,8 @@ impl<'a> VolatileSlice<'a> {
 }
 
 impl<'a> VolatileMemory for VolatileSlice<'a> {
-    fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice> {
-        let mem_end = calc_offset(offset, count)?;
-        if mem_end > self.size {
-            return Err(Error::OutOfBounds { addr: mem_end });
-        }
-        Ok(VolatileSlice {
-            addr: (self.addr as u64 + offset) as *mut _,
-            size: count,
-            phantom: PhantomData,
-        })
+    fn get_slice(&self, offset: usize, count: usize) -> Result<VolatileSlice> {
+        self.sub_slice(offset, count)
     }
 }
 
@@ -358,7 +394,7 @@ impl<'a, T: DataInit> VolatileRef<'a, T> {
     }
 
     /// Gets the address of this slice's memory.
-    pub fn as_ptr(&self) -> *mut T {
+    pub fn as_mut_ptr(&self) -> *mut T {
         self.addr
     }
 
@@ -370,10 +406,10 @@ impl<'a, T: DataInit> VolatileRef<'a, T> {
     /// # use std::mem::size_of;
     /// # use data_model::VolatileRef;
     ///   let v_ref = unsafe { VolatileRef::new(0 as *mut u32) };
-    ///   assert_eq!(v_ref.size(), size_of::<u32>() as u64);
+    ///   assert_eq!(v_ref.size(), size_of::<u32>());
     /// ```
-    pub fn size(&self) -> u64 {
-        size_of::<T>() as u64
+    pub fn size(&self) -> usize {
+        size_of::<T>()
     }
 
     /// Does a volatile write of the value `v` to the address of this ref.
@@ -393,7 +429,7 @@ impl<'a, T: DataInit> VolatileRef<'a, T> {
 
     /// Converts this `T` reference to a raw slice with the same size and address.
     pub fn to_slice(&self) -> VolatileSlice<'a> {
-        unsafe { VolatileSlice::new(self.addr as *mut u8, size_of::<T>() as u64) }
+        unsafe { VolatileSlice::from_raw_parts(self.as_mut_ptr() as *mut u8, self.size()) }
     }
 }
 
@@ -418,19 +454,27 @@ mod tests {
     }
 
     impl VolatileMemory for VecMem {
-        fn get_slice(&self, offset: u64, count: u64) -> Result<VolatileSlice> {
+        fn get_slice(&self, offset: usize, count: usize) -> Result<VolatileSlice> {
             let mem_end = calc_offset(offset, count)?;
-            if mem_end > self.mem.len() as u64 {
+            if mem_end > self.mem.len() {
                 return Err(Error::OutOfBounds { addr: mem_end });
             }
-            Ok(unsafe { VolatileSlice::new((self.mem.as_ptr() as u64 + offset) as *mut _, count) })
+
+            let new_addr = (self.mem.as_ptr() as usize).checked_add(offset).ok_or(
+                VolatileMemoryError::Overflow {
+                    base: self.mem.as_ptr() as usize,
+                    offset,
+                },
+            )?;
+
+            Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
         }
     }
 
     #[test]
     fn ref_store() {
         let mut a = [0u8; 1];
-        let a_ref = &mut a[..];
+        let a_ref = VolatileSlice::new(&mut a[..]);
         let v_ref = a_ref.get_ref(0).unwrap();
         v_ref.store(2u8);
         assert_eq!(a[0], 2);
@@ -440,7 +484,7 @@ mod tests {
     fn ref_load() {
         let mut a = [5u8; 1];
         {
-            let a_ref = &mut a[..];
+            let a_ref = VolatileSlice::new(&mut a[..]);
             let c = {
                 let v_ref = a_ref.get_ref::<u8>(0).unwrap();
                 assert_eq!(v_ref.load(), 5u8);
@@ -457,11 +501,11 @@ mod tests {
     #[test]
     fn ref_to_slice() {
         let mut a = [1u8; 5];
-        let a_ref = &mut a[..];
+        let a_ref = VolatileSlice::new(&mut a[..]);
         let v_ref = a_ref.get_ref(1).unwrap();
         v_ref.store(0x12345678u32);
         let ref_slice = v_ref.to_slice();
-        assert_eq!(v_ref.as_ptr() as u64, ref_slice.as_ptr() as u64);
+        assert_eq!(v_ref.as_mut_ptr() as usize, ref_slice.as_mut_ptr() as usize);
         assert_eq!(v_ref.size(), ref_slice.size());
     }
 
@@ -506,7 +550,7 @@ mod tests {
 
     #[test]
     fn slice_overflow_error() {
-        use std::u64::MAX;
+        use std::usize::MAX;
         let a = VecMem::new(1);
         let res = a.get_slice(MAX, 1).unwrap_err();
         assert_eq!(
@@ -528,7 +572,7 @@ mod tests {
 
     #[test]
     fn ref_overflow_error() {
-        use std::u64::MAX;
+        use std::usize::MAX;
         let a = VecMem::new(1);
         let res = a.get_ref::<u8>(MAX).unwrap_err();
         assert_eq!(