From d0a0828b2e3a5dde4338f5b58ce355c75a79fa9d Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Tue, 14 Aug 2018 09:55:34 -0700 Subject: [PATCH] devices: block: define config space as a struct Define a struct to represent the virtio block configuration as defined in the current revision of the specification. Only the capacity field is set; all other fields are defaulted to 0, which should be safe, since they are all controlled by feature flags that our device does not advertise. This is prep work for adding discard/write zeroes support to the block device, since these commands require additional config fields that will be added at the end of the config struct. BUG=chromium:850998 TEST=cargo test -p devices; verify that container still boots and reports the correct size Change-Id: I8beb76195e446eb3dcbf1a99cc246ddd8cc8a7df Signed-off-by: Daniel Verkamp Reviewed-on: https://chromium-review.googlesource.com/1175235 Reviewed-by: Dylan Reid Reviewed-by: Zach Reizner --- devices/src/virtio/block.rs | 60 +++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs index ee4528b021..09223d4f5e 100644 --- a/devices/src/virtio/block.rs +++ b/devices/src/virtio/block.rs @@ -4,6 +4,7 @@ use std::cmp; use std::io::{self, Seek, SeekFrom, Read, Write}; +use std::mem::size_of_val; use std::os::unix::io::{AsRawFd, RawFd}; use std::result; use std::sync::Arc; @@ -13,6 +14,8 @@ use std::thread; use sys_util::Result as SysResult; use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, PollContext, PollToken}; +use data_model::{DataInit, Le16, Le32, Le64}; + use super::{VirtioDevice, Queue, DescriptorChain, INTERRUPT_STATUS_USED_RING, TYPE_BLOCK}; const QUEUE_SIZE: u16 = 256; @@ -31,6 +34,45 @@ const VIRTIO_BLK_S_UNSUPP: u8 = 2; const VIRTIO_BLK_F_RO: u32 = 5; const VIRTIO_BLK_F_FLUSH: u32 = 9; +#[derive(Copy, Clone, Debug, Default)] +#[repr(C)] +struct virtio_blk_geometry { + cylinders: Le16, + heads: u8, + sectors: u8, +} + +// Safe because it only has data and has no implicit padding. +unsafe impl DataInit for virtio_blk_geometry {} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(C)] +struct virtio_blk_topology { + physical_block_exp: u8, + alignment_offset: u8, + min_io_size: Le16, + opt_io_size: Le32, +} + +// Safe because it only has data and has no implicit padding. +unsafe impl DataInit for virtio_blk_topology {} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(C)] +struct virtio_blk_config { + capacity: Le64, + size_max: Le32, + seg_max: Le32, + geometry: virtio_blk_geometry, + blk_size: Le32, + topology: virtio_blk_topology, + writeback: u8, + unused0: [u8; 3], +} + +// Safe because it only has data and has no implicit padding. +unsafe impl DataInit for virtio_blk_config {} + pub trait DiskFile: Read + Seek + Write {} impl DiskFile for D {} @@ -344,21 +386,19 @@ impl Worker { pub struct Block { kill_evt: Option, disk_image: Option, - config_space: Vec, + config_space: virtio_blk_config, avail_features: u64, read_only: bool, } -fn build_config_space(disk_size: u64) -> Vec { +fn build_config_space(disk_size: u64) -> virtio_blk_config { // 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); + virtio_blk_config { + capacity: Le64::from(disk_size >> SECTOR_SHIFT), + ..Default::default() } - config } impl Block { @@ -426,13 +466,15 @@ impl VirtioDevice for Block { } fn read_config(&self, offset: u64, mut data: &mut [u8]) { - let config_len = self.config_space.len() as u64; + let config_len = size_of_val(&self.config_space) as u64; if offset >= config_len { return; } if let Some(end) = offset.checked_add(data.len() as u64) { + let offset = offset as usize; + let end = cmp::min(end, config_len) as usize; // This write can't fail, offset and end are checked against config_len. - data.write_all(&self.config_space[offset as usize..cmp::min(end, config_len) as usize]) + data.write_all(&self.config_space.as_slice()[offset..end]) .unwrap(); } }