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}; 11 12 const QUEUE: u16 = 0; 13 const QUEUE_SIZE: u16 = 16; 14 15 /// Driver for a VirtIO block device. 16 /// 17 /// This is a simple virtual block device, e.g. disk. 18 /// 19 /// Read and write requests (and other exotic requests) are placed in the queue and serviced 20 /// (probably out of order) by the device except where noted. 21 /// 22 /// # Example 23 /// 24 /// ``` 25 /// # use virtio_drivers::{Error, Hal}; 26 /// # use virtio_drivers::transport::Transport; 27 /// use virtio_drivers::device::blk::{VirtIOBlk, SECTOR_SIZE}; 28 /// 29 /// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> { 30 /// let mut disk = VirtIOBlk::<HalImpl, _>::new(transport)?; 31 /// 32 /// println!("VirtIO block device: {} kB", disk.capacity() * SECTOR_SIZE as u64 / 2); 33 /// 34 /// // Read sector 0 and then copy it to sector 1. 35 /// let mut buf = [0; SECTOR_SIZE]; 36 /// disk.read_block(0, &mut buf)?; 37 /// disk.write_block(1, &buf)?; 38 /// # Ok(()) 39 /// # } 40 /// ``` 41 pub struct VirtIOBlk<H: Hal, T: Transport> { 42 transport: T, 43 queue: VirtQueue<H, { QUEUE_SIZE as usize }>, 44 capacity: u64, 45 readonly: bool, 46 } 47 48 impl<H: Hal, T: Transport> VirtIOBlk<H, T> { 49 /// Create a new VirtIO-Blk driver. new(mut transport: T) -> Result<Self>50 pub fn new(mut transport: T) -> Result<Self> { 51 let mut readonly = false; 52 53 transport.begin_init(|features| { 54 let features = BlkFeature::from_bits_truncate(features); 55 info!("device features: {:?}", features); 56 readonly = features.contains(BlkFeature::RO); 57 // negotiate these flags only 58 let supported_features = BlkFeature::empty(); 59 (features & supported_features).bits() 60 }); 61 62 // read configuration space 63 let config = transport.config_space::<BlkConfig>()?; 64 info!("config: {:?}", config); 65 // Safe because config is a valid pointer to the device configuration space. 66 let capacity = unsafe { 67 volread!(config, capacity_low) as u64 | (volread!(config, capacity_high) as u64) << 32 68 }; 69 info!("found a block device of size {}KB", capacity / 2); 70 71 let queue = VirtQueue::new(&mut transport, QUEUE)?; 72 transport.finish_init(); 73 74 Ok(VirtIOBlk { 75 transport, 76 queue, 77 capacity, 78 readonly, 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.readonly 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 /// Reads a block into the given buffer. 100 /// 101 /// Blocks until the read completes or there is an error. read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result102 pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result { 103 assert_eq!(buf.len(), SECTOR_SIZE); 104 let req = BlkReq { 105 type_: ReqType::In, 106 reserved: 0, 107 sector: block_id as u64, 108 }; 109 let mut resp = BlkResp::default(); 110 self.queue.add_notify_wait_pop( 111 &[req.as_bytes()], 112 &mut [buf, resp.as_bytes_mut()], 113 &mut self.transport, 114 )?; 115 resp.status.into() 116 } 117 118 /// Submits a request to read a block, but returns immediately without waiting for the read to 119 /// complete. 120 /// 121 /// # Arguments 122 /// 123 /// * `block_id` - The identifier of the block to read. 124 /// * `req` - A buffer which the driver can use for the request to send to the device. The 125 /// contents don't matter as `read_block_nb` will initialise it, but like the other buffers it 126 /// needs to be valid (and not otherwise used) until the corresponding `complete_read_block` 127 /// call. 128 /// * `buf` - The buffer in memory into which the block should be read. 129 /// * `resp` - A mutable reference to a variable provided by the caller 130 /// to contain the status of the request. The caller can safely 131 /// read the variable only after the request is complete. 132 /// 133 /// # Usage 134 /// 135 /// It will submit request to the VirtIO block device and return a token identifying 136 /// the position of the first Descriptor in the chain. If there are not enough 137 /// Descriptors to allocate, then it returns [`Error::QueueFull`]. 138 /// 139 /// The caller can then call `peek_used` with the returned token to check whether the device has 140 /// finished handling the request. Once it has, the caller must call `complete_read_block` with 141 /// the same buffers before reading the response. 142 /// 143 /// ``` 144 /// # use virtio_drivers::{Error, Hal}; 145 /// # use virtio_drivers::device::blk::VirtIOBlk; 146 /// # use virtio_drivers::transport::Transport; 147 /// use virtio_drivers::device::blk::{BlkReq, BlkResp, RespStatus}; 148 /// 149 /// # fn example<H: Hal, T: Transport>(blk: &mut VirtIOBlk<H, T>) -> Result<(), Error> { 150 /// let mut request = BlkReq::default(); 151 /// let mut buffer = [0; 512]; 152 /// let mut response = BlkResp::default(); 153 /// let token = unsafe { blk.read_block_nb(42, &mut request, &mut buffer, &mut response) }?; 154 /// 155 /// // Wait for an interrupt to tell us that the request completed... 156 /// assert_eq!(blk.peek_used(), Some(token)); 157 /// 158 /// unsafe { 159 /// blk.complete_read_block(token, &request, &mut buffer, &mut response)?; 160 /// } 161 /// if response.status() == RespStatus::OK { 162 /// println!("Successfully read block."); 163 /// } else { 164 /// println!("Error {:?} reading block.", response.status()); 165 /// } 166 /// # Ok(()) 167 /// # } 168 /// ``` 169 /// 170 /// # Safety 171 /// 172 /// `req`, `buf` and `resp` are still borrowed by the underlying VirtIO block device even after 173 /// this method returns. Thus, it is the caller's responsibility to guarantee that they are not 174 /// accessed before the request is completed in order to avoid data races. read_block_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<u16>175 pub unsafe fn read_block_nb( 176 &mut self, 177 block_id: usize, 178 req: &mut BlkReq, 179 buf: &mut [u8], 180 resp: &mut BlkResp, 181 ) -> Result<u16> { 182 assert_eq!(buf.len(), SECTOR_SIZE); 183 *req = BlkReq { 184 type_: ReqType::In, 185 reserved: 0, 186 sector: block_id as u64, 187 }; 188 let token = self 189 .queue 190 .add(&[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?; 191 if self.queue.should_notify() { 192 self.transport.notify(QUEUE); 193 } 194 Ok(token) 195 } 196 197 /// Completes a read operation which was started by `read_block_nb`. 198 /// 199 /// # Safety 200 /// 201 /// The same buffers must be passed in again as were passed to `read_block_nb` when it returned 202 /// the token. complete_read_block( &mut self, token: u16, req: &BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<()>203 pub unsafe fn complete_read_block( 204 &mut self, 205 token: u16, 206 req: &BlkReq, 207 buf: &mut [u8], 208 resp: &mut BlkResp, 209 ) -> Result<()> { 210 self.queue 211 .pop_used(token, &[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?; 212 resp.status.into() 213 } 214 215 /// Writes the contents of the given buffer to a block. 216 /// 217 /// Blocks until the write is complete or there is an error. write_block(&mut self, block_id: usize, buf: &[u8]) -> Result218 pub fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result { 219 assert_eq!(buf.len(), SECTOR_SIZE); 220 let req = BlkReq { 221 type_: ReqType::Out, 222 reserved: 0, 223 sector: block_id as u64, 224 }; 225 let mut resp = BlkResp::default(); 226 self.queue.add_notify_wait_pop( 227 &[req.as_bytes(), buf], 228 &mut [resp.as_bytes_mut()], 229 &mut self.transport, 230 )?; 231 resp.status.into() 232 } 233 234 /// Submits a request to write a block, but returns immediately without waiting for the write to 235 /// complete. 236 /// 237 /// # Arguments 238 /// 239 /// * `block_id` - The identifier of the block to write. 240 /// * `req` - A buffer which the driver can use for the request to send to the device. The 241 /// contents don't matter as `read_block_nb` will initialise it, but like the other buffers it 242 /// needs to be valid (and not otherwise used) until the corresponding `complete_read_block` 243 /// call. 244 /// * `buf` - The buffer in memory containing the data to write to the block. 245 /// * `resp` - A mutable reference to a variable provided by the caller 246 /// to contain the status of the request. The caller can safely 247 /// read the variable only after the request is complete. 248 /// 249 /// # Usage 250 /// 251 /// See [VirtIOBlk::read_block_nb]. 252 /// 253 /// # Safety 254 /// 255 /// See [VirtIOBlk::read_block_nb]. write_block_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<u16>256 pub unsafe fn write_block_nb( 257 &mut self, 258 block_id: usize, 259 req: &mut BlkReq, 260 buf: &[u8], 261 resp: &mut BlkResp, 262 ) -> Result<u16> { 263 assert_eq!(buf.len(), SECTOR_SIZE); 264 *req = BlkReq { 265 type_: ReqType::Out, 266 reserved: 0, 267 sector: block_id as u64, 268 }; 269 let token = self 270 .queue 271 .add(&[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?; 272 if self.queue.should_notify() { 273 self.transport.notify(QUEUE); 274 } 275 Ok(token) 276 } 277 278 /// Completes a write operation which was started by `write_block_nb`. 279 /// 280 /// # Safety 281 /// 282 /// The same buffers must be passed in again as were passed to `write_block_nb` when it returned 283 /// the token. complete_write_block( &mut self, token: u16, req: &BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<()>284 pub unsafe fn complete_write_block( 285 &mut self, 286 token: u16, 287 req: &BlkReq, 288 buf: &[u8], 289 resp: &mut BlkResp, 290 ) -> Result<()> { 291 self.queue 292 .pop_used(token, &[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?; 293 resp.status.into() 294 } 295 296 /// Fetches the token of the next completed request from the used ring and returns it, without 297 /// removing it from the used ring. If there are no pending completed requests returns `None`. peek_used(&mut self) -> Option<u16>298 pub fn peek_used(&mut self) -> Option<u16> { 299 self.queue.peek_used() 300 } 301 302 /// Returns the size of the device's VirtQueue. 303 /// 304 /// This can be used to tell the caller how many channels to monitor on. virt_queue_size(&self) -> u16305 pub fn virt_queue_size(&self) -> u16 { 306 QUEUE_SIZE 307 } 308 } 309 310 impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> { drop(&mut self)311 fn drop(&mut self) { 312 // Clear any pointers pointing to DMA regions, so the device doesn't try to access them 313 // after they have been freed. 314 self.transport.queue_unset(QUEUE); 315 } 316 } 317 318 #[repr(C)] 319 struct BlkConfig { 320 /// Number of 512 Bytes sectors 321 capacity_low: Volatile<u32>, 322 capacity_high: Volatile<u32>, 323 size_max: Volatile<u32>, 324 seg_max: Volatile<u32>, 325 cylinders: Volatile<u16>, 326 heads: Volatile<u8>, 327 sectors: Volatile<u8>, 328 blk_size: Volatile<u32>, 329 physical_block_exp: Volatile<u8>, 330 alignment_offset: Volatile<u8>, 331 min_io_size: Volatile<u16>, 332 opt_io_size: Volatile<u32>, 333 // ... ignored 334 } 335 336 /// A VirtIO block device request. 337 #[repr(C)] 338 #[derive(AsBytes, Debug)] 339 pub struct BlkReq { 340 type_: ReqType, 341 reserved: u32, 342 sector: u64, 343 } 344 345 impl Default for BlkReq { default() -> Self346 fn default() -> Self { 347 Self { 348 type_: ReqType::In, 349 reserved: 0, 350 sector: 0, 351 } 352 } 353 } 354 355 /// Response of a VirtIOBlk request. 356 #[repr(C)] 357 #[derive(AsBytes, Debug, FromBytes)] 358 pub struct BlkResp { 359 status: RespStatus, 360 } 361 362 impl BlkResp { 363 /// Return the status of a VirtIOBlk request. status(&self) -> RespStatus364 pub fn status(&self) -> RespStatus { 365 self.status 366 } 367 } 368 369 #[repr(u32)] 370 #[derive(AsBytes, Debug)] 371 enum ReqType { 372 In = 0, 373 Out = 1, 374 Flush = 4, 375 Discard = 11, 376 WriteZeroes = 13, 377 } 378 379 /// Status of a VirtIOBlk request. 380 #[repr(transparent)] 381 #[derive(AsBytes, Copy, Clone, Debug, Eq, FromBytes, PartialEq)] 382 pub struct RespStatus(u8); 383 384 impl RespStatus { 385 /// Ok. 386 pub const OK: RespStatus = RespStatus(0); 387 /// IoErr. 388 pub const IO_ERR: RespStatus = RespStatus(1); 389 /// Unsupported yet. 390 pub const UNSUPPORTED: RespStatus = RespStatus(2); 391 /// Not ready. 392 pub const NOT_READY: RespStatus = RespStatus(3); 393 } 394 395 impl From<RespStatus> for Result { from(status: RespStatus) -> Self396 fn from(status: RespStatus) -> Self { 397 match status { 398 RespStatus::OK => Ok(()), 399 RespStatus::IO_ERR => Err(Error::IoError), 400 RespStatus::UNSUPPORTED => Err(Error::Unsupported), 401 RespStatus::NOT_READY => Err(Error::NotReady), 402 _ => Err(Error::IoError), 403 } 404 } 405 } 406 407 impl Default for BlkResp { default() -> Self408 fn default() -> Self { 409 BlkResp { 410 status: RespStatus::NOT_READY, 411 } 412 } 413 } 414 415 /// The standard sector size of a VirtIO block device. Data is read and written in multiples of this 416 /// size. 417 pub const SECTOR_SIZE: usize = 512; 418 419 bitflags! { 420 struct BlkFeature: u64 { 421 /// Device supports request barriers. (legacy) 422 const BARRIER = 1 << 0; 423 /// Maximum size of any single segment is in `size_max`. 424 const SIZE_MAX = 1 << 1; 425 /// Maximum number of segments in a request is in `seg_max`. 426 const SEG_MAX = 1 << 2; 427 /// Disk-style geometry specified in geometry. 428 const GEOMETRY = 1 << 4; 429 /// Device is read-only. 430 const RO = 1 << 5; 431 /// Block size of disk is in `blk_size`. 432 const BLK_SIZE = 1 << 6; 433 /// Device supports scsi packet commands. (legacy) 434 const SCSI = 1 << 7; 435 /// Cache flush command support. 436 const FLUSH = 1 << 9; 437 /// Device exports information on optimal I/O alignment. 438 const TOPOLOGY = 1 << 10; 439 /// Device can toggle its cache between writeback and writethrough modes. 440 const CONFIG_WCE = 1 << 11; 441 /// Device can support discard command, maximum discard sectors size in 442 /// `max_discard_sectors` and maximum discard segment number in 443 /// `max_discard_seg`. 444 const DISCARD = 1 << 13; 445 /// Device can support write zeroes command, maximum write zeroes sectors 446 /// size in `max_write_zeroes_sectors` and maximum write zeroes segment 447 /// number in `max_write_zeroes_seg`. 448 const WRITE_ZEROES = 1 << 14; 449 450 // device independent 451 const NOTIFY_ON_EMPTY = 1 << 24; // legacy 452 const ANY_LAYOUT = 1 << 27; // legacy 453 const RING_INDIRECT_DESC = 1 << 28; 454 const RING_EVENT_IDX = 1 << 29; 455 const UNUSED = 1 << 30; // legacy 456 const VERSION_1 = 1 << 32; // detect legacy 457 458 // the following since virtio v1.1 459 const ACCESS_PLATFORM = 1 << 33; 460 const RING_PACKED = 1 << 34; 461 const IN_ORDER = 1 << 35; 462 const ORDER_PLATFORM = 1 << 36; 463 const SR_IOV = 1 << 37; 464 const NOTIFICATION_DATA = 1 << 38; 465 } 466 } 467 468 #[cfg(test)] 469 mod tests { 470 use super::*; 471 use crate::{ 472 hal::fake::FakeHal, 473 transport::{ 474 fake::{FakeTransport, QueueStatus, State}, 475 DeviceStatus, DeviceType, 476 }, 477 }; 478 use alloc::{sync::Arc, vec}; 479 use core::{mem::size_of, ptr::NonNull}; 480 use std::{sync::Mutex, thread, time::Duration}; 481 482 #[test] config()483 fn config() { 484 let mut config_space = BlkConfig { 485 capacity_low: Volatile::new(0x42), 486 capacity_high: Volatile::new(0x02), 487 size_max: Volatile::new(0), 488 seg_max: Volatile::new(0), 489 cylinders: Volatile::new(0), 490 heads: Volatile::new(0), 491 sectors: Volatile::new(0), 492 blk_size: Volatile::new(0), 493 physical_block_exp: Volatile::new(0), 494 alignment_offset: Volatile::new(0), 495 min_io_size: Volatile::new(0), 496 opt_io_size: Volatile::new(0), 497 }; 498 let state = Arc::new(Mutex::new(State { 499 status: DeviceStatus::empty(), 500 driver_features: 0, 501 guest_page_size: 0, 502 interrupt_pending: false, 503 queues: vec![QueueStatus::default(); 1], 504 })); 505 let transport = FakeTransport { 506 device_type: DeviceType::Console, 507 max_queue_size: QUEUE_SIZE.into(), 508 device_features: BlkFeature::RO.bits(), 509 config_space: NonNull::from(&mut config_space), 510 state: state.clone(), 511 }; 512 let blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap(); 513 514 assert_eq!(blk.capacity(), 0x02_0000_0042); 515 assert_eq!(blk.readonly(), true); 516 } 517 518 #[test] read()519 fn read() { 520 let mut config_space = BlkConfig { 521 capacity_low: Volatile::new(66), 522 capacity_high: Volatile::new(0), 523 size_max: Volatile::new(0), 524 seg_max: Volatile::new(0), 525 cylinders: Volatile::new(0), 526 heads: Volatile::new(0), 527 sectors: Volatile::new(0), 528 blk_size: Volatile::new(0), 529 physical_block_exp: Volatile::new(0), 530 alignment_offset: Volatile::new(0), 531 min_io_size: Volatile::new(0), 532 opt_io_size: Volatile::new(0), 533 }; 534 let state = Arc::new(Mutex::new(State { 535 status: DeviceStatus::empty(), 536 driver_features: 0, 537 guest_page_size: 0, 538 interrupt_pending: false, 539 queues: vec![QueueStatus::default(); 1], 540 })); 541 let transport = FakeTransport { 542 device_type: DeviceType::Console, 543 max_queue_size: QUEUE_SIZE.into(), 544 device_features: 0, 545 config_space: NonNull::from(&mut config_space), 546 state: state.clone(), 547 }; 548 let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap(); 549 550 // Start a thread to simulate the device waiting for a read request. 551 let handle = thread::spawn(move || { 552 println!("Device waiting for a request."); 553 while !state.lock().unwrap().queues[usize::from(QUEUE)].notified { 554 thread::sleep(Duration::from_millis(10)); 555 } 556 println!("Transmit queue was notified."); 557 558 state 559 .lock() 560 .unwrap() 561 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| { 562 assert_eq!( 563 request, 564 BlkReq { 565 type_: ReqType::In, 566 reserved: 0, 567 sector: 42 568 } 569 .as_bytes() 570 ); 571 572 let mut response = vec![0; SECTOR_SIZE]; 573 response[0..9].copy_from_slice(b"Test data"); 574 response.extend_from_slice( 575 BlkResp { 576 status: RespStatus::OK, 577 } 578 .as_bytes(), 579 ); 580 581 response 582 }); 583 }); 584 585 // Read a block from the device. 586 let mut buffer = [0; 512]; 587 blk.read_block(42, &mut buffer).unwrap(); 588 assert_eq!(&buffer[0..9], b"Test data"); 589 590 handle.join().unwrap(); 591 } 592 593 #[test] write()594 fn write() { 595 let mut config_space = BlkConfig { 596 capacity_low: Volatile::new(66), 597 capacity_high: Volatile::new(0), 598 size_max: Volatile::new(0), 599 seg_max: Volatile::new(0), 600 cylinders: Volatile::new(0), 601 heads: Volatile::new(0), 602 sectors: Volatile::new(0), 603 blk_size: Volatile::new(0), 604 physical_block_exp: Volatile::new(0), 605 alignment_offset: Volatile::new(0), 606 min_io_size: Volatile::new(0), 607 opt_io_size: Volatile::new(0), 608 }; 609 let state = Arc::new(Mutex::new(State { 610 status: DeviceStatus::empty(), 611 driver_features: 0, 612 guest_page_size: 0, 613 interrupt_pending: false, 614 queues: vec![QueueStatus::default(); 1], 615 })); 616 let transport = FakeTransport { 617 device_type: DeviceType::Console, 618 max_queue_size: QUEUE_SIZE.into(), 619 device_features: 0, 620 config_space: NonNull::from(&mut config_space), 621 state: state.clone(), 622 }; 623 let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap(); 624 625 // Start a thread to simulate the device waiting for a write request. 626 let handle = thread::spawn(move || { 627 println!("Device waiting for a request."); 628 while !state.lock().unwrap().queues[usize::from(QUEUE)].notified { 629 thread::sleep(Duration::from_millis(10)); 630 } 631 println!("Transmit queue was notified."); 632 633 state 634 .lock() 635 .unwrap() 636 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| { 637 assert_eq!( 638 &request[0..size_of::<BlkReq>()], 639 BlkReq { 640 type_: ReqType::Out, 641 reserved: 0, 642 sector: 42 643 } 644 .as_bytes() 645 ); 646 let data = &request[size_of::<BlkReq>()..]; 647 assert_eq!(data.len(), SECTOR_SIZE); 648 assert_eq!(&data[0..9], b"Test data"); 649 650 let mut response = Vec::new(); 651 response.extend_from_slice( 652 BlkResp { 653 status: RespStatus::OK, 654 } 655 .as_bytes(), 656 ); 657 658 response 659 }); 660 }); 661 662 // Write a block to the device. 663 let mut buffer = [0; 512]; 664 buffer[0..9].copy_from_slice(b"Test data"); 665 blk.write_block(42, &mut buffer).unwrap(); 666 667 handle.join().unwrap(); 668 } 669 } 670