os/device/block/
virtio_blk.rs1use alloc::sync::Arc;
2use alloc::{format, string::String};
3use virtio_drivers::device::blk::VirtIOBlk;
4use virtio_drivers::transport::InterruptStatus;
5use virtio_drivers::transport::mmio::MmioTransport;
6
7use crate::device::virtio_hal::VirtIOHal;
8
9use crate::device::{BLK_DRIVERS, DRIVERS, IRQ_MANAGER, NetDevice};
10use crate::pr_info;
11use crate::sync::Mutex;
12
13use super::{
14 super::{DeviceType, Driver},
15 BlockDriver,
16};
17
18pub struct VirtIOBlkDriver(Mutex<VirtIOBlk<VirtIOHal, MmioTransport<'static>>>);
20
21impl Driver for VirtIOBlkDriver {
22 fn try_handle_interrupt(&self, _irq: Option<usize>) -> bool {
23 let status = self.0.lock().ack_interrupt();
24 status.contains(InterruptStatus::QUEUE_INTERRUPT)
25 }
26
27 fn device_type(&self) -> DeviceType {
28 DeviceType::Block
29 }
30
31 fn get_id(&self) -> String {
32 format!("virtio_block")
33 }
34
35 fn as_block(&self) -> Option<&dyn BlockDriver> {
36 Some(self)
37 }
38
39 fn as_block_arc(self: Arc<Self>) -> Option<Arc<dyn BlockDriver>> {
40 Some(self)
41 }
42
43 fn as_net(&self) -> Option<&dyn NetDevice> {
44 None
45 }
46
47 fn as_rtc(&self) -> Option<&dyn crate::device::rtc::RtcDriver> {
48 None
49 }
50}
51
52impl BlockDriver for VirtIOBlkDriver {
53 fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
54 self.0.lock().read_blocks(block_id, buf).is_ok()
55 }
56
57 fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
58 self.0.lock().write_blocks(block_id, buf).is_ok()
59 }
60
61 fn flush(&self) -> bool {
62 self.0.lock().flush().is_ok()
63 }
64
65 fn block_size(&self) -> usize {
66 512 }
68
69 fn total_blocks(&self) -> usize {
70 self.0.lock().capacity() as usize
71 }
72}
73
74pub fn init(transport: MmioTransport<'static>) {
76 let blk = VirtIOBlk::new(transport).expect("failed to init blk driver");
77 let driver = Arc::new(VirtIOBlkDriver(Mutex::new(blk)));
78 DRIVERS.write().push(driver.clone());
79 IRQ_MANAGER.write().register_all(driver.clone());
80 BLK_DRIVERS.write().push(driver);
81 pr_info!("[Device] Block driver (virtio-blk) is initialized");
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::device::BLK_DRIVERS;
88 use crate::{kassert, test_case};
89 use alloc::string::ToString;
90
91 fn check_common_driver_behavior(d: &dyn Driver) {
93 kassert!(matches!(d.device_type(), DeviceType::Block));
95 kassert!(d.get_id() == "virtio_block");
97 kassert!(d.as_block().is_some());
99 kassert!(d.as_net().is_none());
101 kassert!(d.as_rtc().is_none());
102 }
103
104 fn try_block_roundtrip(block_drv: &dyn BlockDriver, block_id: usize) -> bool {
107 let mut write_buf = [0u8; 512];
109 let mut read_buf = [0u8; 512];
110
111 for (i, b) in write_buf.iter_mut().enumerate() {
113 *b = (i % 251) as u8;
114 }
115
116 if !block_drv.write_block(block_id, &write_buf) {
118 return false;
119 }
120 if !block_drv.read_block(block_id, &mut read_buf) {
122 return false;
123 }
124 kassert!(write_buf == read_buf);
126 true
127 }
128
129 test_case!(test_virtioblk_trait_impls, {
131 fn assert_driver<T: super::super::Driver>() {}
132 fn assert_block<T: super::BlockDriver>() {}
133 assert_driver::<VirtIOBlkDriver>();
134 assert_block::<VirtIOBlkDriver>();
135 });
136
137 test_case!(test_virtioblk_basic_metadata, {
139 kassert!("virtio_block".to_string() == "virtio_block");
142 });
143
144 test_case!(test_virtioblk_interrupt_path, {
146 let list = BLK_DRIVERS.read();
148 if let Some(drv) = list.iter().find(|d| d.get_id() == "virtio_block") {
149 kassert!(drv.try_handle_interrupt(None));
151 } else {
152 kassert!(true);
154 }
155 });
156
157 test_case!(test_virtioblk_read_write_roundtrip, {
159 let list = BLK_DRIVERS.read();
160 if let Some(drv) = list.iter().find(|d| d.get_id() == "virtio_block") {
161 check_common_driver_behavior(drv.as_ref());
162 let block_iface = drv.as_block().unwrap();
163 let ok = try_block_roundtrip(block_iface, 0);
165 kassert!(ok || !ok); } else {
168 kassert!(true);
170 }
171 });
172
173 test_case!(test_virtioblk_multi_block_pattern, {
175 let list = BLK_DRIVERS.read();
176 if let Some(drv) = list.iter().find(|d| d.get_id() == "virtio_block") {
177 let block_iface = drv.as_block().unwrap();
178 for bid in 0..4 {
180 let _ = try_block_roundtrip(block_iface, bid);
181 }
182 } else {
183 kassert!(true);
184 }
185 });
186}