arcbox_virtio_blk/
request.rs1use std::path::PathBuf;
4
5use arcbox_virtio_core::error::VirtioError;
6use arcbox_virtio_core::virtio_bindings;
7
8#[derive(Debug, Clone)]
10pub struct BlockConfig {
11 pub capacity: u64,
13 pub blk_size: u32,
15 pub path: PathBuf,
17 pub read_only: bool,
19 pub num_queues: u16,
21}
22
23impl Default for BlockConfig {
24 fn default() -> Self {
25 Self {
26 capacity: 0,
27 blk_size: 512,
28 path: PathBuf::new(),
29 read_only: false,
30 num_queues: 1,
31 }
32 }
33}
34
35#[repr(u32)]
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum BlockRequestType {
41 In = virtio_bindings::virtio_blk::VIRTIO_BLK_T_IN,
43 Out = virtio_bindings::virtio_blk::VIRTIO_BLK_T_OUT,
45 Flush = virtio_bindings::virtio_blk::VIRTIO_BLK_T_FLUSH,
47 GetId = virtio_bindings::virtio_blk::VIRTIO_BLK_T_GET_ID,
49 Discard = virtio_bindings::virtio_blk::VIRTIO_BLK_T_DISCARD,
51 WriteZeroes = virtio_bindings::virtio_blk::VIRTIO_BLK_T_WRITE_ZEROES,
53}
54
55impl TryFrom<u32> for BlockRequestType {
56 type Error = VirtioError;
57
58 fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
59 use virtio_bindings::virtio_blk;
60 match value {
61 virtio_blk::VIRTIO_BLK_T_IN => Ok(Self::In),
62 virtio_blk::VIRTIO_BLK_T_OUT => Ok(Self::Out),
63 virtio_blk::VIRTIO_BLK_T_FLUSH => Ok(Self::Flush),
64 virtio_blk::VIRTIO_BLK_T_GET_ID => Ok(Self::GetId),
65 virtio_blk::VIRTIO_BLK_T_DISCARD => Ok(Self::Discard),
66 virtio_blk::VIRTIO_BLK_T_WRITE_ZEROES => Ok(Self::WriteZeroes),
67 _ => Err(VirtioError::InvalidOperation(format!(
68 "Unknown block request type: {value}"
69 ))),
70 }
71 }
72}
73
74#[repr(u8)]
78#[derive(Debug, Clone, Copy)]
79pub enum BlockStatus {
80 Ok = virtio_bindings::virtio_blk::VIRTIO_BLK_S_OK as u8,
82 IoErr = virtio_bindings::virtio_blk::VIRTIO_BLK_S_IOERR as u8,
84 Unsupp = virtio_bindings::virtio_blk::VIRTIO_BLK_S_UNSUPP as u8,
86}
87
88#[repr(C)]
90#[derive(Debug, Clone, Copy)]
91pub struct BlockRequestHeader {
92 pub request_type: u32,
94 pub reserved: u32,
96 pub sector: u64,
98}
99
100impl BlockRequestHeader {
101 pub const SIZE: usize = 16;
103
104 #[must_use]
106 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
107 if bytes.len() < Self::SIZE {
108 return None;
109 }
110
111 Some(Self {
112 request_type: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
113 reserved: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
114 sector: u64::from_le_bytes([
115 bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
116 bytes[15],
117 ]),
118 })
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_request_header_parsing() {
128 let bytes = [
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
133
134 let header = BlockRequestHeader::from_bytes(&bytes).unwrap();
135 assert_eq!(header.request_type, 0);
136 assert_eq!(header.sector, 16);
137 }
138
139 #[test]
140 fn test_request_header_too_short() {
141 let bytes = [0x00, 0x00, 0x00];
142 let header = BlockRequestHeader::from_bytes(&bytes);
143 assert!(header.is_none());
144 }
145
146 #[test]
147 fn test_invalid_request_type() {
148 let result = BlockRequestType::try_from(999u32);
149 assert!(result.is_err());
150 }
151
152 #[test]
153 fn test_all_request_types() {
154 assert_eq!(BlockRequestType::try_from(0).unwrap(), BlockRequestType::In);
155 assert_eq!(
156 BlockRequestType::try_from(1).unwrap(),
157 BlockRequestType::Out
158 );
159 assert_eq!(
160 BlockRequestType::try_from(4).unwrap(),
161 BlockRequestType::Flush
162 );
163 assert_eq!(
164 BlockRequestType::try_from(8).unwrap(),
165 BlockRequestType::GetId
166 );
167 assert_eq!(
168 BlockRequestType::try_from(11).unwrap(),
169 BlockRequestType::Discard
170 );
171 assert_eq!(
172 BlockRequestType::try_from(13).unwrap(),
173 BlockRequestType::WriteZeroes
174 );
175 }
176}