hyperlight_common/virtq/
desc.rs1use bitflags::bitflags;
24use bytemuck::{Pod, Zeroable};
25
26use super::MemOps;
27
28bitflags! {
29 #[repr(transparent)]
34 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
35 pub struct DescFlags: u16 {
36 const NEXT = 1 << 0;
38 const WRITE = 1 << 1;
40 const INDIRECT = 1 << 2;
42 const AVAIL = 1 << 7;
44 const USED = 1 << 15;
46 }
47}
48
49impl DescFlags {
50 #[inline]
53 pub fn is_avail(self, wrap: bool) -> bool {
54 let avail = self.contains(DescFlags::AVAIL);
55 let used = self.contains(DescFlags::USED);
56 avail == wrap && used != wrap
57 }
58
59 #[inline]
62 pub fn is_used(self, wrap: bool) -> bool {
63 let avail = self.contains(DescFlags::AVAIL);
64 let used = self.contains(DescFlags::USED);
65 avail == wrap && used == wrap
66 }
67}
68
69#[repr(C)]
70#[derive(Clone, Copy, Debug, Pod, Zeroable, PartialEq, Eq, Hash)]
71pub struct Descriptor {
72 pub addr: u64,
74 pub len: u32,
77 pub id: u16,
80 pub flags: u16,
82}
83
84const _: () = assert!(core::mem::size_of::<Descriptor>() == 16);
85const _: () = assert!(Descriptor::ALIGN == 16);
86const _: () = assert!(Descriptor::ADDR_OFFSET == 0);
87const _: () = assert!(Descriptor::LEN_OFFSET == 8);
88const _: () = assert!(Descriptor::ID_OFFSET == 12);
89const _: () = assert!(Descriptor::FLAGS_OFFSET == 14);
90
91impl Descriptor {
92 pub const ALIGN: usize = 16;
94 pub const SIZE: usize = core::mem::size_of::<Self>();
95
96 pub const ADDR_OFFSET: usize = core::mem::offset_of!(Self, addr);
97 pub const LEN_OFFSET: usize = core::mem::offset_of!(Self, len);
98 pub const ID_OFFSET: usize = core::mem::offset_of!(Self, id);
99 pub const FLAGS_OFFSET: usize = core::mem::offset_of!(Self, flags);
100
101 pub fn new(addr: u64, len: u32, id: u16, flags: DescFlags) -> Self {
102 Self {
103 addr,
104 len,
105 id,
106 flags: flags.bits(),
107 }
108 }
109
110 #[inline]
112 pub fn flags(&self) -> DescFlags {
113 DescFlags::from_bits_truncate(self.flags)
114 }
115
116 #[inline]
118 pub fn is_avail(&self, wrap: bool) -> bool {
119 self.flags().is_avail(wrap)
120 }
121
122 #[inline]
124 pub fn is_used(&self, wrap: bool) -> bool {
125 self.flags().is_used(wrap)
126 }
127
128 #[inline]
130 pub fn is_writable(&self) -> bool {
131 self.flags().contains(DescFlags::WRITE)
132 }
133
134 #[inline]
136 pub fn is_next(&self) -> bool {
137 self.flags().contains(DescFlags::NEXT)
138 }
139
140 #[inline]
145 pub fn mark_avail(&mut self, wrap: bool) {
146 if wrap {
147 self.flags |= DescFlags::AVAIL.bits();
148 self.flags &= !DescFlags::USED.bits();
149 } else {
150 self.flags &= !DescFlags::AVAIL.bits();
151 self.flags |= DescFlags::USED.bits();
152 }
153 }
154
155 #[inline]
158 pub fn mark_used(&mut self, wrap: bool) {
159 if wrap {
160 self.flags |= DescFlags::USED.bits();
161 self.flags |= DescFlags::AVAIL.bits();
162 } else {
163 self.flags &= !DescFlags::USED.bits();
164 self.flags &= !DescFlags::AVAIL.bits();
165 }
166 }
167
168 pub fn write_release<M: MemOps>(&self, mem: &M, addr: u64) -> Result<(), M::Error> {
176 mem.write_val(addr + Self::ADDR_OFFSET as u64, self.addr)?;
177 mem.write_val(addr + Self::LEN_OFFSET as u64, self.len)?;
178 mem.write_val(addr + Self::ID_OFFSET as u64, self.id)?;
179 mem.store_release(addr + Self::FLAGS_OFFSET as u64, self.flags)?;
181 Ok(())
182 }
183
184 pub fn read_flags_acquire<M: MemOps>(mem: &M, addr: u64) -> Result<DescFlags, M::Error> {
187 let flags = mem.load_acquire(addr + Self::FLAGS_OFFSET as u64)?;
188 Ok(DescFlags::from_bits_truncate(flags))
189 }
190
191 pub fn read_body<M: MemOps>(mem: &M, addr: u64, flags: DescFlags) -> Result<Self, M::Error> {
194 let addr_val: u64 = mem.read_val(addr + Self::ADDR_OFFSET as u64)?;
195 let len: u32 = mem.read_val(addr + Self::LEN_OFFSET as u64)?;
196 let id: u16 = mem.read_val(addr + Self::ID_OFFSET as u64)?;
197
198 Ok(Self {
199 addr: addr_val,
200 len,
201 id,
202 flags: flags.bits(),
203 })
204 }
205}
206
207#[derive(Debug, Clone, Copy)]
209pub struct DescTable {
210 base_addr: u64,
211 len: usize,
212}
213
214impl DescTable {
215 pub const DEFAULT_LEN: usize = 256;
216
217 pub unsafe fn from_raw_parts(base_addr: u64, len: usize) -> Self {
226 debug_assert!(base_addr.is_multiple_of(Descriptor::ALIGN as u64));
227 debug_assert!(len <= u16::MAX as usize);
228
229 Self { base_addr, len }
230 }
231
232 pub fn desc_addr(&self, idx: u16) -> Option<u64> {
234 if idx >= self.len as u16 {
235 return None;
236 }
237
238 Some(self.base_addr + (idx as u64 * Descriptor::SIZE as u64))
239 }
240
241 pub fn len(&self) -> usize {
243 self.len
244 }
245
246 pub fn is_empty(&self) -> bool {
248 self.len == 0
249 }
250
251 pub const fn default_len() -> usize {
252 Self::DEFAULT_LEN
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn mark_avail_sets_bits_correctly_wrap_true() {
262 let mut d = Descriptor::zeroed();
263 d.flags = DescFlags::WRITE.bits() | DescFlags::NEXT.bits();
264 d.mark_avail(true);
265 let f = d.flags();
266 assert!(f.contains(DescFlags::AVAIL));
267 assert!(!f.contains(DescFlags::USED));
268 assert!(f.contains(DescFlags::WRITE));
269 assert!(f.contains(DescFlags::NEXT));
270 }
271
272 #[test]
273 fn mark_avail_sets_bits_correctly_wrap_false() {
274 let mut d = Descriptor::zeroed();
275 d.mark_avail(false);
276 let f = d.flags();
277 assert!(!f.contains(DescFlags::AVAIL));
278 assert!(f.contains(DescFlags::USED));
279 }
280
281 #[test]
282 fn mark_used_sets_both_bits_match_wrap_true() {
283 let mut d = Descriptor::zeroed();
284 d.mark_used(true);
285 let f = d.flags();
286 assert!(f.contains(DescFlags::AVAIL));
287 assert!(f.contains(DescFlags::USED));
288 }
289
290 #[test]
291 fn mark_used_sets_both_bits_match_wrap_false() {
292 let mut d = Descriptor::zeroed();
293 d.mark_used(false);
294 let f = d.flags();
295 assert!(!f.contains(DescFlags::AVAIL));
296 assert!(!f.contains(DescFlags::USED));
297 }
298
299 #[test]
300 fn is_avail_and_is_used() {
301 let mut d = Descriptor::zeroed();
302 d.mark_avail(true);
303 assert!(d.is_avail(true));
304 assert!(!d.is_used(true));
305 d.mark_used(true);
306 assert!(d.is_used(true));
307 assert!(!d.is_avail(true));
308 d.mark_avail(false);
309 assert!(d.is_avail(false));
310 assert!(!d.is_used(false));
311 d.mark_used(false);
312 assert!(d.is_used(false));
313 assert!(!d.is_avail(false));
314 }
315
316 #[test]
317 fn writable_and_next_helpers() {
318 let mut d = Descriptor::zeroed();
319 d.flags = (DescFlags::WRITE | DescFlags::NEXT).bits();
320 assert!(d.is_writable());
321 assert!(d.is_next());
322 d.flags = 0;
323 assert!(!d.is_writable());
324 assert!(!d.is_next());
325 }
326
327 #[test]
328 fn avail_then_used_wrap_flip_sequence() {
329 let mut d = Descriptor::zeroed();
330 d.mark_avail(true);
331 assert!(d.is_avail(true));
332 d.mark_used(false);
333 assert!(d.is_used(false));
334 assert!(!d.is_avail(false));
335 d.mark_avail(true);
336 assert!(d.is_avail(true));
337 }
338
339 #[test]
340 fn desc_table_get_out_of_bounds() {
341 let mut buf = vec![0u8; 4 * Descriptor::SIZE + Descriptor::ALIGN];
344 let base = buf.as_mut_ptr() as usize;
345 let aligned = (base + Descriptor::ALIGN - 1) & !(Descriptor::ALIGN - 1);
346 let table = unsafe { DescTable::from_raw_parts(aligned as u64, 4) };
347 assert!(table.desc_addr(3).is_some());
348 assert!(table.desc_addr(4).is_none());
349 }
350}