summary refs log tree commit diff
path: root/crosvm_plugin
diff options
context:
space:
mode:
authorMatt Delco <delco@chromium.org>2019-11-01 16:45:46 -0700
committerCommit Bot <commit-bot@chromium.org>2019-11-06 23:01:15 +0000
commita52b2a6c8167eebb285b70b54077919bbf113a35 (patch)
tree600d2fec69fd89e3216a84b2bc64de510c7f9207 /crosvm_plugin
parent1de9cb53e1eccfaf138553c45a800f26927663bb (diff)
downloadcrosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.tar
crosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.tar.gz
crosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.tar.bz2
crosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.tar.lz
crosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.tar.xz
crosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.tar.zst
crosvm-a52b2a6c8167eebb285b70b54077919bbf113a35.zip
crosvm: add plugin API for async writes
A plugin might care to be immediately notified when a write
is made to a port, but it doesn't care to have the VM stopped
while the plugin calls back to resume the VM.

Unfortunately this means that multiple messages can be queued up in the
pipe and read() together by the plugin API.  Protobuf's parsing function
doesn't report how many bytes it read, so I've resorted to having crosvm
prefix every message with a length and then have the plugin lib parse
this number.  Impact on performance has not been measured.

BUG=b:143294496
TEST=Local build and run of build_test.  Verified that new unit
test was executed, exercised the case where multiple msgs are
received together, and completed successfully.

