hyperlight-common 0.17.0

Hyperlight's components common to host and guest.
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 The Hyperlight Authors.

//! Event Suppression for Virtqueue Notifications
//!
//! This module implements the event suppression mechanism from VIRTIO 1.1+
//! that allows fine-grained control over when notifications are sent between
//! driver and device.

use bitflags::bitflags;
use bytemuck::{Pod, Zeroable};

use super::MemOps;

bitflags! {
    #[repr(transparent)]
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    pub struct EventFlags: u16 {
        /// Enable notifications (always notify).
        const ENABLE = 0x0;
        /// Disable notifications (never notify).
        const DISABLE = 0x1;
        /// Notify only at specific descriptor (EVENT_IDX mode).
        const DESC = 0x2;
    }
}

/// Event suppression structure for controlling notifications.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable, PartialEq, Eq, Hash)]
pub struct EventSuppression {
    /// bits 0-14: offset, bit 15: wrap
    off_wrap: u16,
    /// bits 0-1: flags, bits 2-15: reserved
    flags: u16,
}

const _: () = assert!(core::mem::size_of::<EventSuppression>() == 4);
const _: () = assert!(EventSuppression::WRAP_OFFSET == 0);
const _: () = assert!(EventSuppression::FLAGS_OFFSET == 2);

impl EventSuppression {
    const FLAGS_MASK: u16 = 0x3;
    const DESC_EVENT_OFF_MASK: u16 = 0x7FFF;
    const DESC_EVENT_WRAP: u16 = 0x8000;

    pub const SIZE: usize = core::mem::size_of::<Self>();
    pub const ALIGN: usize = core::mem::align_of::<Self>();
    pub const WRAP_OFFSET: usize = core::mem::offset_of!(Self, off_wrap);
    pub const FLAGS_OFFSET: usize = core::mem::offset_of!(Self, flags);

    /// Create a new event suppression with the given offset/wrap and flags.
    pub fn new(off_wrap: u16, flags: EventFlags) -> Self {
        Self {
            off_wrap,
            flags: flags.bits(),
        }
    }

    /// Get the event flags.
    pub fn flags(&self) -> EventFlags {
        EventFlags::from_bits_truncate(self.flags & Self::FLAGS_MASK)
    }

    /// Set the event flags.
    pub fn set_flags(&mut self, flags: EventFlags) {
        self.flags = (self.flags & !Self::FLAGS_MASK) | (flags.bits() & Self::FLAGS_MASK);
    }

    /// Get the descriptor event offset (bits 0-14).
    pub fn desc_event_off(&self) -> u16 {
        self.off_wrap & Self::DESC_EVENT_OFF_MASK
    }

    /// Check if the descriptor event wrap bit (bit 15) is set.
    pub fn desc_event_wrap(&self) -> bool {
        (self.off_wrap & Self::DESC_EVENT_WRAP) != 0
    }

    /// Set the descriptor event offset and wrap bit.
    pub fn set_desc_event(&mut self, off: u16, wrap: bool) {
        self.off_wrap =
            (off & Self::DESC_EVENT_OFF_MASK) | if wrap { Self::DESC_EVENT_WRAP } else { 0 };
    }

    /// Acquire-load only the flags word - the publish point - without reading
    /// the `off_wrap` payload.
    ///
    /// # Invariant
    ///
    /// The caller must ensure that `addr` is a valid pointer to an EventSuppression.
    pub fn read_flags_acquire<M: MemOps>(mem: &M, addr: u64) -> Result<EventFlags, M::Error> {
        // Atomic Acquire load of flags (publish point)
        let flags = mem.load_acquire(addr + Self::FLAGS_OFFSET as u64)?;
        Ok(EventFlags::from_bits_truncate(flags))
    }

    /// Read the `off_wrap` payload and combine it with flags already obtained
    /// from [`read_flags_acquire`](Self::read_flags_acquire).
    ///
    /// # Invariant
    ///
    /// The caller must ensure that `addr` is a valid pointer to an EventSuppression.
    pub fn read_body<M: MemOps>(mem: &M, addr: u64, flags: EventFlags) -> Result<Self, M::Error> {
        let off_wrap: u16 = mem.read_val(addr + Self::WRAP_OFFSET as u64)?;

        Ok(Self {
            off_wrap,
            flags: flags.bits(),
        })
    }

    /// Write an `EventSuppression` to a raw pointer with release semantics.
    ///
    /// # Invariant
    ///
    /// The caller must ensure that `base` is a valid pointer to an EventSuppression.
    pub fn write_release<M: MemOps>(&self, mem: &M, addr: u64) -> Result<(), M::Error> {
        mem.write_val(addr + Self::WRAP_OFFSET as u64, self.off_wrap)?;
        // Atomic Release store of flags (publish point)
        mem.store_release(addr + Self::FLAGS_OFFSET as u64, self.flags)?;
        Ok(())
    }
}