1use crate::hal::Hal;
4use crate::queue::VirtQueue;
5use crate::transport::Transport;
6use crate::volatile::{volread, Volatile};
7use crate::{Error, Result};
8use bitflags::bitflags;
9use log::info;
10use zerocopy::{AsBytes, FromBytes, FromZeroes};
11
12const QUEUE: u16 = 0;
13const QUEUE_SIZE: u16 = 16;
14const SUPPORTED_FEATURES: BlkFeature = BlkFeature::RO
15 .union(BlkFeature::FLUSH)
16 .union(BlkFeature::RING_INDIRECT_DESC)
17 .union(BlkFeature::RING_EVENT_IDX);
18
19pub 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
52impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
53 pub fn new(mut transport: T) -> Result<Self> {
55 let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
56
57 let config = transport.config_space::<BlkConfig>()?;
59 info!("config: {:?}", config);
60 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 pub fn capacity(&self) -> u64 {
84 self.capacity
85 }
86
87 pub fn readonly(&self) -> bool {
89 self.negotiated_features.contains(BlkFeature::RO)
90 }
91
92 pub fn ack_interrupt(&mut self) -> bool {
96 self.transport.ack_interrupt()
97 }
98
99 pub fn enable_interrupts(&mut self) {
101 self.queue.set_dev_notify(true);
102 }
103
104 pub fn disable_interrupts(&mut self) {
106 self.queue.set_dev_notify(false);
107 }
108
109 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 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 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 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 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 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 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 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 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 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 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 pub fn peek_used(&mut self) -> Option<u16> {
374 self.queue.peek_used()
375 }
376
377 pub fn virt_queue_size(&self) -> u16 {
381 QUEUE_SIZE
382 }
383}
384
385impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> {
386 fn drop(&mut self) {
387 self.transport.queue_unset(QUEUE);
390 }
391}
392
393#[repr(C)]
394struct BlkConfig {
395 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 }
410
411#[repr(C)]
413#[derive(AsBytes, Debug)]
414pub struct BlkReq {
415 type_: ReqType,
416 reserved: u32,
417 sector: u64,
418}
419
420impl Default for BlkReq {
421 fn default() -> Self {
422 Self {
423 type_: ReqType::In,
424 reserved: 0,
425 sector: 0,
426 }
427 }
428}
429
430#[repr(C)]
432#[derive(AsBytes, Debug, FromBytes, FromZeroes)]
433pub struct BlkResp {
434 status: RespStatus,
435}
436
437impl BlkResp {
438 pub fn status(&self) -> RespStatus {
440 self.status
441 }
442}
443
444#[repr(u32)]
445#[derive(AsBytes, Debug)]
446enum 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#[repr(transparent)]
459#[derive(AsBytes, Copy, Clone, Debug, Eq, FromBytes, FromZeroes, PartialEq)]
460pub struct RespStatus(u8);
461
462impl RespStatus {
463 pub const OK: RespStatus = RespStatus(0);
465 pub const IO_ERR: RespStatus = RespStatus(1);
467 pub const UNSUPPORTED: RespStatus = RespStatus(2);
469 pub const NOT_READY: RespStatus = RespStatus(3);
471}
472
473impl From<RespStatus> for Result {
474 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
485impl Default for BlkResp {
486 fn default() -> Self {
487 BlkResp {
488 status: RespStatus::NOT_READY,
489 }
490 }
491}
492
493pub const SECTOR_SIZE: usize = 512;
496
497bitflags! {
498 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
499 struct BlkFeature: u64 {
500 const BARRIER = 1 << 0;
502 const SIZE_MAX = 1 << 1;
504 const SEG_MAX = 1 << 2;
506 const GEOMETRY = 1 << 4;
508 const RO = 1 << 5;
510 const BLK_SIZE = 1 << 6;
512 const SCSI = 1 << 7;
514 const FLUSH = 1 << 9;
516 const TOPOLOGY = 1 << 10;
518 const CONFIG_WCE = 1 << 11;
520 const MQ = 1 << 12;
522 const DISCARD = 1 << 13;
526 const WRITE_ZEROES = 1 << 14;
530 const LIFETIME = 1 << 15;
532 const SECURE_ERASE = 1 << 16;
534
535 const NOTIFY_ON_EMPTY = 1 << 24; const ANY_LAYOUT = 1 << 27; const RING_INDIRECT_DESC = 1 << 28;
539 const RING_EVENT_IDX = 1 << 29;
540 const UNUSED = 1 << 30; const VERSION_1 = 1 << 32; 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)]
554mod 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]
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]
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 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 assert!(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 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]
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 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 assert!(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 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 blk.flush().unwrap();
741
742 handle.join().unwrap();
743 }
744
745 #[test]
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 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 assert!(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 blk.flush().unwrap();
808
809 handle.join().unwrap();
810 }
811
812 #[test]
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 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 assert!(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}