Change-Id: If6ef463e7b4d2e688e649f832a764fa644bf2d36
Signed-off-by: Matt Delco <delco@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1896376
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Diffstat (limited to 'crosvm_plugin')
-rw-r--r--crosvm_plugin/crosvm.h27
-rw-r--r--crosvm_plugin/src/lib.rs83
2 files changed, 98 insertions, 12 deletions
diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h
index 700ab0c..63763f1 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 18
+#define CROSVM_API_MINOR 19
 #define CROSVM_API_PATCH 0
 
 enum crosvm_address_space {
@@ -175,6 +175,23 @@ int crosvm_reserve_range(struct crosvm*, uint32_t __space, uint64_t __start,
                          uint64_t __length);
 
 /*
+ * Registers a range in the given address space that, when accessed via write,
+ * will cause a notification in crosvm_vcpu_wait() but the VM will continue
+ * running.
+ * For this type of notification (where |no_resume| is set) the next call
+ * should be crosvm_vcpu_wait() (without an inbetween call to
+ * crosvm_vcpu_resume() ).
+ *
+ * The requested range must not overlap any prior (and currently active)
+ * reservation to crosvm_reserve_range() or crosvm_reserve_async_write_range().
+ *
+ * To unreserve a range previously reserved by this function, pass the |__space|
+ * and |__start| of the old reservation with a 0 |__length|.
+ */
+int crosvm_reserve_async_write_range(struct crosvm*, uint32_t __space,
+                                     uint64_t __start, uint64_t __length);
+
+/*
  * Sets the state of the given irq pin.
  */
 int crosvm_set_irq(struct crosvm*, uint32_t __irq_id, bool __active);
@@ -509,7 +526,13 @@ struct crosvm_vcpu_event {
        */
       uint8_t is_write;
 
-      uint8_t _reserved[3];
+      /*
+       * Valid when |is_write| is true -- indicates that VM has continued
+       * to run.  The only next valid call for the vcpu is crosvm_vcpu_wait().
+       */
+      uint8_t no_resume;
+
+      uint8_t _reserved[2];
     } io_access;
 
     /* CROSVM_VCPU_EVENT_KIND_PAUSED */
diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs
index 8def28d..53a8569 100644
--- a/crosvm_plugin/src/lib.rs
+++ b/crosvm_plugin/src/lib.rs
@@ -171,6 +171,7 @@ pub enum Stat {
     GetMsrIndexList,
     NetGetConfig,
     ReserveRange,
+    ReserveAsyncWriteRange,
     SetIrq,
     SetIrqRouting,
     GetPicState,
@@ -466,12 +467,19 @@ impl crosvm {
         Ok(())
     }
 
-    fn reserve_range(&mut self, space: u32, start: u64, length: u64) -> result::Result<(), c_int> {
+    fn reserve_range(
+        &mut self,
+        space: u32,
+        start: u64,
+        length: u64,
+        async_write: bool,
+    ) -> result::Result<(), c_int> {
         let mut r = MainRequest::new();
         let reserve: &mut MainRequest_ReserveRange = r.mut_reserve_range();
         reserve.space = AddressSpace::from_i32(space as i32).ok_or(EINVAL)?;
         reserve.start = start;
         reserve.length = length;
+        reserve.async_write = async_write;
 
         self.main_transaction(&r, &[])?;
         Ok(())
@@ -910,7 +918,8 @@ struct anon_io_access {
     data: *mut u8,
     length: u32,
     is_write: u8,
-    __reserved1: u8,
+    no_resume: u8,
+    __reserved1: [u8; 2],
 }
 
 #[repr(C)]
@@ -949,6 +958,8 @@ pub struct crosvm_vcpu {
     send_init: bool,
     request_buffer: Vec<u8>,
     response_buffer: Vec<u8>,
+    response_base: usize,
+    response_length: usize,
     resume_data: Vec<u8>,
 
     regs: crosvm_vcpu_reg_cache,
@@ -956,6 +967,25 @@ pub struct crosvm_vcpu {
     debugregs: crosvm_vcpu_reg_cache,
 }
 
+fn read_varint32(data: &[u8]) -> (u32, usize) {
+    let mut value: u32 = 0;
+    let mut shift: u32 = 0;
+    for (i, &b) in data.iter().enumerate() {
+        if b < 0x80 {
+            return match (b as u32).checked_shl(shift) {
+                None => (0, 0),
+                Some(b) => (value | b, i + 1),
+            };
+        }
+        match ((b as u32) & 0x7F).checked_shl(shift) {
+            None => return (0, 0),
+            Some(b) => value |= b,
+        }
+        shift += 7;
+    }
+    (0, 0)
+}
+
 impl crosvm_vcpu {
     fn new(read_pipe: File, write_pipe: File) -> crosvm_vcpu {
         crosvm_vcpu {
@@ -964,6 +994,8 @@ impl crosvm_vcpu {
             send_init: true,
             request_buffer: Vec::new(),
             response_buffer: vec![0; MAX_DATAGRAM_SIZE],
+            response_base: 0,
+            response_length: 0,
             resume_data: Vec::new(),
             regs: crosvm_vcpu_reg_cache {
                 get: false,
@@ -994,13 +1026,30 @@ impl crosvm_vcpu {
     }
 
     fn vcpu_recv(&mut self) -> result::Result<VcpuResponse, c_int> {
-        let msg_size = self
-            .read_pipe
-            .read(&mut self.response_buffer)
-            .map_err(|e| -e.raw_os_error().unwrap_or(EINVAL))?;
-
-        let response: VcpuResponse =
-            parse_from_bytes(&self.response_buffer[..msg_size]).map_err(proto_error_to_int)?;
+        if self.response_length == 0 {
+            let msg_size = self
+                .read_pipe
+                .read(&mut self.response_buffer)
+                .map_err(|e| -e.raw_os_error().unwrap_or(EINVAL))?;
+            self.response_base = 0;
+            self.response_length = msg_size;
+        }
+        if self.response_length == 0 {
+            return Err(EINVAL);
+        }
+        let (value, bytes) = read_varint32(
+            &self.response_buffer[self.response_base..self.response_base + self.response_length],
+        );
+        let total_size: usize = bytes + value as usize;
+        if bytes == 0 || total_size > self.response_length {
+            return Err(EINVAL);
+        }
+        let response: VcpuResponse = parse_from_bytes(
+            &self.response_buffer[self.response_base + bytes..self.response_base + total_size],
+        )
+        .map_err(proto_error_to_int)?;
+        self.response_base += total_size;
+        self.response_length -= total_size;
         if response.errno != 0 {
             return Err(response.errno);
         }
@@ -1041,6 +1090,7 @@ impl crosvm_vcpu {
                 data: io.data.as_mut_ptr(),
                 length: io.data.len() as u32,
                 is_write: io.is_write as u8,
+                no_resume: io.no_resume as u8,
                 __reserved1: Default::default(),
             };
             self.resume_data = io.data;
@@ -1359,7 +1409,20 @@ pub unsafe extern "C" fn crosvm_reserve_range(
 ) -> c_int {
     let _u = record(Stat::ReserveRange);
     let self_ = &mut (*self_);
-    let ret = self_.reserve_range(space, start, length);
+    let ret = self_.reserve_range(space, start, length, false);
+    to_crosvm_rc(ret)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crosvm_reserve_async_write_range(
+    self_: *mut crosvm,
+    space: u32,
+    start: u64,
+    length: u64,
+) -> c_int {
+    let _u = record(Stat::ReserveAsyncWriteRange);
+    let self_ = &mut (*self_);
+    let ret = self_.reserve_range(space, start, length, true);
     to_crosvm_rc(ret)
 }