1use 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
35pub const WRITE_ZEROES_FLAG_UNMAP: u32 = 1;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct DiscardWriteZeroesRange {
46 pub sector: u64,
48 pub num_sectors: u32,
50 pub flags: u32,
52}
53
54const RANGE_ENTRY_SIZE: usize = 16;
55
56pub fn parse_range_list(
63 bytes: &[u8],
64) -> std::result::Result<Vec<DiscardWriteZeroesRange>, VirtioError> {
65 if bytes.is_empty() || bytes.len() % RANGE_ENTRY_SIZE != 0 {
66 return Err(VirtioError::InvalidOperation(format!(
67 "range list size {} not a multiple of 16",
68 bytes.len()
69 )));
70 }
71 let mut ranges = Vec::with_capacity(bytes.len() / RANGE_ENTRY_SIZE);
72 for chunk in bytes.chunks_exact(RANGE_ENTRY_SIZE) {
73 ranges.push(DiscardWriteZeroesRange {
74 sector: u64::from_le_bytes(chunk[0..8].try_into().unwrap()),
75 num_sectors: u32::from_le_bytes(chunk[8..12].try_into().unwrap()),
76 flags: u32::from_le_bytes(chunk[12..16].try_into().unwrap()),
77 });
78 }
79 Ok(ranges)
80}
81
82impl DiscardWriteZeroesRange {
83 pub fn checked_byte_range(
90 self,
91 blk_size: u32,
92 capacity_sectors: u64,
93 ) -> std::result::Result<(u64, u64), VirtioError> {
94 let sector_end = self
95 .sector
96 .checked_add(u64::from(self.num_sectors))
97 .ok_or_else(|| VirtioError::InvalidOperation("range sector overflow".into()))?;
98 if sector_end > capacity_sectors {
99 return Err(VirtioError::InvalidOperation(format!(
100 "range exceeds device capacity: {}..{} > {} sectors",
101 self.sector, sector_end, capacity_sectors
102 )));
103 }
104
105 let block_size = u64::from(blk_size);
106 let start = self
107 .sector
108 .checked_mul(block_size)
109 .ok_or_else(|| VirtioError::InvalidOperation("range byte offset overflow".into()))?;
110 let bytes = u64::from(self.num_sectors)
111 .checked_mul(block_size)
112 .ok_or_else(|| VirtioError::InvalidOperation("range byte length overflow".into()))?;
113 let end = start
114 .checked_add(bytes)
115 .ok_or_else(|| VirtioError::InvalidOperation("range byte end overflow".into()))?;
116 Ok((start, end))
117 }
118}
119
120pub fn checked_io_byte_range(
127 sector: u64,
128 byte_len: usize,
129 blk_size: u32,
130 capacity_sectors: u64,
131) -> std::result::Result<(u64, u64), VirtioError> {
132 let block_size = u64::from(blk_size);
133 let capacity_bytes = capacity_sectors
134 .checked_mul(block_size)
135 .ok_or_else(|| VirtioError::InvalidOperation("device capacity overflow".into()))?;
136 let start = sector
137 .checked_mul(block_size)
138 .ok_or_else(|| VirtioError::InvalidOperation("I/O byte offset overflow".into()))?;
139 let len = u64::try_from(byte_len)
140 .map_err(|_| VirtioError::InvalidOperation("I/O byte length overflow".into()))?;
141 let end = start
142 .checked_add(len)
143 .ok_or_else(|| VirtioError::InvalidOperation("I/O byte end overflow".into()))?;
144 if end > capacity_bytes {
145 return Err(VirtioError::InvalidOperation(format!(
146 "I/O exceeds device capacity: bytes {}..{} > {}",
147 start, end, capacity_bytes
148 )));
149 }
150 Ok((start, end))
151}
152
153#[repr(u32)]
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub enum BlockRequestType {
159 In = virtio_bindings::virtio_blk::VIRTIO_BLK_T_IN,
161 Out = virtio_bindings::virtio_blk::VIRTIO_BLK_T_OUT,
163 Flush = virtio_bindings::virtio_blk::VIRTIO_BLK_T_FLUSH,
165 GetId = virtio_bindings::virtio_blk::VIRTIO_BLK_T_GET_ID,
167 Discard = virtio_bindings::virtio_blk::VIRTIO_BLK_T_DISCARD,
169 WriteZeroes = virtio_bindings::virtio_blk::VIRTIO_BLK_T_WRITE_ZEROES,
171}
172
173impl TryFrom<u32> for BlockRequestType {
174 type Error = VirtioError;
175
176 fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
177 use virtio_bindings::virtio_blk;
178 match value {
179 virtio_blk::VIRTIO_BLK_T_IN => Ok(Self::In),
180 virtio_blk::VIRTIO_BLK_T_OUT => Ok(Self::Out),
181 virtio_blk::VIRTIO_BLK_T_FLUSH => Ok(Self::Flush),
182 virtio_blk::VIRTIO_BLK_T_GET_ID => Ok(Self::GetId),
183 virtio_blk::VIRTIO_BLK_T_DISCARD => Ok(Self::Discard),
184 virtio_blk::VIRTIO_BLK_T_WRITE_ZEROES => Ok(Self::WriteZeroes),
185 _ => Err(VirtioError::InvalidOperation(format!(
186 "Unknown block request type: {value}"
187 ))),
188 }
189 }
190}
191
192#[repr(u8)]
196#[derive(Debug, Clone, Copy)]
197pub enum BlockStatus {
198 Ok = virtio_bindings::virtio_blk::VIRTIO_BLK_S_OK as u8,
200 IoErr = virtio_bindings::virtio_blk::VIRTIO_BLK_S_IOERR as u8,
202 Unsupp = virtio_bindings::virtio_blk::VIRTIO_BLK_S_UNSUPP as u8,
204}
205
206#[repr(C)]
208#[derive(Debug, Clone, Copy)]
209pub struct BlockRequestHeader {
210 pub request_type: u32,
212 pub reserved: u32,
214 pub sector: u64,
216}
217
218impl BlockRequestHeader {
219 pub const SIZE: usize = 16;
221
222 #[must_use]
224 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
225 if bytes.len() < Self::SIZE {
226 return None;
227 }
228
229 Some(Self {
230 request_type: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
231 reserved: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
232 sector: u64::from_le_bytes([
233 bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
234 bytes[15],
235 ]),
236 })
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_request_header_parsing() {
246 let bytes = [
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
251
252 let header = BlockRequestHeader::from_bytes(&bytes).unwrap();
253 assert_eq!(header.request_type, 0);
254 assert_eq!(header.sector, 16);
255 }
256
257 #[test]
258 fn test_request_header_too_short() {
259 let bytes = [0x00, 0x00, 0x00];
260 let header = BlockRequestHeader::from_bytes(&bytes);
261 assert!(header.is_none());
262 }
263
264 #[test]
265 fn test_invalid_request_type() {
266 let result = BlockRequestType::try_from(999u32);
267 assert!(result.is_err());
268 }
269
270 #[test]
271 fn test_all_request_types() {
272 assert_eq!(BlockRequestType::try_from(0).unwrap(), BlockRequestType::In);
273 assert_eq!(
274 BlockRequestType::try_from(1).unwrap(),
275 BlockRequestType::Out
276 );
277 assert_eq!(
278 BlockRequestType::try_from(4).unwrap(),
279 BlockRequestType::Flush
280 );
281 assert_eq!(
282 BlockRequestType::try_from(8).unwrap(),
283 BlockRequestType::GetId
284 );
285 assert_eq!(
286 BlockRequestType::try_from(11).unwrap(),
287 BlockRequestType::Discard
288 );
289 assert_eq!(
290 BlockRequestType::try_from(13).unwrap(),
291 BlockRequestType::WriteZeroes
292 );
293 }
294}