Skip to main content

hyperlight_common/virtq/
event.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 The Hyperlight Authors.
3
4//! Event Suppression for Virtqueue Notifications
5//!
6//! This module implements the event suppression mechanism from VIRTIO 1.1+
7//! that allows fine-grained control over when notifications are sent between
8//! driver and device.
9
10use bitflags::bitflags;
11use bytemuck::{Pod, Zeroable};
12
13use super::MemOps;
14
15bitflags! {
16    #[repr(transparent)]
17    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
18    pub struct EventFlags: u16 {
19        /// Enable notifications (always notify).
20        const ENABLE = 0x0;
21        /// Disable notifications (never notify).
22        const DISABLE = 0x1;
23        /// Notify only at specific descriptor (EVENT_IDX mode).
24        const DESC = 0x2;
25    }
26}
27
28/// Event suppression structure for controlling notifications.
29#[repr(C)]
30#[derive(Clone, Copy, Debug, Pod, Zeroable, PartialEq, Eq, Hash)]
31pub struct EventSuppression {
32    /// bits 0-14: offset, bit 15: wrap
33    off_wrap: u16,
34    /// bits 0-1: flags, bits 2-15: reserved
35    flags: u16,
36}
37
38const _: () = assert!(core::mem::size_of::<EventSuppression>() == 4);
39const _: () = assert!(EventSuppression::WRAP_OFFSET == 0);
40const _: () = assert!(EventSuppression::FLAGS_OFFSET == 2);
41
42impl EventSuppression {
43    const FLAGS_MASK: u16 = 0x3;
44    const DESC_EVENT_OFF_MASK: u16 = 0x7FFF;
45    const DESC_EVENT_WRAP: u16 = 0x8000;
46
47    pub const SIZE: usize = core::mem::size_of::<Self>();
48    pub const ALIGN: usize = core::mem::align_of::<Self>();
49    pub const WRAP_OFFSET: usize = core::mem::offset_of!(Self, off_wrap);
50    pub const FLAGS_OFFSET: usize = core::mem::offset_of!(Self, flags);
51
52    /// Create a new event suppression with the given offset/wrap and flags.
53    pub fn new(off_wrap: u16, flags: EventFlags) -> Self {
54        Self {
55            off_wrap,
56            flags: flags.bits(),
57        }
58    }
59
60    /// Get the event flags.
61    pub fn flags(&self) -> EventFlags {
62        EventFlags::from_bits_truncate(self.flags & Self::FLAGS_MASK)
63    }
64
65    /// Set the event flags.
66    pub fn set_flags(&mut self, flags: EventFlags) {
67        self.flags = (self.flags & !Self::FLAGS_MASK) | (flags.bits() & Self::FLAGS_MASK);
68    }
69
70    /// Get the descriptor event offset (bits 0-14).
71    pub fn desc_event_off(&self) -> u16 {
72        self.off_wrap & Self::DESC_EVENT_OFF_MASK
73    }
74
75    /// Check if the descriptor event wrap bit (bit 15) is set.
76    pub fn desc_event_wrap(&self) -> bool {
77        (self.off_wrap & Self::DESC_EVENT_WRAP) != 0
78    }
79
80    /// Set the descriptor event offset and wrap bit.
81    pub fn set_desc_event(&mut self, off: u16, wrap: bool) {
82        self.off_wrap =
83            (off & Self::DESC_EVENT_OFF_MASK) | if wrap { Self::DESC_EVENT_WRAP } else { 0 };
84    }
85
86    /// Acquire-load only the flags word - the publish point - without reading
87    /// the `off_wrap` payload.
88    ///
89    /// # Invariant
90    ///
91    /// The caller must ensure that `addr` is a valid pointer to an EventSuppression.
92    pub fn read_flags_acquire<M: MemOps>(mem: &M, addr: u64) -> Result<EventFlags, M::Error> {
93        // Atomic Acquire load of flags (publish point)
94        let flags = mem.load_acquire(addr + Self::FLAGS_OFFSET as u64)?;
95        Ok(EventFlags::from_bits_truncate(flags))
96    }
97
98    /// Read the `off_wrap` payload and combine it with flags already obtained
99    /// from [`read_flags_acquire`](Self::read_flags_acquire).
100    ///
101    /// # Invariant
102    ///
103    /// The caller must ensure that `addr` is a valid pointer to an EventSuppression.
104    pub fn read_body<M: MemOps>(mem: &M, addr: u64, flags: EventFlags) -> Result<Self, M::Error> {
105        let off_wrap: u16 = mem.read_val(addr + Self::WRAP_OFFSET as u64)?;
106
107        Ok(Self {
108            off_wrap,
109            flags: flags.bits(),
110        })
111    }
112
113    /// Write an `EventSuppression` to a raw pointer with release semantics.
114    ///
115    /// # Invariant
116    ///
117    /// The caller must ensure that `base` is a valid pointer to an EventSuppression.
118    pub fn write_release<M: MemOps>(&self, mem: &M, addr: u64) -> Result<(), M::Error> {
119        mem.write_val(addr + Self::WRAP_OFFSET as u64, self.off_wrap)?;
120        // Atomic Release store of flags (publish point)
121        mem.store_release(addr + Self::FLAGS_OFFSET as u64, self.flags)?;
122        Ok(())
123    }
124}