blk.rs 9.8 KB

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