virtio_accel_transport/
regions.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum RegionDirection {
6 DeviceReadable,
8 DeviceWritable,
10}
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct ChainRegion {
15 pub direction: RegionDirection,
17 pub bytes: u64,
19}
20
21impl ChainRegion {
22 pub const fn readable(bytes: u64) -> Self {
24 Self {
25 direction: RegionDirection::DeviceReadable,
26 bytes,
27 }
28 }
29
30 pub const fn writable(bytes: u64) -> Self {
32 Self {
33 direction: RegionDirection::DeviceWritable,
34 bytes,
35 }
36 }
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum ChainLayoutError {
42 DescriptorCount,
44 ZeroLength,
46 Direction,
48 LengthOverflow,
50 PortLengthMismatch,
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub struct ChainLayout {
57 descriptor_count: u16,
58 readable_descriptors: u16,
59 writable_descriptors: u16,
60 readable_bytes: u64,
61 writable_bytes: u64,
62}
63
64impl ChainLayout {
65 pub const fn descriptor_count(self) -> u16 {
67 self.descriptor_count
68 }
69
70 pub const fn readable_descriptors(self) -> u16 {
72 self.readable_descriptors
73 }
74
75 pub const fn writable_descriptors(self) -> u16 {
77 self.writable_descriptors
78 }
79
80 pub const fn readable_bytes(self) -> u64 {
82 self.readable_bytes
83 }
84
85 pub const fn writable_bytes(self) -> u64 {
87 self.writable_bytes
88 }
89
90 pub const fn validate_port_lengths(
92 self,
93 request_bytes: u64,
94 response_bytes: u64,
95 ) -> Result<(), ChainLayoutError> {
96 if request_bytes != self.readable_bytes || response_bytes != self.writable_bytes {
97 return Err(ChainLayoutError::PortLengthMismatch);
98 }
99 Ok(())
100 }
101}
102
103pub fn validate_chain_layout(
108 regions: &[ChainRegion],
109 max_descriptors: u16,
110) -> Result<ChainLayout, ChainLayoutError> {
111 if regions.len() < 2 || regions.len() > usize::from(max_descriptors) {
112 return Err(ChainLayoutError::DescriptorCount);
113 }
114
115 let mut readable_descriptors = 0_u16;
116 let mut writable_descriptors = 0_u16;
117 let mut readable_bytes = 0_u64;
118 let mut writable_bytes = 0_u64;
119 let mut saw_writable = false;
120
121 for region in regions {
122 if region.bytes == 0 {
123 return Err(ChainLayoutError::ZeroLength);
124 }
125 match region.direction {
126 RegionDirection::DeviceReadable => {
127 if saw_writable {
128 return Err(ChainLayoutError::Direction);
129 }
130 readable_descriptors += 1;
131 readable_bytes = readable_bytes
132 .checked_add(region.bytes)
133 .ok_or(ChainLayoutError::LengthOverflow)?;
134 }
135 RegionDirection::DeviceWritable => {
136 saw_writable = true;
137 writable_descriptors += 1;
138 writable_bytes = writable_bytes
139 .checked_add(region.bytes)
140 .ok_or(ChainLayoutError::LengthOverflow)?;
141 }
142 }
143 }
144
145 if readable_descriptors == 0 || writable_descriptors == 0 {
146 return Err(ChainLayoutError::Direction);
147 }
148
149 Ok(ChainLayout {
150 descriptor_count: regions.len() as u16,
151 readable_descriptors,
152 writable_descriptors,
153 readable_bytes,
154 writable_bytes,
155 })
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn chain_layout_rejects_direction_and_length_errors() {
164 assert_eq!(
165 validate_chain_layout(&[ChainRegion::readable(16)], 8),
166 Err(ChainLayoutError::DescriptorCount)
167 );
168 assert_eq!(
169 validate_chain_layout(
170 &[
171 ChainRegion::readable(16),
172 ChainRegion::writable(16),
173 ChainRegion::readable(1),
174 ],
175 8,
176 ),
177 Err(ChainLayoutError::Direction)
178 );
179 assert_eq!(
180 validate_chain_layout(&[ChainRegion::readable(0), ChainRegion::writable(16)], 8),
181 Err(ChainLayoutError::ZeroLength)
182 );
183 assert_eq!(
184 validate_chain_layout(
185 &[
186 ChainRegion::readable(u64::MAX),
187 ChainRegion::readable(1),
188 ChainRegion::writable(16),
189 ],
190 8,
191 ),
192 Err(ChainLayoutError::LengthOverflow)
193 );
194
195 let layout =
196 validate_chain_layout(&[ChainRegion::readable(16), ChainRegion::writable(16)], 8)
197 .unwrap();
198 assert_eq!(
199 layout.validate_port_lengths(15, 16),
200 Err(ChainLayoutError::PortLengthMismatch)
201 );
202 }
203}