summary refs log tree commit diff
path: root/src/hw/virtio
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2017-07-12 17:52:05 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-07-20 15:00:37 -0700
commit3a4cca14a0a840971410b6e7dd3e1871939d0f03 (patch)
tree0cdcdaadb3734356323bb32a7d0593b5bffac9f0 /src/hw/virtio
parent2cfc2056aae2cf694791705db45cd2f4c2bd5373 (diff)
downloadcrosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.tar
crosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.tar.gz
crosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.tar.bz2
crosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.tar.lz
crosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.tar.xz
crosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.tar.zst
crosvm-3a4cca14a0a840971410b6e7dd3e1871939d0f03.zip
crosvm: block - Fill upper 4 bytes of disk size
Fill the upper for bytes of the disk size configuration.  The size is a
64bit value accessed with two 32bit reads.

The guest is permitted to read at any byte offset in the config space.
Allow it to do so, even if it doesn't make much sense.

Change-Id: I5d02620a8751b31784e419ae6a57173a2e212b8f
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/569359
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'src/hw/virtio')
-rw-r--r--src/hw/virtio/block.rs67
1 files changed, 55 insertions, 12 deletions
diff --git a/src/hw/virtio/block.rs b/src/hw/virtio/block.rs
index 7a6175b..fbcc663 100644
--- a/src/hw/virtio/block.rs
+++ b/src/hw/virtio/block.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::cmp;
 use std::fs::File;
 use std::io::{Seek, SeekFrom, Read, Write};
 use std::os::unix::io::{AsRawFd, RawFd};
@@ -10,8 +11,6 @@ use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::thread::spawn;
 
-use byteorder::{ByteOrder, LittleEndian};
-
 use sys_util::Result as SysResult;
 use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, Poller};
 
@@ -20,6 +19,7 @@ use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TY
 const QUEUE_SIZE: u16 = 256;
 const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE];
 const SECTOR_SHIFT: u8 = 9;
+const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
 
 const VIRTIO_BLK_T_IN: u32 = 0;
 const VIRTIO_BLK_T_OUT: u32 = 1;
@@ -263,8 +263,20 @@ impl Worker {
 /// Virtio device for exposing block level read/write operations on a host file.
 pub struct Block {
     kill_evt: Option<EventFd>,
-    disk_size: u64,
     disk_image: Option<File>,
+    config_space: Vec<u8>,
+}
+
+fn build_config_space(disk_size: u64) -> Vec<u8> {
+    // We only support disk size, which uses the first two words of the configuration space.
+    // If the image is not a multiple of the sector size, the tail bits are not exposed.
+    // The config space is little endian.
+    let mut config = Vec::with_capacity(8);
+    let num_sectors = disk_size >> SECTOR_SHIFT;
+    for i in 0..8 {
+        config.push((num_sectors >> (8 * i)) as u8);
+    }
+    config
 }
 
 impl Block {
@@ -273,10 +285,16 @@ impl Block {
     /// The given file must be seekable and sizable.
     pub fn new(mut disk_image: File) -> SysResult<Block> {
         let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
+        if disk_size % SECTOR_SIZE != 0 {
+            println!("block: Disk size {} is not a multiple of sector size {}; \
+                         the remainder will not be visible to the guest.",
+                     disk_size,
+                     SECTOR_SIZE);
+        }
         Ok(Block {
                kill_evt: None,
-               disk_size: disk_size,
                disk_image: Some(disk_image),
+               config_space: build_config_space(disk_size),
            })
     }
 }
@@ -309,14 +327,16 @@ impl VirtioDevice for Block {
         QUEUE_SIZES
     }
 
-    fn read_config(&self, offset: u64, data: &mut [u8]) {
-        // We only support the first word of configuration space.
-        let v = match offset {
-            0 => ((self.disk_size + 0x511) >> SECTOR_SHIFT) as u32,
-            _ => return,
-        };
-
-        LittleEndian::write_u32(data, v);
+    fn read_config(&self, offset: u64, mut data: &mut [u8]) {
+        let config_len = self.config_space.len() as u64;
+        if offset >= config_len {
+            return;
+        }
+        if let Some(end) = offset.checked_add(data.len() as u64) {
+            // This write can't fail, offset and end are checked against config_len.
+            data.write(&self.config_space[offset as usize..cmp::min(end, config_len) as usize])
+                .unwrap();
+        }
     }
 
     fn activate(&mut self,
@@ -353,3 +373,26 @@ impl VirtioDevice for Block {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use std::path::PathBuf;
+    use sys_util::TempDir;
+
+    use super::*;
+
+    #[test]
+    fn read_size() {
+        let tempdir = TempDir::new("/tmp/block_read_test").unwrap();
+        let mut path = PathBuf::from(tempdir.as_path().unwrap());
+        path.push("disk_image");
+        let f = File::create(&path).unwrap();
+        f.set_len(0x1000).unwrap();
+
+        let b = Block::new(f).unwrap();
+        let mut num_sectors = [0u8; 4];
+        b.read_config(0, &mut num_sectors);
+        // size is 0x1000, so num_sectors is 8 (4096/512).
+        assert_eq!([0x08, 0x00, 0x00, 0x00], num_sectors);
+    }
+}