|  | @@ -8,10 +8,30 @@ use zerocopy::{AsBytes, FromBytes};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const QUEUE: u16 = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/// The virtio block device is a simple virtual block device (ie. disk).
 | 
	
		
			
				|  |  | +/// Driver for a VirtIO block device.
 | 
	
		
			
				|  |  |  ///
 | 
	
		
			
				|  |  | -/// 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.
 | 
	
		
			
				|  |  | +/// 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, Transport};
 | 
	
		
			
				|  |  | +/// use virtio_drivers::{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_block(0, &mut buf)?;
 | 
	
		
			
				|  |  | +/// disk.write_block(1, &buf)?;
 | 
	
		
			
				|  |  | +/// # Ok(())
 | 
	
		
			
				|  |  | +/// # }
 | 
	
		
			
				|  |  | +/// ```
 | 
	
		
			
				|  |  |  pub struct VirtIOBlk<H: Hal, T: Transport> {
 | 
	
		
			
				|  |  |      transport: T,
 | 
	
		
			
				|  |  |      queue: VirtQueue<H>,
 | 
	
	
		
			
				|  | @@ -53,7 +73,7 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
 | 
	
		
			
				|  |  |          })
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Gets the capacity of the block device, in 512 byte sectors.
 | 
	
		
			
				|  |  | +    /// Gets the capacity of the block device, in 512 byte ([`SECTOR_SIZE`]) sectors.
 | 
	
		
			
				|  |  |      pub fn capacity(&self) -> u64 {
 | 
	
		
			
				|  |  |          self.capacity
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -63,12 +83,16 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
 | 
	
		
			
				|  |  |          self.readonly
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Acknowledge interrupt.
 | 
	
		
			
				|  |  | +    /// 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()
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Read a block.
 | 
	
		
			
				|  |  | +    /// Reads a block into the given buffer.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// Blocks until the read completes or there is an error.
 | 
	
		
			
				|  |  |      pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
 | 
	
		
			
				|  |  |          assert_eq!(buf.len(), SECTOR_SIZE);
 | 
	
		
			
				|  |  |          let req = BlkReq {
 | 
	
	
		
			
				|  | @@ -88,42 +112,63 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Read a block in a non-blocking way which means that it returns immediately.
 | 
	
		
			
				|  |  | +    /// Submits a request to read a block, but returns immediately without waiting for the read to
 | 
	
		
			
				|  |  | +    /// complete.
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// # Arguments
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// * `block_id` - The identifier of the block to read.
 | 
	
		
			
				|  |  | -    /// * `buf` - The buffer in the memory which the block is read into.
 | 
	
		
			
				|  |  | +    /// * `req` - A buffer which the driver can use for the request to send to the device. The
 | 
	
		
			
				|  |  | +    ///   contents don't matter as `read_block_nb` will initialise it, but like the other buffers it
 | 
	
		
			
				|  |  | +    ///   needs to be valid (and not otherwise used) until the corresponding `pop_used` call.
 | 
	
		
			
				|  |  | +    /// * `buf` - The buffer in memory into which the block should be read.
 | 
	
		
			
				|  |  |      /// * `resp` - A mutable reference to a variable provided by the caller
 | 
	
		
			
				|  |  | -    ///   which contains the status of the requests. The caller can safely
 | 
	
		
			
				|  |  | -    ///   read the variable only after the request is ready.
 | 
	
		
			
				|  |  | +    ///   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
 | 
	
		
			
				|  |  | +    /// 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::BufferTooSmall].
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  | -    /// After the request is ready, `resp` will be updated and the caller can get the
 | 
	
		
			
				|  |  | -    /// status of the request(e.g. succeed or failed) through it. However, the caller
 | 
	
		
			
				|  |  | -    /// **must not** spin on `resp` to wait for it to change. A safe way is to read it
 | 
	
		
			
				|  |  | -    /// after the same token as this method returns is fetched through [VirtIOBlk::pop_used()],
 | 
	
		
			
				|  |  | -    /// which means that the request has been ready.
 | 
	
		
			
				|  |  | +    /// The caller can then call `pop_used` to check whether the device has finished handling the
 | 
	
		
			
				|  |  | +    /// request. Once it has, the caller can then read the response and dispose of the buffers.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// ```
 | 
	
		
			
				|  |  | +    /// # use virtio_drivers::{BlkReq, BlkResp, Error, Hal, RespStatus, Transport, VirtIOBlk};
 | 
	
		
			
				|  |  | +    /// # 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_block_nb(42, &mut request, &mut buffer, &mut response) }?;
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// // Wait for an interrupt to tell us that the request completed...
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// assert_eq!(blk.pop_used()?, token);
 | 
	
		
			
				|  |  | +    /// if response.status() == RespStatus::OK {
 | 
	
		
			
				|  |  | +    ///   println!("Successfully read block.");
 | 
	
		
			
				|  |  | +    /// } else {
 | 
	
		
			
				|  |  | +    ///   println!("Error {:?} reading block.", response.status());
 | 
	
		
			
				|  |  | +    /// }
 | 
	
		
			
				|  |  | +    /// # Ok(())
 | 
	
		
			
				|  |  | +    /// # }
 | 
	
		
			
				|  |  | +    /// ```
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// # Safety
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  | -    /// `buf` is still borrowed by the underlying virtio block device even if this
 | 
	
		
			
				|  |  | -    /// method returns. Thus, it is the caller's responsibility to guarantee that
 | 
	
		
			
				|  |  | -    /// `buf` is not accessed before the request is completed in order to avoid
 | 
	
		
			
				|  |  | -    /// data races.
 | 
	
		
			
				|  |  | +    /// `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_block_nb(
 | 
	
		
			
				|  |  |          &mut self,
 | 
	
		
			
				|  |  |          block_id: usize,
 | 
	
		
			
				|  |  | +        req: &mut BlkReq,
 | 
	
		
			
				|  |  |          buf: &mut [u8],
 | 
	
		
			
				|  |  |          resp: &mut BlkResp,
 | 
	
		
			
				|  |  |      ) -> Result<u16> {
 | 
	
		
			
				|  |  |          assert_eq!(buf.len(), SECTOR_SIZE);
 | 
	
		
			
				|  |  | -        let req = BlkReq {
 | 
	
		
			
				|  |  | +        *req = BlkReq {
 | 
	
		
			
				|  |  |              type_: ReqType::In,
 | 
	
		
			
				|  |  |              reserved: 0,
 | 
	
		
			
				|  |  |              sector: block_id as u64,
 | 
	
	
		
			
				|  | @@ -135,7 +180,9 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
 | 
	
		
			
				|  |  |          Ok(token)
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Write a block.
 | 
	
		
			
				|  |  | +    /// Writes the contents of the given buffer to a block.
 | 
	
		
			
				|  |  | +    ///
 | 
	
		
			
				|  |  | +    /// Blocks until the write is complete or there is an error.
 | 
	
		
			
				|  |  |      pub fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result {
 | 
	
		
			
				|  |  |          assert_eq!(buf.len(), SECTOR_SIZE);
 | 
	
		
			
				|  |  |          let req = BlkReq {
 | 
	
	
		
			
				|  | @@ -155,31 +202,36 @@ impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    //// Write a block in a non-blocking way which means that it returns immediately.
 | 
	
		
			
				|  |  | +    /// Submits a request to write a block, but returns immediately without waiting for the write to
 | 
	
		
			
				|  |  | +    /// complete.
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// # Arguments
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// * `block_id` - The identifier of the block to write.
 | 
	
		
			
				|  |  | -    /// * `buf` - The buffer in the memory containing the data to write to the block.
 | 
	
		
			
				|  |  | +    /// * `req` - A buffer which the driver can use for the request to send to the device. The
 | 
	
		
			
				|  |  | +    ///   contents don't matter as `read_block_nb` will initialise it, but like the other buffers it
 | 
	
		
			
				|  |  | +    ///   needs to be valid (and not otherwise used) until the corresponding `pop_used` call.
 | 
	
		
			
				|  |  | +    /// * `buf` - The buffer in memory containing the data to write to the block.
 | 
	
		
			
				|  |  |      /// * `resp` - A mutable reference to a variable provided by the caller
 | 
	
		
			
				|  |  | -    ///   which contains the status of the requests. The caller can safely
 | 
	
		
			
				|  |  | -    ///   read the variable only after the request is ready.
 | 
	
		
			
				|  |  | +    ///   to contain the status of the request. The caller can safely
 | 
	
		
			
				|  |  | +    ///   read the variable only after the request is complete.
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// # Usage
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  | -    /// See also [VirtIOBlk::read_block_nb()].
 | 
	
		
			
				|  |  | +    /// See [VirtIOBlk::read_block_nb].
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  |      /// # Safety
 | 
	
		
			
				|  |  |      ///
 | 
	
		
			
				|  |  | -    /// See also [VirtIOBlk::read_block_nb()].
 | 
	
		
			
				|  |  | +    /// See  [VirtIOBlk::read_block_nb].
 | 
	
		
			
				|  |  |      pub unsafe fn write_block_nb(
 | 
	
		
			
				|  |  |          &mut self,
 | 
	
		
			
				|  |  |          block_id: usize,
 | 
	
		
			
				|  |  | +        req: &mut BlkReq,
 | 
	
		
			
				|  |  |          buf: &[u8],
 | 
	
		
			
				|  |  |          resp: &mut BlkResp,
 | 
	
		
			
				|  |  |      ) -> Result<u16> {
 | 
	
		
			
				|  |  |          assert_eq!(buf.len(), SECTOR_SIZE);
 | 
	
		
			
				|  |  | -        let req = BlkReq {
 | 
	
		
			
				|  |  | +        *req = BlkReq {
 | 
	
		
			
				|  |  |              type_: ReqType::Out,
 | 
	
		
			
				|  |  |              reserved: 0,
 | 
	
		
			
				|  |  |              sector: block_id as u64,
 | 
	
	
		
			
				|  | @@ -231,14 +283,25 @@ struct BlkConfig {
 | 
	
		
			
				|  |  |      // ... ignored
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/// A VirtIO block device request.
 | 
	
		
			
				|  |  |  #[repr(C)]
 | 
	
		
			
				|  |  |  #[derive(AsBytes, Debug)]
 | 
	
		
			
				|  |  | -struct BlkReq {
 | 
	
		
			
				|  |  | +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)]
 |