1 //! Driver for VirtIO block devices.
2 
3 use crate::hal::Hal;
4 use crate::queue::VirtQueue;
5 use crate::transport::Transport;
6 use crate::volatile::{volread, Volatile};
7 use crate::{Error, Result};
8 use bitflags::bitflags;
9 use log::info;
10 use zerocopy::{AsBytes, FromBytes, FromZeroes};
11 
12 const QUEUE: u16 = 0;
13 const QUEUE_SIZE: u16 = 16;
14 const SUPPORTED_FEATURES: BlkFeature = BlkFeature::RO
15     .union(BlkFeature::FLUSH)
16     .union(BlkFeature::RING_INDIRECT_DESC)
17     .union(BlkFeature::RING_EVENT_IDX);
18 
19 /// Driver for a VirtIO block device.
20 ///
21 /// This is a simple virtual block device, e.g. disk.
22 ///
23 /// Read and write requests (and other exotic requests) are placed in the queue and serviced
24 /// (probably out of order) by the device except where noted.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// # use virtio_drivers::{Error, Hal};
30 /// # use virtio_drivers::transport::Transport;
31 /// use virtio_drivers::device::blk::{VirtIOBlk, SECTOR_SIZE};
32 ///
33 /// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> {
34 /// let mut disk = VirtIOBlk::<HalImpl, _>::new(transport)?;
35 ///
36 /// println!("VirtIO block device: {} kB", disk.capacity() * SECTOR_SIZE as u64 / 2);
37 ///
38 /// // Read sector 0 and then copy it to sector 1.
39 /// let mut buf = [0; SECTOR_SIZE];
40 /// disk.read_blocks(0, &mut buf)?;
41 /// disk.write_blocks(1, &buf)?;
42 /// # Ok(())
43 /// # }
44 /// ```
45 pub struct VirtIOBlk<H: Hal, T: Transport> {
46     transport: T,
47     queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
48     capacity: u64,
49     negotiated_features: BlkFeature,
50 }
51 
52 impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
53     /// Create a new VirtIO-Blk driver.
new(mut transport: T) -> Result<Self>54     pub fn new(mut transport: T) -> Result<Self> {
55         let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
56 
57         // Read configuration space.
58         let config = transport.config_space::<BlkConfig>()?;
59         info!("config: {:?}", config);
60         // Safe because config is a valid pointer to the device configuration space.
61         let capacity = unsafe {
62             volread!(config, capacity_low) as u64 | (volread!(config, capacity_high) as u64) << 32
63         };
64         info!("found a block device of size {}KB", capacity / 2);
65 
66         let queue = VirtQueue::new(
67             &mut transport,
68             QUEUE,
69             negotiated_features.contains(BlkFeature::RING_INDIRECT_DESC),
70             negotiated_features.contains(BlkFeature::RING_EVENT_IDX),
71         )?;
72         transport.finish_init();
73 
74         Ok(VirtIOBlk {
75             transport,
76             queue,
77             capacity,
78             negotiated_features,
79         })
80     }
81 
82     /// Gets the capacity of the block device, in 512 byte ([`SECTOR_SIZE`]) sectors.
capacity(&self) -> u6483     pub fn capacity(&self) -> u64 {
84         self.capacity
85     }
86 
87     /// Returns true if the block device is read-only, or false if it allows writes.
readonly(&self) -> bool88     pub fn readonly(&self) -> bool {
89         self.negotiated_features.contains(BlkFeature::RO)
90     }
91 
92     /// Acknowledges a pending interrupt, if any.
93     ///
94     /// Returns true if there was an interrupt to acknowledge.
ack_interrupt(&mut self) -> bool95     pub fn ack_interrupt(&mut self) -> bool {
96         self.transport.ack_interrupt()
97     }
98 
99     /// Enables interrupts from the device.
enable_interrupts(&mut self)100     pub fn enable_interrupts(&mut self) {
101         self.queue.set_dev_notify(true);
102     }
103 
104     /// Disables interrupts from the device.
disable_interrupts(&mut self)105     pub fn disable_interrupts(&mut self) {
106         self.queue.set_dev_notify(false);
107     }
108 
109     /// Sends the given request to the device and waits for a response, with no extra data.
request(&mut self, request: BlkReq) -> Result110     fn request(&mut self, request: BlkReq) -> Result {
111         let mut resp = BlkResp::default();
112         self.queue.add_notify_wait_pop(
113             &[request.as_bytes()],
114             &mut [resp.as_bytes_mut()],
115             &mut self.transport,
116         )?;
117         resp.status.into()
118     }
119 
120     /// Sends the given request to the device and waits for a response, including the given data.
request_read(&mut self, request: BlkReq, data: &mut [u8]) -> Result121     fn request_read(&mut self, request: BlkReq, data: &mut [u8]) -> Result {
122         let mut resp = BlkResp::default();
123         self.queue.add_notify_wait_pop(
124             &[request.as_bytes()],
125             &mut [data, resp.as_bytes_mut()],
126             &mut self.transport,
127         )?;
128         resp.status.into()
129     }
130 
131     /// Sends the given request and data to the device and waits for a response.
request_write(&mut self, request: BlkReq, data: &[u8]) -> Result132     fn request_write(&mut self, request: BlkReq, data: &[u8]) -> Result {
133         let mut resp = BlkResp::default();
134         self.queue.add_notify_wait_pop(
135             &[request.as_bytes(), data],
136             &mut [resp.as_bytes_mut()],
137             &mut self.transport,
138         )?;
139         resp.status.into()
140     }
141 
142     /// Requests the device to flush any pending writes to storage.
143     ///
144     /// This will be ignored if the device doesn't support the `VIRTIO_BLK_F_FLUSH` feature.
flush(&mut self) -> Result145     pub fn flush(&mut self) -> Result {
146         if self.negotiated_features.contains(BlkFeature::FLUSH) {
147             self.request(BlkReq {
148                 type_: ReqType::Flush,
149                 ..Default::default()
150             })
151         } else {
152             Ok(())
153         }
154     }
155 
156     /// Gets the device ID.
157     ///
158     /// The ID is written as ASCII into the given buffer, which must be 20 bytes long, and the used
159     /// length returned.
device_id(&mut self, id: &mut [u8; 20]) -> Result<usize>160     pub fn device_id(&mut self, id: &mut [u8; 20]) -> Result<usize> {
161         self.request_read(
162             BlkReq {
163                 type_: ReqType::GetId,
164                 ..Default::default()
165             },
166             id,
167         )?;
168 
169         let length = id.iter().position(|&x| x == 0).unwrap_or(20);
170         Ok(length)
171     }
172 
173     /// Reads one or more blocks into the given buffer.
174     ///
175     /// The buffer length must be a non-zero multiple of [`SECTOR_SIZE`].
176     ///
177     /// Blocks until the read completes or there is an error.
read_blocks(&mut self, block_id: usize, buf: &mut [u8]) -> Result178     pub fn read_blocks(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
179         assert_ne!(buf.len(), 0);
180         assert_eq!(buf.len() % SECTOR_SIZE, 0);
181         self.request_read(
182             BlkReq {
183                 type_: ReqType::In,
184                 reserved: 0,
185                 sector: block_id as u64,
186             },
187             buf,
188         )
189     }
190 
191     /// Submits a request to read one or more blocks, but returns immediately without waiting for
192     /// the read to complete.
193     ///
194     /// # Arguments
195     ///
196     /// * `block_id` - The identifier of the first block to read.
197     /// * `req` - A buffer which the driver can use for the request to send to the device. The
198     ///   contents don't matter as `read_blocks_nb` will initialise it, but like the other buffers
199     ///   it needs to be valid (and not otherwise used) until the corresponding
200     ///   `complete_read_blocks` call. Its length must be a non-zero multiple of [`SECTOR_SIZE`].
201     /// * `buf` - The buffer in memory into which the block should be read.
202     /// * `resp` - A mutable reference to a variable provided by the caller
203     ///   to contain the status of the request. The caller can safely
204     ///   read the variable only after the request is complete.
205     ///
206     /// # Usage
207     ///
208     /// It will submit request to the VirtIO block device and return a token identifying
209     /// the position of the first Descriptor in the chain. If there are not enough
210     /// Descriptors to allocate, then it returns [`Error::QueueFull`].
211     ///
212     /// The caller can then call `peek_used` with the returned token to check whether the device has
213     /// finished handling the request. Once it has, the caller must call `complete_read_blocks` with
214     /// the same buffers before reading the response.
215     ///
216     /// ```
217     /// # use virtio_drivers::{Error, Hal};
218     /// # use virtio_drivers::device::blk::VirtIOBlk;
219     /// # use virtio_drivers::transport::Transport;
220     /// use virtio_drivers::device::blk::{BlkReq, BlkResp, RespStatus};
221     ///
222     /// # fn example<H: Hal, T: Transport>(blk: &mut VirtIOBlk<H, T>) -> Result<(), Error> {
223     /// let mut request = BlkReq::default();
224     /// let mut buffer = [0; 512];
225     /// let mut response = BlkResp::default();
226     /// let token = unsafe { blk.read_blocks_nb(42, &mut request, &mut buffer, &mut response) }?;
227     ///
228     /// // Wait for an interrupt to tell us that the request completed...
229     /// assert_eq!(blk.peek_used(), Some(token));
230     ///
231     /// unsafe {
232     ///   blk.complete_read_blocks(token, &request, &mut buffer, &mut response)?;
233     /// }
234     /// if response.status() == RespStatus::OK {
235     ///   println!("Successfully read block.");
236     /// } else {
237     ///   println!("Error {:?} reading block.", response.status());
238     /// }
239     /// # Ok(())
240     /// # }
241     /// ```
242     ///
243     /// # Safety
244     ///
245     /// `req`, `buf` and `resp` are still borrowed by the underlying VirtIO block device even after
246     /// this method returns. Thus, it is the caller's responsibility to guarantee that they are not
247     /// accessed before the request is completed in order to avoid data races.
read_blocks_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<u16>248     pub unsafe fn read_blocks_nb(
249         &mut self,
250         block_id: usize,
251         req: &mut BlkReq,
252         buf: &mut [u8],
253         resp: &mut BlkResp,
254     ) -> Result<u16> {
255         assert_ne!(buf.len(), 0);
256         assert_eq!(buf.len() % SECTOR_SIZE, 0);
257         *req = BlkReq {
258             type_: ReqType::In,
259             reserved: 0,
260             sector: block_id as u64,
261         };
262         let token = self
263             .queue
264             .add(&[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?;
265         if self.queue.should_notify() {
266             self.transport.notify(QUEUE);
267         }
268         Ok(token)
269     }
270 
271     /// Completes a read operation which was started by `read_blocks_nb`.
272     ///
273     /// # Safety
274     ///
275     /// The same buffers must be passed in again as were passed to `read_blocks_nb` when it returned
276     /// the token.
complete_read_blocks( &mut self, token: u16, req: &BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<()>277     pub unsafe fn complete_read_blocks(
278         &mut self,
279         token: u16,
280         req: &BlkReq,
281         buf: &mut [u8],
282         resp: &mut BlkResp,
283     ) -> Result<()> {
284         self.queue
285             .pop_used(token, &[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?;
286         resp.status.into()
287     }
288 
289     /// Writes the contents of the given buffer to a block or blocks.
290     ///
291     /// The buffer length must be a non-zero multiple of [`SECTOR_SIZE`].
292     ///
293     /// Blocks until the write is complete or there is an error.
write_blocks(&mut self, block_id: usize, buf: &[u8]) -> Result294     pub fn write_blocks(&mut self, block_id: usize, buf: &[u8]) -> Result {
295         assert_ne!(buf.len(), 0);
296         assert_eq!(buf.len() % SECTOR_SIZE, 0);
297         self.request_write(
298             BlkReq {
299                 type_: ReqType::Out,
300                 sector: block_id as u64,
301                 ..Default::default()
302             },
303             buf,
304         )
305     }
306 
307     /// Submits a request to write one or more blocks, but returns immediately without waiting for
308     /// the write to complete.
309     ///
310     /// # Arguments
311     ///
312     /// * `block_id` - The identifier of the first block to write.
313     /// * `req` - A buffer which the driver can use for the request to send to the device. The
314     ///   contents don't matter as `read_blocks_nb` will initialise it, but like the other buffers
315     ///   it needs to be valid (and not otherwise used) until the corresponding
316     ///   `complete_write_blocks` call.
317     /// * `buf` - The buffer in memory containing the data to write to the blocks. Its length must
318     ///   be a non-zero multiple of [`SECTOR_SIZE`].
319     /// * `resp` - A mutable reference to a variable provided by the caller
320     ///   to contain the status of the request. The caller can safely
321     ///   read the variable only after the request is complete.
322     ///
323     /// # Usage
324     ///
325     /// See [VirtIOBlk::read_blocks_nb].
326     ///
327     /// # Safety
328     ///
329     /// See  [VirtIOBlk::read_blocks_nb].
write_blocks_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<u16>330     pub unsafe fn write_blocks_nb(
331         &mut self,
332         block_id: usize,
333         req: &mut BlkReq,
334         buf: &[u8],
335         resp: &mut BlkResp,
336     ) -> Result<u16> {
337         assert_ne!(buf.len(), 0);
338         assert_eq!(buf.len() % SECTOR_SIZE, 0);
339         *req = BlkReq {
340             type_: ReqType::Out,
341             reserved: 0,
342             sector: block_id as u64,
343         };
344         let token = self
345             .queue
346             .add(&[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?;
347         if self.queue.should_notify() {
348             self.transport.notify(QUEUE);
349         }
350         Ok(token)
351     }
352 
353     /// Completes a write operation which was started by `write_blocks_nb`.
354     ///
355     /// # Safety
356     ///
357     /// The same buffers must be passed in again as were passed to `write_blocks_nb` when it
358     /// returned the token.
complete_write_blocks( &mut self, token: u16, req: &BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<()>359     pub unsafe fn complete_write_blocks(
360         &mut self,
361         token: u16,
362         req: &BlkReq,
363         buf: &[u8],
364         resp: &mut BlkResp,
365     ) -> Result<()> {
366         self.queue
367             .pop_used(token, &[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?;
368         resp.status.into()
369     }
370 
371     /// Fetches the token of the next completed request from the used ring and returns it, without
372     /// removing it from the used ring. If there are no pending completed requests returns `None`.
peek_used(&mut self) -> Option<u16>373     pub fn peek_used(&mut self) -> Option<u16> {
374         self.queue.peek_used()
375     }
376 
377     /// Returns the size of the device's VirtQueue.
378     ///
379     /// This can be used to tell the caller how many channels to monitor on.
virt_queue_size(&self) -> u16380     pub fn virt_queue_size(&self) -> u16 {
381         QUEUE_SIZE
382     }
383 }
384 
385 impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> {
drop(&mut self)386     fn drop(&mut self) {
387         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
388         // after they have been freed.
389         self.transport.queue_unset(QUEUE);
390     }
391 }
392 
393 #[repr(C)]
394 struct BlkConfig {
395     /// Number of 512 Bytes sectors
396     capacity_low: Volatile<u32>,
397     capacity_high: Volatile<u32>,
398     size_max: Volatile<u32>,
399     seg_max: Volatile<u32>,
400     cylinders: Volatile<u16>,
401     heads: Volatile<u8>,
402     sectors: Volatile<u8>,
403     blk_size: Volatile<u32>,
404     physical_block_exp: Volatile<u8>,
405     alignment_offset: Volatile<u8>,
406     min_io_size: Volatile<u16>,
407     opt_io_size: Volatile<u32>,
408     // ... ignored
409 }
410 
411 /// A VirtIO block device request.
412 #[repr(C)]
413 #[derive(AsBytes, Debug)]
414 pub struct BlkReq {
415     type_: ReqType,
416     reserved: u32,
417     sector: u64,
418 }
419 
420 impl Default for BlkReq {
default() -> Self421     fn default() -> Self {
422         Self {
423             type_: ReqType::In,
424             reserved: 0,
425             sector: 0,
426         }
427     }
428 }
429 
430 /// Response of a VirtIOBlk request.
431 #[repr(C)]
432 #[derive(AsBytes, Debug, FromBytes, FromZeroes)]
433 pub struct BlkResp {
434     status: RespStatus,
435 }
436 
437 impl BlkResp {
438     /// Return the status of a VirtIOBlk request.
status(&self) -> RespStatus439     pub fn status(&self) -> RespStatus {
440         self.status
441     }
442 }
443 
444 #[repr(u32)]
445 #[derive(AsBytes, Debug)]
446 enum ReqType {
447     In = 0,
448     Out = 1,
449     Flush = 4,
450     GetId = 8,
451     GetLifetime = 10,
452     Discard = 11,
453     WriteZeroes = 13,
454     SecureErase = 14,
455 }
456 
457 /// Status of a VirtIOBlk request.
458 #[repr(transparent)]
459 #[derive(AsBytes, Copy, Clone, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
460 pub struct RespStatus(u8);
461 
462 impl RespStatus {
463     /// Ok.
464     pub const OK: RespStatus = RespStatus(0);
465     /// IoErr.
466     pub const IO_ERR: RespStatus = RespStatus(1);
467     /// Unsupported yet.
468     pub const UNSUPPORTED: RespStatus = RespStatus(2);
469     /// Not ready.
470     pub const NOT_READY: RespStatus = RespStatus(3);
471 }
472 
473 impl From<RespStatus> for Result {
from(status: RespStatus) -> Self474     fn from(status: RespStatus) -> Self {
475         match status {
476             RespStatus::OK => Ok(()),
477             RespStatus::IO_ERR => Err(Error::IoError),
478             RespStatus::UNSUPPORTED => Err(Error::Unsupported),
479             RespStatus::NOT_READY => Err(Error::NotReady),
480             _ => Err(Error::IoError),
481         }
482     }
483 }
484 
485 impl Default for BlkResp {
default() -> Self486     fn default() -> Self {
487         BlkResp {
488             status: RespStatus::NOT_READY,
489         }
490     }
491 }
492 
493 /// The standard sector size of a VirtIO block device. Data is read and written in multiples of this
494 /// size.
495 pub const SECTOR_SIZE: usize = 512;
496 
497 bitflags! {
498     #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
499     struct BlkFeature: u64 {
500         /// Device supports request barriers. (legacy)
501         const BARRIER       = 1 << 0;
502         /// Maximum size of any single segment is in `size_max`.
503         const SIZE_MAX      = 1 << 1;
504         /// Maximum number of segments in a request is in `seg_max`.
505         const SEG_MAX       = 1 << 2;
506         /// Disk-style geometry specified in geometry.
507         const GEOMETRY      = 1 << 4;
508         /// Device is read-only.
509         const RO            = 1 << 5;
510         /// Block size of disk is in `blk_size`.
511         const BLK_SIZE      = 1 << 6;
512         /// Device supports scsi packet commands. (legacy)
513         const SCSI          = 1 << 7;
514         /// Cache flush command support.
515         const FLUSH         = 1 << 9;
516         /// Device exports information on optimal I/O alignment.
517         const TOPOLOGY      = 1 << 10;
518         /// Device can toggle its cache between writeback and writethrough modes.
519         const CONFIG_WCE    = 1 << 11;
520         /// Device supports multiqueue.
521         const MQ            = 1 << 12;
522         /// Device can support discard command, maximum discard sectors size in
523         /// `max_discard_sectors` and maximum discard segment number in
524         /// `max_discard_seg`.
525         const DISCARD       = 1 << 13;
526         /// Device can support write zeroes command, maximum write zeroes sectors
527         /// size in `max_write_zeroes_sectors` and maximum write zeroes segment
528         /// number in `max_write_zeroes_seg`.
529         const WRITE_ZEROES  = 1 << 14;
530         /// Device supports providing storage lifetime information.
531         const LIFETIME      = 1 << 15;
532         /// Device can support the secure erase command.
533         const SECURE_ERASE  = 1 << 16;
534 
535         // device independent
536         const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
537         const ANY_LAYOUT            = 1 << 27; // legacy
538         const RING_INDIRECT_DESC    = 1 << 28;
539         const RING_EVENT_IDX        = 1 << 29;
540         const UNUSED                = 1 << 30; // legacy
541         const VERSION_1             = 1 << 32; // detect legacy
542 
543         // the following since virtio v1.1
544         const ACCESS_PLATFORM       = 1 << 33;
545         const RING_PACKED           = 1 << 34;
546         const IN_ORDER              = 1 << 35;
547         const ORDER_PLATFORM        = 1 << 36;
548         const SR_IOV                = 1 << 37;
549         const NOTIFICATION_DATA     = 1 << 38;
550     }
551 }
552 
553 #[cfg(test)]
554 mod tests {
555     use super::*;
556     use crate::{
557         hal::fake::FakeHal,
558         transport::{
559             fake::{FakeTransport, QueueStatus, State},
560             DeviceType,
561         },
562     };
563     use alloc::{sync::Arc, vec};
564     use core::{mem::size_of, ptr::NonNull};
565     use std::{sync::Mutex, thread};
566 
567     #[test]
config()568     fn config() {
569         let mut config_space = BlkConfig {
570             capacity_low: Volatile::new(0x42),
571             capacity_high: Volatile::new(0x02),
572             size_max: Volatile::new(0),
573             seg_max: Volatile::new(0),
574             cylinders: Volatile::new(0),
575             heads: Volatile::new(0),
576             sectors: Volatile::new(0),
577             blk_size: Volatile::new(0),
578             physical_block_exp: Volatile::new(0),
579             alignment_offset: Volatile::new(0),
580             min_io_size: Volatile::new(0),
581             opt_io_size: Volatile::new(0),
582         };
583         let state = Arc::new(Mutex::new(State {
584             queues: vec![QueueStatus::default()],
585             ..Default::default()
586         }));
587         let transport = FakeTransport {
588             device_type: DeviceType::Block,
589             max_queue_size: QUEUE_SIZE.into(),
590             device_features: BlkFeature::RO.bits(),
591             config_space: NonNull::from(&mut config_space),
592             state: state.clone(),
593         };
594         let blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
595 
596         assert_eq!(blk.capacity(), 0x02_0000_0042);
597         assert_eq!(blk.readonly(), true);
598     }
599 
600     #[test]
read()601     fn read() {
602         let mut config_space = BlkConfig {
603             capacity_low: Volatile::new(66),
604             capacity_high: Volatile::new(0),
605             size_max: Volatile::new(0),
606             seg_max: Volatile::new(0),
607             cylinders: Volatile::new(0),
608             heads: Volatile::new(0),
609             sectors: Volatile::new(0),
610             blk_size: Volatile::new(0),
611             physical_block_exp: Volatile::new(0),
612             alignment_offset: Volatile::new(0),
613             min_io_size: Volatile::new(0),
614             opt_io_size: Volatile::new(0),
615         };
616         let state = Arc::new(Mutex::new(State {
617             queues: vec![QueueStatus::default()],
618             ..Default::default()
619         }));
620         let transport = FakeTransport {
621             device_type: DeviceType::Block,
622             max_queue_size: QUEUE_SIZE.into(),
623             device_features: BlkFeature::RING_INDIRECT_DESC.bits(),
624             config_space: NonNull::from(&mut config_space),
625             state: state.clone(),
626         };
627         let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
628 
629         // Start a thread to simulate the device waiting for a read request.
630         let handle = thread::spawn(move || {
631             println!("Device waiting for a request.");
632             State::wait_until_queue_notified(&state, QUEUE);
633             println!("Transmit queue was notified.");
634 
635             state
636                 .lock()
637                 .unwrap()
638                 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
639                     assert_eq!(
640                         request,
641                         BlkReq {
642                             type_: ReqType::In,
643                             reserved: 0,
644                             sector: 42
645                         }
646                         .as_bytes()
647                     );
648 
649                     let mut response = vec![0; SECTOR_SIZE];
650                     response[0..9].copy_from_slice(b"Test data");
651                     response.extend_from_slice(
652                         BlkResp {
653                             status: RespStatus::OK,
654                         }
655                         .as_bytes(),
656                     );
657 
658                     response
659                 });
660         });
661 
662         // Read a block from the device.
663         let mut buffer = [0; 512];
664         blk.read_blocks(42, &mut buffer).unwrap();
665         assert_eq!(&buffer[0..9], b"Test data");
666 
667         handle.join().unwrap();
668     }
669 
670     #[test]
write()671     fn write() {
672         let mut config_space = BlkConfig {
673             capacity_low: Volatile::new(66),
674             capacity_high: Volatile::new(0),
675             size_max: Volatile::new(0),
676             seg_max: Volatile::new(0),
677             cylinders: Volatile::new(0),
678             heads: Volatile::new(0),
679             sectors: Volatile::new(0),
680             blk_size: Volatile::new(0),
681             physical_block_exp: Volatile::new(0),
682             alignment_offset: Volatile::new(0),
683             min_io_size: Volatile::new(0),
684             opt_io_size: Volatile::new(0),
685         };
686         let state = Arc::new(Mutex::new(State {
687             queues: vec![QueueStatus::default()],
688             ..Default::default()
689         }));
690         let transport = FakeTransport {
691             device_type: DeviceType::Block,
692             max_queue_size: QUEUE_SIZE.into(),
693             device_features: BlkFeature::RING_INDIRECT_DESC.bits(),
694             config_space: NonNull::from(&mut config_space),
695             state: state.clone(),
696         };
697         let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
698 
699         // Start a thread to simulate the device waiting for a write request.
700         let handle = thread::spawn(move || {
701             println!("Device waiting for a request.");
702             State::wait_until_queue_notified(&state, QUEUE);
703             println!("Transmit queue was notified.");
704 
705             state
706                 .lock()
707                 .unwrap()
708                 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
709                     assert_eq!(
710                         &request[0..size_of::<BlkReq>()],
711                         BlkReq {
712                             type_: ReqType::Out,
713                             reserved: 0,
714                             sector: 42
715                         }
716                         .as_bytes()
717                     );
718                     let data = &request[size_of::<BlkReq>()..];
719                     assert_eq!(data.len(), SECTOR_SIZE);
720                     assert_eq!(&data[0..9], b"Test data");
721 
722                     let mut response = Vec::new();
723                     response.extend_from_slice(
724                         BlkResp {
725                             status: RespStatus::OK,
726                         }
727                         .as_bytes(),
728                     );
729 
730                     response
731                 });
732         });
733 
734         // Write a block to the device.
735         let mut buffer = [0; 512];
736         buffer[0..9].copy_from_slice(b"Test data");
737         blk.write_blocks(42, &mut buffer).unwrap();
738 
739         // Request to flush should be ignored as the device doesn't support it.
740         blk.flush().unwrap();
741 
742         handle.join().unwrap();
743     }
744 
745     #[test]
flush()746     fn flush() {
747         let mut config_space = BlkConfig {
748             capacity_low: Volatile::new(66),
749             capacity_high: Volatile::new(0),
750             size_max: Volatile::new(0),
751             seg_max: Volatile::new(0),
752             cylinders: Volatile::new(0),
753             heads: Volatile::new(0),
754             sectors: Volatile::new(0),
755             blk_size: Volatile::new(0),
756             physical_block_exp: Volatile::new(0),
757             alignment_offset: Volatile::new(0),
758             min_io_size: Volatile::new(0),
759             opt_io_size: Volatile::new(0),
760         };
761         let state = Arc::new(Mutex::new(State {
762             queues: vec![QueueStatus::default()],
763             ..Default::default()
764         }));
765         let transport = FakeTransport {
766             device_type: DeviceType::Block,
767             max_queue_size: QUEUE_SIZE.into(),
768             device_features: (BlkFeature::RING_INDIRECT_DESC | BlkFeature::FLUSH).bits(),
769             config_space: NonNull::from(&mut config_space),
770             state: state.clone(),
771         };
772         let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
773 
774         // Start a thread to simulate the device waiting for a flush request.
775         let handle = thread::spawn(move || {
776             println!("Device waiting for a request.");
777             State::wait_until_queue_notified(&state, QUEUE);
778             println!("Transmit queue was notified.");
779 
780             state
781                 .lock()
782                 .unwrap()
783                 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
784                     assert_eq!(
785                         request,
786                         BlkReq {
787                             type_: ReqType::Flush,
788                             reserved: 0,
789                             sector: 0,
790                         }
791                         .as_bytes()
792                     );
793 
794                     let mut response = Vec::new();
795                     response.extend_from_slice(
796                         BlkResp {
797                             status: RespStatus::OK,
798                         }
799                         .as_bytes(),
800                     );
801 
802                     response
803                 });
804         });
805 
806         // Request to flush.
807         blk.flush().unwrap();
808 
809         handle.join().unwrap();
810     }
811 
812     #[test]
device_id()813     fn device_id() {
814         let mut config_space = BlkConfig {
815             capacity_low: Volatile::new(66),
816             capacity_high: Volatile::new(0),
817             size_max: Volatile::new(0),
818             seg_max: Volatile::new(0),
819             cylinders: Volatile::new(0),
820             heads: Volatile::new(0),
821             sectors: Volatile::new(0),
822             blk_size: Volatile::new(0),
823             physical_block_exp: Volatile::new(0),
824             alignment_offset: Volatile::new(0),
825             min_io_size: Volatile::new(0),
826             opt_io_size: Volatile::new(0),
827         };
828         let state = Arc::new(Mutex::new(State {
829             queues: vec![QueueStatus::default()],
830             ..Default::default()
831         }));
832         let transport = FakeTransport {
833             device_type: DeviceType::Block,
834             max_queue_size: QUEUE_SIZE.into(),
835             device_features: BlkFeature::RING_INDIRECT_DESC.bits(),
836             config_space: NonNull::from(&mut config_space),
837             state: state.clone(),
838         };
839         let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
840 
841         // Start a thread to simulate the device waiting for a flush request.
842         let handle = thread::spawn(move || {
843             println!("Device waiting for a request.");
844             State::wait_until_queue_notified(&state, QUEUE);
845             println!("Transmit queue was notified.");
846 
847             state
848                 .lock()
849                 .unwrap()
850                 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
851                     assert_eq!(
852                         request,
853                         BlkReq {
854                             type_: ReqType::GetId,
855                             reserved: 0,
856                             sector: 0,
857                         }
858                         .as_bytes()
859                     );
860 
861                     let mut response = Vec::new();
862                     response.extend_from_slice(b"device_id\0\0\0\0\0\0\0\0\0\0\0");
863                     response.extend_from_slice(
864                         BlkResp {
865                             status: RespStatus::OK,
866                         }
867                         .as_bytes(),
868                     );
869 
870                     response
871                 });
872         });
873 
874         let mut id = [0; 20];
875         let length = blk.device_id(&mut id).unwrap();
876         assert_eq!(&id[0..length], b"device_id");
877 
878         handle.join().unwrap();
879     }
880 }
881