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