blk.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. use super::*;
  2. use crate::queue::VirtQueue;
  3. use crate::transport::Transport;
  4. use crate::volatile::{volread, Volatile};
  5. use bitflags::*;
  6. use core::hint::spin_loop;
  7. use log::*;
  8. /// The virtio block device is a simple virtual block device (ie. disk).
  9. ///
  10. /// Read and write requests (and other exotic requests) are placed in the queue,
  11. /// and serviced (probably out of order) by the device except where noted.
  12. pub struct VirtIOBlk<H: Hal, T: Transport> {
  13. transport: T,
  14. queue: VirtQueue<H>,
  15. capacity: usize,
  16. }
  17. impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
  18. /// Create a new VirtIO-Blk driver.
  19. pub fn new(mut transport: T) -> Result<Self> {
  20. transport.begin_init(|features| {
  21. let features = BlkFeature::from_bits_truncate(features);
  22. info!("device features: {:?}", features);
  23. // negotiate these flags only
  24. let supported_features = BlkFeature::empty();
  25. (features & supported_features).bits()
  26. });
  27. // read configuration space
  28. let config = transport.config_space().cast::<BlkConfig>();
  29. info!("config: {:?}", config);
  30. // Safe because config is a valid pointer to the device configuration space.
  31. let capacity = unsafe { volread!(config, capacity) };
  32. info!("found a block device of size {}KB", capacity / 2);
  33. let queue = VirtQueue::new(&mut transport, 0, 16)?;
  34. transport.finish_init();
  35. Ok(VirtIOBlk {
  36. transport,
  37. queue,
  38. capacity: capacity as usize,
  39. })
  40. }
  41. /// Acknowledge interrupt.
  42. pub fn ack_interrupt(&mut self) -> bool {
  43. self.transport.ack_interrupt()
  44. }
  45. /// Read a block.
  46. pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
  47. assert_eq!(buf.len(), BLK_SIZE);
  48. let req = BlkReq {
  49. type_: ReqType::In,
  50. reserved: 0,
  51. sector: block_id as u64,
  52. };
  53. let mut resp = BlkResp::default();
  54. self.queue.add(&[req.as_buf()], &[buf, resp.as_buf_mut()])?;
  55. self.transport.notify(0);
  56. while !self.queue.can_pop() {
  57. spin_loop();
  58. }
  59. self.queue.pop_used()?;
  60. match resp.status {
  61. RespStatus::Ok => Ok(()),
  62. _ => Err(Error::IoError),
  63. }
  64. }
  65. /// Read a block in a non-blocking way which means that it returns immediately.
  66. ///
  67. /// # Arguments
  68. ///
  69. /// * `block_id` - The identifier of the block to read.
  70. /// * `buf` - The buffer in the memory which the block is read into.
  71. /// * `resp` - A mutable reference to a variable provided by the caller
  72. /// which contains the status of the requests. The caller can safely
  73. /// read the variable only after the request is ready.
  74. ///
  75. /// # Usage
  76. ///
  77. /// It will submit request to the virtio block device and return a token identifying
  78. /// the position of the first Descriptor in the chain. If there are not enough
  79. /// Descriptors to allocate, then it returns [Error::BufferTooSmall].
  80. ///
  81. /// After the request is ready, `resp` will be updated and the caller can get the
  82. /// status of the request(e.g. succeed or failed) through it. However, the caller
  83. /// **must not** spin on `resp` to wait for it to change. A safe way is to read it
  84. /// after the same token as this method returns is fetched through [VirtIOBlk::pop_used()],
  85. /// which means that the request has been ready.
  86. ///
  87. /// # Safety
  88. ///
  89. /// `buf` is still borrowed by the underlying virtio block device even if this
  90. /// method returns. Thus, it is the caller's responsibility to guarantee that
  91. /// `buf` is not accessed before the request is completed in order to avoid
  92. /// data races.
  93. pub unsafe fn read_block_nb(
  94. &mut self,
  95. block_id: usize,
  96. buf: &mut [u8],
  97. resp: &mut BlkResp,
  98. ) -> Result<u16> {
  99. assert_eq!(buf.len(), BLK_SIZE);
  100. let req = BlkReq {
  101. type_: ReqType::In,
  102. reserved: 0,
  103. sector: block_id as u64,
  104. };
  105. let token = self.queue.add(&[req.as_buf()], &[buf, resp.as_buf_mut()])?;
  106. self.transport.notify(0);
  107. Ok(token)
  108. }
  109. /// Write a block.
  110. pub fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result {
  111. assert_eq!(buf.len(), BLK_SIZE);
  112. let req = BlkReq {
  113. type_: ReqType::Out,
  114. reserved: 0,
  115. sector: block_id as u64,
  116. };
  117. let mut resp = BlkResp::default();
  118. self.queue.add(&[req.as_buf(), buf], &[resp.as_buf_mut()])?;
  119. self.transport.notify(0);
  120. while !self.queue.can_pop() {
  121. spin_loop();
  122. }
  123. self.queue.pop_used()?;
  124. match resp.status {
  125. RespStatus::Ok => Ok(()),
  126. _ => Err(Error::IoError),
  127. }
  128. }
  129. //// Write a block in a non-blocking way which means that it returns immediately.
  130. ///
  131. /// # Arguments
  132. ///
  133. /// * `block_id` - The identifier of the block to write.
  134. /// * `buf` - The buffer in the memory containing the data to write to the block.
  135. /// * `resp` - A mutable reference to a variable provided by the caller
  136. /// which contains the status of the requests. The caller can safely
  137. /// read the variable only after the request is ready.
  138. ///
  139. /// # Usage
  140. ///
  141. /// See also [VirtIOBlk::read_block_nb()].
  142. ///
  143. /// # Safety
  144. ///
  145. /// See also [VirtIOBlk::read_block_nb()].
  146. pub unsafe fn write_block_nb(
  147. &mut self,
  148. block_id: usize,
  149. buf: &[u8],
  150. resp: &mut BlkResp,
  151. ) -> Result<u16> {
  152. assert_eq!(buf.len(), BLK_SIZE);
  153. let req = BlkReq {
  154. type_: ReqType::Out,
  155. reserved: 0,
  156. sector: block_id as u64,
  157. };
  158. let token = self.queue.add(&[req.as_buf(), buf], &[resp.as_buf_mut()])?;
  159. self.transport.notify(0);
  160. Ok(token)
  161. }
  162. /// During an interrupt, it fetches a token of a completed request from the used
  163. /// ring and return it. If all completed requests have already been fetched, return
  164. /// Err(Error::NotReady).
  165. pub fn pop_used(&mut self) -> Result<u16> {
  166. self.queue.pop_used().map(|p| p.0)
  167. }
  168. /// Return size of its VirtQueue.
  169. /// It can be used to tell the caller how many channels he should monitor on.
  170. pub fn virt_queue_size(&self) -> u16 {
  171. self.queue.size()
  172. }
  173. }
  174. #[repr(C)]
  175. struct BlkConfig {
  176. /// Number of 512 Bytes sectors
  177. capacity: Volatile<u64>,
  178. size_max: Volatile<u32>,
  179. seg_max: Volatile<u32>,
  180. cylinders: Volatile<u16>,
  181. heads: Volatile<u8>,
  182. sectors: Volatile<u8>,
  183. blk_size: Volatile<u32>,
  184. physical_block_exp: Volatile<u8>,
  185. alignment_offset: Volatile<u8>,
  186. min_io_size: Volatile<u16>,
  187. opt_io_size: Volatile<u32>,
  188. // ... ignored
  189. }
  190. #[repr(C)]
  191. #[derive(Debug)]
  192. struct BlkReq {
  193. type_: ReqType,
  194. reserved: u32,
  195. sector: u64,
  196. }
  197. /// Response of a VirtIOBlk request.
  198. #[repr(C)]
  199. #[derive(Debug)]
  200. pub struct BlkResp {
  201. status: RespStatus,
  202. }
  203. impl BlkResp {
  204. /// Return the status of a VirtIOBlk request.
  205. pub fn status(&self) -> RespStatus {
  206. self.status
  207. }
  208. }
  209. #[repr(u32)]
  210. #[derive(Debug)]
  211. enum ReqType {
  212. In = 0,
  213. Out = 1,
  214. Flush = 4,
  215. Discard = 11,
  216. WriteZeroes = 13,
  217. }
  218. /// Status of a VirtIOBlk request.
  219. #[repr(u8)]
  220. #[derive(Debug, Eq, PartialEq, Copy, Clone)]
  221. pub enum RespStatus {
  222. /// Ok.
  223. Ok = 0,
  224. /// IoErr.
  225. IoErr = 1,
  226. /// Unsupported yet.
  227. Unsupported = 2,
  228. /// Not ready.
  229. _NotReady = 3,
  230. }
  231. impl Default for BlkResp {
  232. fn default() -> Self {
  233. BlkResp {
  234. status: RespStatus::_NotReady,
  235. }
  236. }
  237. }
  238. const BLK_SIZE: usize = 512;
  239. bitflags! {
  240. struct BlkFeature: u64 {
  241. /// Device supports request barriers. (legacy)
  242. const BARRIER = 1 << 0;
  243. /// Maximum size of any single segment is in `size_max`.
  244. const SIZE_MAX = 1 << 1;
  245. /// Maximum number of segments in a request is in `seg_max`.
  246. const SEG_MAX = 1 << 2;
  247. /// Disk-style geometry specified in geometry.
  248. const GEOMETRY = 1 << 4;
  249. /// Device is read-only.
  250. const RO = 1 << 5;
  251. /// Block size of disk is in `blk_size`.
  252. const BLK_SIZE = 1 << 6;
  253. /// Device supports scsi packet commands. (legacy)
  254. const SCSI = 1 << 7;
  255. /// Cache flush command support.
  256. const FLUSH = 1 << 9;
  257. /// Device exports information on optimal I/O alignment.
  258. const TOPOLOGY = 1 << 10;
  259. /// Device can toggle its cache between writeback and writethrough modes.
  260. const CONFIG_WCE = 1 << 11;
  261. /// Device can support discard command, maximum discard sectors size in
  262. /// `max_discard_sectors` and maximum discard segment number in
  263. /// `max_discard_seg`.
  264. const DISCARD = 1 << 13;
  265. /// Device can support write zeroes command, maximum write zeroes sectors
  266. /// size in `max_write_zeroes_sectors` and maximum write zeroes segment
  267. /// number in `max_write_zeroes_seg`.
  268. const WRITE_ZEROES = 1 << 14;
  269. // device independent
  270. const NOTIFY_ON_EMPTY = 1 << 24; // legacy
  271. const ANY_LAYOUT = 1 << 27; // legacy
  272. const RING_INDIRECT_DESC = 1 << 28;
  273. const RING_EVENT_IDX = 1 << 29;
  274. const UNUSED = 1 << 30; // legacy
  275. const VERSION_1 = 1 << 32; // detect legacy
  276. // the following since virtio v1.1
  277. const ACCESS_PLATFORM = 1 << 33;
  278. const RING_PACKED = 1 << 34;
  279. const IN_ORDER = 1 << 35;
  280. const ORDER_PLATFORM = 1 << 36;
  281. const SR_IOV = 1 << 37;
  282. const NOTIFICATION_DATA = 1 << 38;
  283. }
  284. }
  285. unsafe impl AsBuf for BlkReq {}
  286. unsafe impl AsBuf for BlkResp {}