123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- //! Driver for VirtIO block devices.
- use crate::hal::Hal;
- use crate::queue::VirtQueue;
- use crate::transport::Transport;
- use crate::volatile::{volread, Volatile};
- use crate::{Error, Result};
- use bitflags::bitflags;
- use log::info;
- use zerocopy::{AsBytes, FromBytes, FromZeroes};
- const QUEUE: u16 = 0;
- const QUEUE_SIZE: u16 = 16;
- const SUPPORTED_FEATURES: BlkFeature = BlkFeature::RO
- .union(BlkFeature::FLUSH)
- .union(BlkFeature::RING_INDIRECT_DESC)
- .union(BlkFeature::RING_EVENT_IDX);
- /// Driver for a VirtIO block device.
- ///
- /// This is a simple virtual block device, e.g. disk.
- ///
- /// Read and write requests (and other exotic requests) are placed in the queue and serviced
- /// (probably out of order) by the device except where noted.
- ///
- /// # Example
- ///
- /// ```
- /// # use virtio_drivers::{Error, Hal};
- /// # use virtio_drivers::transport::Transport;
- /// use virtio_drivers::device::blk::{VirtIOBlk, SECTOR_SIZE};
- ///
- /// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> {
- /// let mut disk = VirtIOBlk::<HalImpl, _>::new(transport)?;
- ///
- /// println!("VirtIO block device: {} kB", disk.capacity() * SECTOR_SIZE as u64 / 2);
- ///
- /// // Read sector 0 and then copy it to sector 1.
- /// let mut buf = [0; SECTOR_SIZE];
- /// disk.read_blocks(0, &mut buf)?;
- /// disk.write_blocks(1, &buf)?;
- /// # Ok(())
- /// # }
- /// ```
- pub struct VirtIOBlk<H: Hal, T: Transport> {
- transport: T,
- queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
- capacity: u64,
- negotiated_features: BlkFeature,
- }
- impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
- /// Create a new VirtIO-Blk driver.
- pub fn new(mut transport: T) -> Result<Self> {
- let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
- // Read configuration space.
- let config = transport.config_space::<BlkConfig>()?;
- info!("config: {:?}", config);
- // Safe because config is a valid pointer to the device configuration space.
- let capacity = unsafe {
- volread!(config, capacity_low) as u64 | (volread!(config, capacity_high) as u64) << 32
- };
- info!("found a block device of size {}KB", capacity / 2);
- let queue = VirtQueue::new(
- &mut transport,
- QUEUE,
- negotiated_features.contains(BlkFeature::RING_INDIRECT_DESC),
- negotiated_features.contains(BlkFeature::RING_EVENT_IDX),
- )?;
- transport.finish_init();
- Ok(VirtIOBlk {
- transport,
- queue,
- capacity,
- negotiated_features,
- })
- }
- /// Gets the capacity of the block device, in 512 byte ([`SECTOR_SIZE`]) sectors.
- pub fn capacity(&self) -> u64 {
- self.capacity
- }
- /// Returns true if the block device is read-only, or false if it allows writes.
- pub fn readonly(&self) -> bool {
- self.negotiated_features.contains(BlkFeature::RO)
- }
- /// Acknowledges a pending interrupt, if any.
- ///
- /// Returns true if there was an interrupt to acknowledge.
- pub fn ack_interrupt(&mut self) -> bool {
- self.transport.ack_interrupt()
- }
- /// Enables interrupts from the device.
- pub fn enable_interrupts(&mut self) {
- self.queue.set_dev_notify(true);
- }
- /// Disables interrupts from the device.
- pub fn disable_interrupts(&mut self) {
- self.queue.set_dev_notify(false);
- }
- /// Sends the given request to the device and waits for a response, with no extra data.
- fn request(&mut self, request: BlkReq) -> Result {
- let mut resp = BlkResp::default();
- self.queue.add_notify_wait_pop(
- &[request.as_bytes()],
- &mut [resp.as_bytes_mut()],
- &mut self.transport,
- )?;
- resp.status.into()
- }
- /// Sends the given request to the device and waits for a response, including the given data.
- fn request_read(&mut self, request: BlkReq, data: &mut [u8]) -> Result {
- let mut resp = BlkResp::default();
- self.queue.add_notify_wait_pop(
- &[request.as_bytes()],
- &mut [data, resp.as_bytes_mut()],
- &mut self.transport,
- )?;
- resp.status.into()
- }
- /// Sends the given request and data to the device and waits for a response.
- fn request_write(&mut self, request: BlkReq, data: &[u8]) -> Result {
- let mut resp = BlkResp::default();
- self.queue.add_notify_wait_pop(
- &[request.as_bytes(), data],
- &mut [resp.as_bytes_mut()],
- &mut self.transport,
- )?;
- resp.status.into()
- }
- /// Requests the device to flush any pending writes to storage.
- ///
- /// This will be ignored if the device doesn't support the `VIRTIO_BLK_F_FLUSH` feature.
- pub fn flush(&mut self) -> Result {
- if self.negotiated_features.contains(BlkFeature::FLUSH) {
- self.request(BlkReq {
- type_: ReqType::Flush,
- ..Default::default()
- })
- } else {
- Ok(())
- }
- }
- /// Gets the device ID.
- ///
- /// The ID is written as ASCII into the given buffer, which must be 20 bytes long, and the used
- /// length returned.
- pub fn device_id(&mut self, id: &mut [u8; 20]) -> Result<usize> {
- self.request_read(
- BlkReq {
- type_: ReqType::GetId,
- ..Default::default()
- },
- id,
- )?;
- let length = id.iter().position(|&x| x == 0).unwrap_or(20);
- Ok(length)
- }
- /// Reads one or more blocks into the given buffer.
- ///
- /// The buffer length must be a non-zero multiple of [`SECTOR_SIZE`].
- ///
- /// Blocks until the read completes or there is an error.
- pub fn read_blocks(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
- assert_ne!(buf.len(), 0);
- assert_eq!(buf.len() % SECTOR_SIZE, 0);
- self.request_read(
- BlkReq {
- type_: ReqType::In,
- reserved: 0,
- sector: block_id as u64,
- },
- buf,
- )
- }
- /// Submits a request to read one or more blocks, but returns immediately without waiting for
- /// the read to complete.
- ///
- /// # Arguments
- ///
- /// * `block_id` - The identifier of the first block to read.
- /// * `req` - A buffer which the driver can use for the request to send to the device. The
- /// contents don't matter as `read_blocks_nb` will initialise it, but like the other buffers
- /// it needs to be valid (and not otherwise used) until the corresponding
- /// `complete_read_blocks` call. Its length must be a non-zero multiple of [`SECTOR_SIZE`].
- /// * `buf` - The buffer in memory into which the block should be read.
- /// * `resp` - A mutable reference to a variable provided by the caller
- /// to contain the status of the request. The caller can safely
- /// read the variable only after the request is complete.
- ///
- /// # Usage
- ///
- /// It will submit request to the VirtIO block device and return a token identifying
- /// the position of the first Descriptor in the chain. If there are not enough
- /// Descriptors to allocate, then it returns [`Error::QueueFull`].
- ///
- /// The caller can then call `peek_used` with the returned token to check whether the device has
- /// finished handling the request. Once it has, the caller must call `complete_read_blocks` with
- /// the same buffers before reading the response.
- ///
- /// ```
- /// # use virtio_drivers::{Error, Hal};
- /// # use virtio_drivers::device::blk::VirtIOBlk;
- /// # use virtio_drivers::transport::Transport;
- /// use virtio_drivers::device::blk::{BlkReq, BlkResp, RespStatus};
- ///
- /// # fn example<H: Hal, T: Transport>(blk: &mut VirtIOBlk<H, T>) -> Result<(), Error> {
- /// let mut request = BlkReq::default();
- /// let mut buffer = [0; 512];
- /// let mut response = BlkResp::default();
- /// let token = unsafe { blk.read_blocks_nb(42, &mut request, &mut buffer, &mut response) }?;
- ///
- /// // Wait for an interrupt to tell us that the request completed...
- /// assert_eq!(blk.peek_used(), Some(token));
- ///
- /// unsafe {
- /// blk.complete_read_blocks(token, &request, &mut buffer, &mut response)?;
- /// }
- /// if response.status() == RespStatus::OK {
- /// println!("Successfully read block.");
- /// } else {
- /// println!("Error {:?} reading block.", response.status());
- /// }
- /// # Ok(())
- /// # }
- /// ```
- ///
- /// # Safety
- ///
- /// `req`, `buf` and `resp` are still borrowed by the underlying VirtIO block device even after
- /// this method returns. Thus, it is the caller's responsibility to guarantee that they are not
- /// accessed before the request is completed in order to avoid data races.
- pub unsafe fn read_blocks_nb(
- &mut self,
- block_id: usize,
- req: &mut BlkReq,
- buf: &mut [u8],
- resp: &mut BlkResp,
- ) -> Result<u16> {
- assert_ne!(buf.len(), 0);
- assert_eq!(buf.len() % SECTOR_SIZE, 0);
- *req = BlkReq {
- type_: ReqType::In,
- reserved: 0,
- sector: block_id as u64,
- };
- let token = self
- .queue
- .add(&[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?;
- if self.queue.should_notify() {
- self.transport.notify(QUEUE);
- }
- Ok(token)
- }
- /// Completes a read operation which was started by `read_blocks_nb`.
- ///
- /// # Safety
- ///
- /// The same buffers must be passed in again as were passed to `read_blocks_nb` when it returned
- /// the token.
- pub unsafe fn complete_read_blocks(
- &mut self,
- token: u16,
- req: &BlkReq,
- buf: &mut [u8],
- resp: &mut BlkResp,
- ) -> Result<()> {
- self.queue
- .pop_used(token, &[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?;
- resp.status.into()
- }
- /// Writes the contents of the given buffer to a block or blocks.
- ///
- /// The buffer length must be a non-zero multiple of [`SECTOR_SIZE`].
- ///
- /// Blocks until the write is complete or there is an error.
- pub fn write_blocks(&mut self, block_id: usize, buf: &[u8]) -> Result {
- assert_ne!(buf.len(), 0);
- assert_eq!(buf.len() % SECTOR_SIZE, 0);
- self.request_write(
- BlkReq {
- type_: ReqType::Out,
- sector: block_id as u64,
- ..Default::default()
- },
- buf,
- )
- }
- /// Submits a request to write one or more blocks, but returns immediately without waiting for
- /// the write to complete.
- ///
- /// # Arguments
- ///
- /// * `block_id` - The identifier of the first block to write.
- /// * `req` - A buffer which the driver can use for the request to send to the device. The
- /// contents don't matter as `read_blocks_nb` will initialise it, but like the other buffers
- /// it needs to be valid (and not otherwise used) until the corresponding
- /// `complete_write_blocks` call.
- /// * `buf` - The buffer in memory containing the data to write to the blocks. Its length must
- /// be a non-zero multiple of [`SECTOR_SIZE`].
- /// * `resp` - A mutable reference to a variable provided by the caller
- /// to contain the status of the request. The caller can safely
- /// read the variable only after the request is complete.
- ///
- /// # Usage
- ///
- /// See [VirtIOBlk::read_blocks_nb].
- ///
- /// # Safety
- ///
- /// See [VirtIOBlk::read_blocks_nb].
- pub unsafe fn write_blocks_nb(
- &mut self,
- block_id: usize,
- req: &mut BlkReq,
- buf: &[u8],
- resp: &mut BlkResp,
- ) -> Result<u16> {
- assert_ne!(buf.len(), 0);
- assert_eq!(buf.len() % SECTOR_SIZE, 0);
- *req = BlkReq {
- type_: ReqType::Out,
- reserved: 0,
- sector: block_id as u64,
- };
- let token = self
- .queue
- .add(&[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?;
- if self.queue.should_notify() {
- self.transport.notify(QUEUE);
- }
- Ok(token)
- }
- /// Completes a write operation which was started by `write_blocks_nb`.
- ///
- /// # Safety
- ///
- /// The same buffers must be passed in again as were passed to `write_blocks_nb` when it
- /// returned the token.
- pub unsafe fn complete_write_blocks(
- &mut self,
- token: u16,
- req: &BlkReq,
- buf: &[u8],
- resp: &mut BlkResp,
- ) -> Result<()> {
- self.queue
- .pop_used(token, &[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?;
- resp.status.into()
- }
- /// Fetches the token of the next completed request from the used ring and returns it, without
- /// removing it from the used ring. If there are no pending completed requests returns `None`.
- pub fn peek_used(&mut self) -> Option<u16> {
- self.queue.peek_used()
- }
- /// Returns the size of the device's VirtQueue.
- ///
- /// This can be used to tell the caller how many channels to monitor on.
- pub fn virt_queue_size(&self) -> u16 {
- QUEUE_SIZE
- }
- }
- impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> {
- fn drop(&mut self) {
- // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
- // after they have been freed.
- self.transport.queue_unset(QUEUE);
- }
- }
- #[repr(C)]
- struct BlkConfig {
- /// Number of 512 Bytes sectors
- capacity_low: Volatile<u32>,
- capacity_high: Volatile<u32>,
- size_max: Volatile<u32>,
- seg_max: Volatile<u32>,
- cylinders: Volatile<u16>,
- heads: Volatile<u8>,
- sectors: Volatile<u8>,
- blk_size: Volatile<u32>,
- physical_block_exp: Volatile<u8>,
- alignment_offset: Volatile<u8>,
- min_io_size: Volatile<u16>,
- opt_io_size: Volatile<u32>,
- // ... ignored
- }
- /// A VirtIO block device request.
- #[repr(C)]
- #[derive(AsBytes, Debug)]
- pub struct BlkReq {
- type_: ReqType,
- reserved: u32,
- sector: u64,
- }
- impl Default for BlkReq {
- fn default() -> Self {
- Self {
- type_: ReqType::In,
- reserved: 0,
- sector: 0,
- }
- }
- }
- /// Response of a VirtIOBlk request.
- #[repr(C)]
- #[derive(AsBytes, Debug, FromBytes, FromZeroes)]
- pub struct BlkResp {
- status: RespStatus,
- }
- impl BlkResp {
- /// Return the status of a VirtIOBlk request.
- pub fn status(&self) -> RespStatus {
- self.status
- }
- }
- #[repr(u32)]
- #[derive(AsBytes, Debug)]
- enum ReqType {
- In = 0,
- Out = 1,
- Flush = 4,
- GetId = 8,
- GetLifetime = 10,
- Discard = 11,
- WriteZeroes = 13,
- SecureErase = 14,
- }
- /// Status of a VirtIOBlk request.
- #[repr(transparent)]
- #[derive(AsBytes, Copy, Clone, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
- pub struct RespStatus(u8);
- impl RespStatus {
- /// Ok.
- pub const OK: RespStatus = RespStatus(0);
- /// IoErr.
- pub const IO_ERR: RespStatus = RespStatus(1);
- /// Unsupported yet.
- pub const UNSUPPORTED: RespStatus = RespStatus(2);
- /// Not ready.
- pub const NOT_READY: RespStatus = RespStatus(3);
- }
- impl From<RespStatus> for Result {
- fn from(status: RespStatus) -> Self {
- match status {
- RespStatus::OK => Ok(()),
- RespStatus::IO_ERR => Err(Error::IoError),
- RespStatus::UNSUPPORTED => Err(Error::Unsupported),
- RespStatus::NOT_READY => Err(Error::NotReady),
- _ => Err(Error::IoError),
- }
- }
- }
- impl Default for BlkResp {
- fn default() -> Self {
- BlkResp {
- status: RespStatus::NOT_READY,
- }
- }
- }
- /// The standard sector size of a VirtIO block device. Data is read and written in multiples of this
- /// size.
- pub const SECTOR_SIZE: usize = 512;
- bitflags! {
- #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
- struct BlkFeature: u64 {
- /// Device supports request barriers. (legacy)
- const BARRIER = 1 << 0;
- /// Maximum size of any single segment is in `size_max`.
- const SIZE_MAX = 1 << 1;
- /// Maximum number of segments in a request is in `seg_max`.
- const SEG_MAX = 1 << 2;
- /// Disk-style geometry specified in geometry.
- const GEOMETRY = 1 << 4;
- /// Device is read-only.
- const RO = 1 << 5;
- /// Block size of disk is in `blk_size`.
- const BLK_SIZE = 1 << 6;
- /// Device supports scsi packet commands. (legacy)
- const SCSI = 1 << 7;
- /// Cache flush command support.
- const FLUSH = 1 << 9;
- /// Device exports information on optimal I/O alignment.
- const TOPOLOGY = 1 << 10;
- /// Device can toggle its cache between writeback and writethrough modes.
- const CONFIG_WCE = 1 << 11;
- /// Device supports multiqueue.
- const MQ = 1 << 12;
- /// Device can support discard command, maximum discard sectors size in
- /// `max_discard_sectors` and maximum discard segment number in
- /// `max_discard_seg`.
- const DISCARD = 1 << 13;
- /// Device can support write zeroes command, maximum write zeroes sectors
- /// size in `max_write_zeroes_sectors` and maximum write zeroes segment
- /// number in `max_write_zeroes_seg`.
- const WRITE_ZEROES = 1 << 14;
- /// Device supports providing storage lifetime information.
- const LIFETIME = 1 << 15;
- /// Device can support the secure erase command.
- const SECURE_ERASE = 1 << 16;
- // device independent
- const NOTIFY_ON_EMPTY = 1 << 24; // legacy
- const ANY_LAYOUT = 1 << 27; // legacy
- const RING_INDIRECT_DESC = 1 << 28;
- const RING_EVENT_IDX = 1 << 29;
- const UNUSED = 1 << 30; // legacy
- const VERSION_1 = 1 << 32; // detect legacy
- // the following since virtio v1.1
- const ACCESS_PLATFORM = 1 << 33;
- const RING_PACKED = 1 << 34;
- const IN_ORDER = 1 << 35;
- const ORDER_PLATFORM = 1 << 36;
- const SR_IOV = 1 << 37;
- const NOTIFICATION_DATA = 1 << 38;
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- use crate::{
- hal::fake::FakeHal,
- transport::{
- fake::{FakeTransport, QueueStatus, State},
- DeviceType,
- },
- };
- use alloc::{sync::Arc, vec};
- use core::{mem::size_of, ptr::NonNull};
- use std::{sync::Mutex, thread};
- #[test]
- fn config() {
- let mut config_space = BlkConfig {
- capacity_low: Volatile::new(0x42),
- capacity_high: Volatile::new(0x02),
- size_max: Volatile::new(0),
- seg_max: Volatile::new(0),
- cylinders: Volatile::new(0),
- heads: Volatile::new(0),
- sectors: Volatile::new(0),
- blk_size: Volatile::new(0),
- physical_block_exp: Volatile::new(0),
- alignment_offset: Volatile::new(0),
- min_io_size: Volatile::new(0),
- opt_io_size: Volatile::new(0),
- };
- let state = Arc::new(Mutex::new(State {
- queues: vec![QueueStatus::default()],
- ..Default::default()
- }));
- let transport = FakeTransport {
- device_type: DeviceType::Block,
- max_queue_size: QUEUE_SIZE.into(),
- device_features: BlkFeature::RO.bits(),
- config_space: NonNull::from(&mut config_space),
- state: state.clone(),
- };
- let blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
- assert_eq!(blk.capacity(), 0x02_0000_0042);
- assert_eq!(blk.readonly(), true);
- }
- #[test]
- fn read() {
- let mut config_space = BlkConfig {
- capacity_low: Volatile::new(66),
- capacity_high: Volatile::new(0),
- size_max: Volatile::new(0),
- seg_max: Volatile::new(0),
- cylinders: Volatile::new(0),
- heads: Volatile::new(0),
- sectors: Volatile::new(0),
- blk_size: Volatile::new(0),
- physical_block_exp: Volatile::new(0),
- alignment_offset: Volatile::new(0),
- min_io_size: Volatile::new(0),
- opt_io_size: Volatile::new(0),
- };
- let state = Arc::new(Mutex::new(State {
- queues: vec![QueueStatus::default()],
- ..Default::default()
- }));
- let transport = FakeTransport {
- device_type: DeviceType::Block,
- max_queue_size: QUEUE_SIZE.into(),
- device_features: BlkFeature::RING_INDIRECT_DESC.bits(),
- config_space: NonNull::from(&mut config_space),
- state: state.clone(),
- };
- let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
- // Start a thread to simulate the device waiting for a read request.
- let handle = thread::spawn(move || {
- println!("Device waiting for a request.");
- State::wait_until_queue_notified(&state, QUEUE);
- println!("Transmit queue was notified.");
- state
- .lock()
- .unwrap()
- .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
- assert_eq!(
- request,
- BlkReq {
- type_: ReqType::In,
- reserved: 0,
- sector: 42
- }
- .as_bytes()
- );
- let mut response = vec![0; SECTOR_SIZE];
- response[0..9].copy_from_slice(b"Test data");
- response.extend_from_slice(
- BlkResp {
- status: RespStatus::OK,
- }
- .as_bytes(),
- );
- response
- });
- });
- // Read a block from the device.
- let mut buffer = [0; 512];
- blk.read_blocks(42, &mut buffer).unwrap();
- assert_eq!(&buffer[0..9], b"Test data");
- handle.join().unwrap();
- }
- #[test]
- fn write() {
- let mut config_space = BlkConfig {
- capacity_low: Volatile::new(66),
- capacity_high: Volatile::new(0),
- size_max: Volatile::new(0),
- seg_max: Volatile::new(0),
- cylinders: Volatile::new(0),
- heads: Volatile::new(0),
- sectors: Volatile::new(0),
- blk_size: Volatile::new(0),
- physical_block_exp: Volatile::new(0),
- alignment_offset: Volatile::new(0),
- min_io_size: Volatile::new(0),
- opt_io_size: Volatile::new(0),
- };
- let state = Arc::new(Mutex::new(State {
- queues: vec![QueueStatus::default()],
- ..Default::default()
- }));
- let transport = FakeTransport {
- device_type: DeviceType::Block,
- max_queue_size: QUEUE_SIZE.into(),
- device_features: BlkFeature::RING_INDIRECT_DESC.bits(),
- config_space: NonNull::from(&mut config_space),
- state: state.clone(),
- };
- let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
- // Start a thread to simulate the device waiting for a write request.
- let handle = thread::spawn(move || {
- println!("Device waiting for a request.");
- State::wait_until_queue_notified(&state, QUEUE);
- println!("Transmit queue was notified.");
- state
- .lock()
- .unwrap()
- .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
- assert_eq!(
- &request[0..size_of::<BlkReq>()],
- BlkReq {
- type_: ReqType::Out,
- reserved: 0,
- sector: 42
- }
- .as_bytes()
- );
- let data = &request[size_of::<BlkReq>()..];
- assert_eq!(data.len(), SECTOR_SIZE);
- assert_eq!(&data[0..9], b"Test data");
- let mut response = Vec::new();
- response.extend_from_slice(
- BlkResp {
- status: RespStatus::OK,
- }
- .as_bytes(),
- );
- response
- });
- });
- // Write a block to the device.
- let mut buffer = [0; 512];
- buffer[0..9].copy_from_slice(b"Test data");
- blk.write_blocks(42, &mut buffer).unwrap();
- // Request to flush should be ignored as the device doesn't support it.
- blk.flush().unwrap();
- handle.join().unwrap();
- }
- #[test]
- fn flush() {
- let mut config_space = BlkConfig {
- capacity_low: Volatile::new(66),
- capacity_high: Volatile::new(0),
- size_max: Volatile::new(0),
- seg_max: Volatile::new(0),
- cylinders: Volatile::new(0),
- heads: Volatile::new(0),
- sectors: Volatile::new(0),
- blk_size: Volatile::new(0),
- physical_block_exp: Volatile::new(0),
- alignment_offset: Volatile::new(0),
- min_io_size: Volatile::new(0),
- opt_io_size: Volatile::new(0),
- };
- let state = Arc::new(Mutex::new(State {
- queues: vec![QueueStatus::default()],
- ..Default::default()
- }));
- let transport = FakeTransport {
- device_type: DeviceType::Block,
- max_queue_size: QUEUE_SIZE.into(),
- device_features: (BlkFeature::RING_INDIRECT_DESC | BlkFeature::FLUSH).bits(),
- config_space: NonNull::from(&mut config_space),
- state: state.clone(),
- };
- let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
- // Start a thread to simulate the device waiting for a flush request.
- let handle = thread::spawn(move || {
- println!("Device waiting for a request.");
- State::wait_until_queue_notified(&state, QUEUE);
- println!("Transmit queue was notified.");
- state
- .lock()
- .unwrap()
- .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
- assert_eq!(
- request,
- BlkReq {
- type_: ReqType::Flush,
- reserved: 0,
- sector: 0,
- }
- .as_bytes()
- );
- let mut response = Vec::new();
- response.extend_from_slice(
- BlkResp {
- status: RespStatus::OK,
- }
- .as_bytes(),
- );
- response
- });
- });
- // Request to flush.
- blk.flush().unwrap();
- handle.join().unwrap();
- }
- #[test]
- fn device_id() {
- let mut config_space = BlkConfig {
- capacity_low: Volatile::new(66),
- capacity_high: Volatile::new(0),
- size_max: Volatile::new(0),
- seg_max: Volatile::new(0),
- cylinders: Volatile::new(0),
- heads: Volatile::new(0),
- sectors: Volatile::new(0),
- blk_size: Volatile::new(0),
- physical_block_exp: Volatile::new(0),
- alignment_offset: Volatile::new(0),
- min_io_size: Volatile::new(0),
- opt_io_size: Volatile::new(0),
- };
- let state = Arc::new(Mutex::new(State {
- queues: vec![QueueStatus::default()],
- ..Default::default()
- }));
- let transport = FakeTransport {
- device_type: DeviceType::Block,
- max_queue_size: QUEUE_SIZE.into(),
- device_features: BlkFeature::RING_INDIRECT_DESC.bits(),
- config_space: NonNull::from(&mut config_space),
- state: state.clone(),
- };
- let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
- // Start a thread to simulate the device waiting for a flush request.
- let handle = thread::spawn(move || {
- println!("Device waiting for a request.");
- State::wait_until_queue_notified(&state, QUEUE);
- println!("Transmit queue was notified.");
- state
- .lock()
- .unwrap()
- .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
- assert_eq!(
- request,
- BlkReq {
- type_: ReqType::GetId,
- reserved: 0,
- sector: 0,
- }
- .as_bytes()
- );
- let mut response = Vec::new();
- response.extend_from_slice(b"device_id\0\0\0\0\0\0\0\0\0\0\0");
- response.extend_from_slice(
- BlkResp {
- status: RespStatus::OK,
- }
- .as_bytes(),
- );
- response
- });
- });
- let mut id = [0; 20];
- let length = blk.device_id(&mut id).unwrap();
- assert_eq!(&id[0..length], b"device_id");
- handle.join().unwrap();
- }
- }
|