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, FromZeroes}; 11 12 const QUEUE: u16 = 0; 13 const QUEUE_SIZE: u16 = 16; 14 const 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 /// ``` 45 pub 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 52 impl<H: Hal, T: Transport> VirtIOBlk<H, T> { 53 /// Create a new VirtIO-Blk driver. new(mut transport: T) -> Result<Self>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. 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.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. ack_interrupt(&mut self) -> bool95 pub fn ack_interrupt(&mut self) -> bool { 96 self.transport.ack_interrupt() 97 } 98 99 /// Enables interrupts from the device. enable_interrupts(&mut self)100 pub fn enable_interrupts(&mut self) { 101 self.queue.set_dev_notify(true); 102 } 103 104 /// Disables interrupts from the device. disable_interrupts(&mut self)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. request(&mut self, request: BlkReq) -> Result110 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. request_read(&mut self, request: BlkReq, data: &mut [u8]) -> Result121 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. request_write(&mut self, request: BlkReq, data: &[u8]) -> Result132 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. flush(&mut self) -> Result145 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. device_id(&mut self, id: &mut [u8; 20]) -> Result<usize>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. read_blocks(&mut self, block_id: usize, buf: &mut [u8]) -> Result178 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. read_blocks_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<u16>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. complete_read_blocks( &mut self, token: u16, req: &BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<()>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. write_blocks(&mut self, block_id: usize, buf: &[u8]) -> Result294 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]. write_blocks_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<u16>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. complete_write_blocks( &mut self, token: u16, req: &BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<()>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`. peek_used(&mut self) -> Option<u16>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. virt_queue_size(&self) -> u16380 pub fn virt_queue_size(&self) -> u16 { 381 QUEUE_SIZE 382 } 383 } 384 385 impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> { drop(&mut self)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)] 394 struct 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)] 414 pub struct BlkReq { 415 type_: ReqType, 416 reserved: u32, 417 sector: u64, 418 } 419 420 impl Default for BlkReq { default() -> Self421 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)] 433 pub struct BlkResp { 434 status: RespStatus, 435 } 436 437 impl BlkResp { 438 /// Return the status of a VirtIOBlk request. status(&self) -> RespStatus439 pub fn status(&self) -> RespStatus { 440 self.status 441 } 442 } 443 444 #[repr(u32)] 445 #[derive(AsBytes, Debug)] 446 enum 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)] 460 pub struct RespStatus(u8); 461 462 impl 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 473 impl From<RespStatus> for Result { from(status: RespStatus) -> Self474 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 485 impl Default for BlkResp { default() -> Self486 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. 495 pub const SECTOR_SIZE: usize = 512; 496 497 bitflags! { 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)] 554 mod 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] config()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] read()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 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] write()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 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] flush()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 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] device_id()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 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 } 881