hyperlight_common/virtq/
desc.rs1use bitflags::bitflags;
11use bytemuck::{Pod, Zeroable};
12
13use super::MemOps;
14
15bitflags! {
16 #[repr(transparent)]
21 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22 pub struct DescFlags: u16 {
23 const NEXT = 1 << 0;
25 const WRITE = 1 << 1;
27 const INDIRECT = 1 << 2;
29 const AVAIL = 1 << 7;
31 const USED = 1 << 15;
33 }
34}
35
36impl DescFlags {
37 #[inline]
40 pub fn is_avail(self, wrap: bool) -> bool {
41 let avail = self.contains(DescFlags::AVAIL);
42 let used = self.contains(DescFlags::USED);
43 avail == wrap && used != wrap
44 }
45
46 #[inline]
49 pub fn is_used(self, wrap: bool) -> bool {
50 let avail = self.contains(DescFlags::AVAIL);
51 let used = self.contains(DescFlags::USED);
52 avail == wrap && used == wrap
53 }
54}
55
56#[repr(C)]
57#[derive(Clone, Copy, Debug, Pod, Zeroable, PartialEq, Eq, Hash)]
58pub struct Descriptor {
59 pub addr: u64,
61 pub len: u32,
64 pub id: u16,
67 pub flags: u16,
69}
70
71const _: () = assert!(core::mem::size_of::<Descriptor>() == 16);
72const _: () = assert!(Descriptor::ALIGN == 16);
73const _: () = assert!(Descriptor::ADDR_OFFSET == 0);
74const _: () = assert!(Descriptor::LEN_OFFSET == 8);
75const _: () = assert!(Descriptor::ID_OFFSET == 12);
76const _: () = assert!(Descriptor::FLAGS_OFFSET == 14);
77
78impl Descriptor {
79 pub const ALIGN: usize = 16;
81 pub const SIZE: usize = core::mem::size_of::<Self>();
82
83 pub const ADDR_OFFSET: usize = core::mem::offset_of!(Self, addr);
84 pub const LEN_OFFSET: usize = core::mem::offset_of!(Self, len);
85 pub const ID_OFFSET: usize = core::mem::offset_of!(Self, id);
86 pub const FLAGS_OFFSET: usize = core::mem::offset_of!(Self, flags);
87
88 pub fn new(addr: u64, len: u32, id: u16, flags: DescFlags) -> Self {
89 Self {
90 addr,
91 len,
92 id,
93 flags: flags.bits(),
94 }
95 }
96
97 #[inline]
99 pub fn flags(&self) -> DescFlags {
100 DescFlags::from_bits_truncate(self.flags)
101 }
102
103 #[inline]
105 pub fn is_avail(&self, wrap: bool) -> bool {
106 self.flags().is_avail(wrap)
107 }
108
109 #[inline]
111 pub fn is_used(&self, wrap: bool) -> bool {
112 self.flags().is_used(wrap)
113 }
114
115 #[inline]
117 pub fn is_writable(&self) -> bool {
118 self.flags().contains(DescFlags::WRITE)
119 }
120
121 #[inline]
123 pub fn is_next(&self) -> bool {
124 self.flags().contains(DescFlags::NEXT)
125 }
126
127 #[inline]
132 pub fn mark_avail(&mut self, wrap: bool) {
133 if wrap {
134 self.flags |= DescFlags::AVAIL.bits();
135 self.flags &= !DescFlags::USED.bits();
136 } else {
137 self.flags &= !DescFlags::AVAIL.bits();
138 self.flags |= DescFlags::USED.bits();
139 }
140 }
141
142 #[inline]
145 pub fn mark_used(&mut self, wrap: bool) {
146 if wrap {
147 self.flags |= DescFlags::USED.bits();
148 self.flags |= DescFlags::AVAIL.bits();
149 } else {
150 self.flags &= !DescFlags::USED.bits();
151 self.flags &= !DescFlags::AVAIL.bits();
152 }
153 }
154
155 pub fn write_release<M: MemOps>(&self, mem: &M, addr: u64) -> Result<(), M::Error> {
163 mem.write_val(addr + Self::ADDR_OFFSET as u64, self.addr)?;
164 mem.write_val(addr + Self::LEN_OFFSET as u64, self.len)?;
165 mem.write_val(addr + Self::ID_OFFSET as u64, self.id)?;
166 mem.store_release(addr + Self::FLAGS_OFFSET as u64, self.flags)?;
168 Ok(())
169 }
170
171 pub fn read_flags_acquire<M: MemOps>(mem: &M, addr: u64) -> Result<DescFlags, M::Error> {
174 let flags = mem.load_acquire(addr + Self::FLAGS_OFFSET as u64)?;
175 Ok(DescFlags::from_bits_truncate(flags))
176 }
177
178 pub fn read_body<M: MemOps>(mem: &M, addr: u64, flags: DescFlags) -> Result<Self, M::Error> {
181 let addr_val: u64 = mem.read_val(addr + Self::ADDR_OFFSET as u64)?;
182 let len: u32 = mem.read_val(addr + Self::LEN_OFFSET as u64)?;
183 let id: u16 = mem.read_val(addr + Self::ID_OFFSET as u64)?;
184
185 Ok(Self {
186 addr: addr_val,
187 len,
188 id,
189 flags: flags.bits(),
190 })
191 }
192}
193
194#[derive(Debug, Clone, Copy)]
196pub struct DescTable {
197 base_addr: u64,
198 len: usize,
199}
200
201impl DescTable {
202 pub const DEFAULT_LEN: usize = 256;
203
204 pub unsafe fn from_raw_parts(base_addr: u64, len: usize) -> Self {
213 debug_assert!(base_addr.is_multiple_of(Descriptor::ALIGN as u64));
214 debug_assert!(len <= u16::MAX as usize);
215
216 Self { base_addr, len }
217 }
218
219 pub fn desc_addr(&self, idx: u16) -> Option<u64> {
221 if idx >= self.len as u16 {
222 return None;
223 }
224
225 Some(self.base_addr + (idx as u64 * Descriptor::SIZE as u64))
226 }
227
228 pub fn len(&self) -> usize {
230 self.len
231 }
232
233 pub fn is_empty(&self) -> bool {
235 self.len == 0
236 }
237
238 pub const fn default_len() -> usize {
239 Self::DEFAULT_LEN
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn mark_avail_sets_bits_correctly_wrap_true() {
249 let mut d = Descriptor::zeroed();
250 d.flags = DescFlags::WRITE.bits() | DescFlags::NEXT.bits();
251 d.mark_avail(true);
252 let f = d.flags();
253 assert!(f.contains(DescFlags::AVAIL));
254 assert!(!f.contains(DescFlags::USED));
255 assert!(f.contains(DescFlags::WRITE));
256 assert!(f.contains(DescFlags::NEXT));
257 }
258
259 #[test]
260 fn mark_avail_sets_bits_correctly_wrap_false() {
261 let mut d = Descriptor::zeroed();
262 d.mark_avail(false);
263 let f = d.flags();
264 assert!(!f.contains(DescFlags::AVAIL));
265 assert!(f.contains(DescFlags::USED));
266 }
267
268 #[test]
269 fn mark_used_sets_both_bits_match_wrap_true() {
270 let mut d = Descriptor::zeroed();
271 d.mark_used(true);
272 let f = d.flags();
273 assert!(f.contains(DescFlags::AVAIL));
274 assert!(f.contains(DescFlags::USED));
275 }
276
277 #[test]
278 fn mark_used_sets_both_bits_match_wrap_false() {
279 let mut d = Descriptor::zeroed();
280 d.mark_used(false);
281 let f = d.flags();
282 assert!(!f.contains(DescFlags::AVAIL));
283 assert!(!f.contains(DescFlags::USED));
284 }
285
286 #[test]
287 fn is_avail_and_is_used() {
288 let mut d = Descriptor::zeroed();
289 d.mark_avail(true);
290 assert!(d.is_avail(true));
291 assert!(!d.is_used(true));
292 d.mark_used(true);
293 assert!(d.is_used(true));
294 assert!(!d.is_avail(true));
295 d.mark_avail(false);
296 assert!(d.is_avail(false));
297 assert!(!d.is_used(false));
298 d.mark_used(false);
299 assert!(d.is_used(false));
300 assert!(!d.is_avail(false));
301 }
302
303 #[test]
304 fn writable_and_next_helpers() {
305 let mut d = Descriptor::zeroed();
306 d.flags = (DescFlags::WRITE | DescFlags::NEXT).bits();
307 assert!(d.is_writable());
308 assert!(d.is_next());
309 d.flags = 0;
310 assert!(!d.is_writable());
311 assert!(!d.is_next());
312 }
313
314 #[test]
315 fn avail_then_used_wrap_flip_sequence() {
316 let mut d = Descriptor::zeroed();
317 d.mark_avail(true);
318 assert!(d.is_avail(true));
319 d.mark_used(false);
320 assert!(d.is_used(false));
321 assert!(!d.is_avail(false));
322 d.mark_avail(true);
323 assert!(d.is_avail(true));
324 }
325
326 #[test]
327 fn desc_table_get_out_of_bounds() {
328 let mut buf = vec![0u8; 4 * Descriptor::SIZE + Descriptor::ALIGN];
331 let base = buf.as_mut_ptr() as usize;
332 let aligned = (base + Descriptor::ALIGN - 1) & !(Descriptor::ALIGN - 1);
333 let table = unsafe { DescTable::from_raw_parts(aligned as u64, 4) };
334 assert!(table.desc_addr(3).is_some());
335 assert!(table.desc_addr(4).is_none());
336 }
337}