summary refs log tree commit diff
path: root/kvm/src/lib.rs
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2017-08-02 14:08:29 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-08-05 22:07:53 -0700
commit09a95f5e74c47d43a1f6962fca108466d8cc31e5 (patch)
treeb99352962c0edcea90fa36dc39641d4b15f8c477 /kvm/src/lib.rs
parent7a9de27c36b9a5ea3e29b3508939c20e6a5c7779 (diff)
downloadcrosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.tar
crosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.tar.gz
crosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.tar.bz2
crosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.tar.lz
crosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.tar.xz
crosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.tar.zst
crosvm-09a95f5e74c47d43a1f6962fca108466d8cc31e5.zip
kvm: add device memory interface to Vm
This interface is to dynamically add and remove memory mappings to guest
physical address space.

TEST=cargo test
BUG=chromium:738638

Change-Id: I695775289d56686ef16a1e3cf7640c97d5da2662
Reviewed-on: https://chromium-review.googlesource.com/599040
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'kvm/src/lib.rs')
-rw-r--r--kvm/src/lib.rs72
1 files changed, 64 insertions, 8 deletions
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs
index 1a39cf1..084365c 100644
--- a/kvm/src/lib.rs
+++ b/kvm/src/lib.rs
@@ -12,10 +12,12 @@ mod cap;
 
 use std::fs::File;
 use std::mem;
+use std::collections::HashMap;
+use std::collections::hash_map::Entry;
 use std::os::raw::*;
 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 
-use libc::{open, O_RDWR, EINVAL, ENOSPC};
+use libc::{open, O_RDWR, EINVAL, ENOSPC, ENOENT};
 
 use kvm_sys::*;
 
@@ -150,6 +152,7 @@ impl Into<u64> for NoDatamatch {
 pub struct Vm {
     vm: File,
     guest_mem: GuestMemory,
+    device_memory: HashMap<u32, MemoryMapping>,
     next_mem_slot: usize,
 }
 
@@ -177,6 +180,7 @@ impl Vm {
             Ok(Vm {
                 vm: vm_file,
                 guest_mem: guest_mem,
+                device_memory: HashMap::new(),
                 next_mem_slot: next_mem_slot,
             })
         } else {
@@ -186,28 +190,58 @@ impl Vm {
 
     /// Inserts the given `MemoryMapping` into the VM's address space at `guest_addr`.
     ///
-    /// Note that memory inserted into the VM's address space must not overlap
-    /// with any other memory slot's region.
-    pub fn add_device_memory(&mut self, guest_addr: GuestAddress, mem: MemoryMapping) -> Result<()> {
+    /// The slot that was assigned the device memory mapping is returned on success. The slot can be
+    /// given to `Vm::remove_device_memory` to remove the memory from the VM's address space and
+    /// take back ownership of `mem`.
+    ///
+    /// Note that memory inserted into the VM's address space must not overlap with any other memory
+    /// slot's region.
+    pub fn add_device_memory(&mut self,
+                             guest_addr: GuestAddress,
+                             mem: MemoryMapping)
+                             -> Result<u32> {
         if guest_addr < self.guest_mem.end_addr() {
             return Err(Error::new(ENOSPC));
         }
 
+        let slot = self.next_mem_slot as u32;
+
         // Safe because we check that the given guest address is valid and has no overlaps. We also
         // know that the pointer and size are correct because the MemoryMapping interface ensures
-        // this.
+        // this. We take ownership of the memory mapping so that it won't be unmapped until the slot
+        // is removed.
         unsafe {
-            set_user_memory_region(&self.vm, self.next_mem_slot as u32,
+            set_user_memory_region(&self.vm, slot,
                                         guest_addr.offset() as u64,
                                         mem.size() as u64,
                                         mem.as_ptr() as u64)?;
         };
+        self.device_memory.insert(slot, mem);
         self.next_mem_slot += 1;
 
-        Ok(())
+        Ok(slot)
     }
 
-    /// Gets a reference to the memory at the given address in the VM's address space.
+    /// Removes device memory that was previously added at the given slot.
+    ///
+    /// Ownership of the host memory mapping associated with the given slot is returned on success.
+    pub fn remove_device_memory(&mut self, slot: u32) -> Result<MemoryMapping> {
+        match self.device_memory.entry(slot) {
+            Entry::Occupied(entry) => {
+                // Safe because the slot is checked against the list of device memory slots.
+                unsafe {
+                    set_user_memory_region(&self.vm, slot, 0, 0, 0)?;
+                }
+                Ok(entry.remove())
+            }
+            _ => Err(Error::new(-ENOENT))
+        }
+    }
+
+    /// Gets a reference to the guest memory owned by this VM.
+    ///
+    /// Note that `GuestMemory` does not include any device memory that may have been added after
+    /// this VM was constructed.
     pub fn get_memory(&self) -> &GuestMemory {
         &self.guest_mem
     }
@@ -705,6 +739,28 @@ mod tests {
         vm.add_device_memory(GuestAddress(0x1000), mem).unwrap();
     }
 
+    #[test]
+    fn remove_memory() {
+        let kvm = Kvm::new().unwrap();
+        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+        let mut vm = Vm::new(&kvm, gm).unwrap();
+        let mem_size = 0x1000;
+        let mem = MemoryMapping::new(mem_size).unwrap();
+        let mem_ptr = mem.as_ptr();
+        let slot = vm.add_device_memory(GuestAddress(0x1000), mem).unwrap();
+        let mem = vm.remove_device_memory(slot).unwrap();
+        assert_eq!(mem.size(), mem_size);
+        assert_eq!(mem.as_ptr(), mem_ptr);
+    }
+
+    #[test]
+    fn remove_invalid_memory() {
+        let kvm = Kvm::new().unwrap();
+        let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
+        let mut vm = Vm::new(&kvm, gm).unwrap();
+        assert!(vm.remove_device_memory(0).unwrap());
+    }
+
      #[test]
     fn overlap_memory() {
         let kvm = Kvm::new().unwrap();