virtio_drivers/device/
blk.rs

1//! Driver for VirtIO block devices.
2
3use crate::hal::Hal;
4use crate::queue::VirtQueue;
5use crate::transport::Transport;
6use crate::volatile::{volread, Volatile};
7use crate::{Error, Result};
8use bitflags::bitflags;
9use log::info;
10use zerocopy::{AsBytes, FromBytes, FromZeroes};
11
12const QUEUE: u16 = 0;
13const QUEUE_SIZE: u16 = 16;
14const 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/// ```
45pub 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
52impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
53    /// Create a new VirtIO-Blk driver.
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.
83    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.
88    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.
95    pub fn ack_interrupt(&mut self) -> bool {
96        self.transport.ack_interrupt()
97    }
98
99    /// Enables interrupts from the device.
100    pub fn enable_interrupts(&mut self) {
101        self.queue.set_dev_notify(true);
102    }
103
104    /// Disables interrupts from the device.
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.
110    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.
121    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.
132    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.
145    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.
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.
178    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.
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.
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.
294    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].
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.
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`.
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.
380    pub fn virt_queue_size(&self) -> u16 {
381        QUEUE_SIZE
382    }
383}
384
385impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> {
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)]
394struct 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)]
414pub struct BlkReq {
415    type_: ReqType,
416    reserved: u32,
417    sector: u64,
418}
419
420impl Default for BlkReq {
421    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)]
433pub struct BlkResp {
434    status: RespStatus,
435}
436
437impl BlkResp {
438    /// Return the status of a VirtIOBlk request.
439    pub fn status(&self) -> RespStatus {
440        self.status
441    }
442}
443
444#[repr(u32)]
445#[derive(AsBytes, Debug)]
446enum 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)]
460pub struct RespStatus(u8);
461
462impl 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
473impl From<RespStatus> for Result {
474    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
485impl Default for BlkResp {
486    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.
495pub const SECTOR_SIZE: usize = 512;
496
497bitflags! {
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)]
554mod 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]
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]
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            assert!(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]
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            assert!(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]
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            assert!(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]
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            assert!(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}