Skip to main content

hyperlight_common/virtq/
access.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 The Hyperlight Authors.
3
4//! Memory Access Traits for Virtqueue Operations
5//!
6//! This module defines the [`MemOps`] trait that abstracts memory access patterns
7//! required by the virtqueue implementation. This allows the virtqueue code to
8//! work with different memory backends e.g. Host vs Guest.
9
10use alloc::sync::Arc;
11
12use bytemuck::Pod;
13
14/// Backend-provided memory access for virtqueue.
15///
16/// # Safety
17///
18/// Implementations must ensure that:
19/// - Addresses accepted by these methods are translated according to the
20///   backend's memory model.
21/// - Invalid or inaccessible addresses are reported with `Self::Error` rather
22///   than causing undefined behavior.
23/// - Memory ordering guarantees are upheld as documented.
24/// - Typed reads/writes and atomic operations honor alignment and initialized
25///   memory requirements for the translated addresses.
26///
27/// [`RingProducer`]: super::RingProducer
28/// [`RingConsumer`]: super::RingConsumer
29pub unsafe trait MemOps {
30    type Error;
31
32    /// Read bytes from physical memory.
33    ///
34    /// Used for reading buffer contents pointed to by descriptors.
35    ///
36    /// # Arguments
37    ///
38    /// * `addr` - Guest physical address to read from
39    /// * `dst` - Destination buffer to fill
40    ///
41    /// Implementations must return an error if `addr` cannot be read for
42    /// at least `dst.len()` bytes.
43    fn read(&self, addr: u64, dst: &mut [u8]) -> Result<(), Self::Error>;
44
45    /// Write bytes to physical memory.
46    ///
47    /// # Arguments
48    ///
49    /// * `addr` - address to write to
50    /// * `src` - Source data to write
51    ///
52    /// Implementations must return an error if `addr` cannot be written for
53    /// at least `src.len()` bytes.
54    fn write(&self, addr: u64, src: &[u8]) -> Result<(), Self::Error>;
55
56    /// Load a u16 with acquire semantics.
57    ///
58    /// Implementations must return an error if `addr` does not translate to a
59    /// valid, aligned `AtomicU16` in shared memory.
60    fn load_acquire(&self, addr: u64) -> Result<u16, Self::Error>;
61
62    /// Store a u16 with release semantics.
63    ///
64    /// Implementations must return an error if `addr` does not translate to a
65    /// valid, aligned `AtomicU16` in shared memory.
66    fn store_release(&self, addr: u64, val: u16) -> Result<(), Self::Error>;
67
68    /// Get a direct read-only slice into shared memory.
69    ///
70    /// # Safety
71    ///
72    /// The caller must ensure:
73    /// - `addr` is valid and points to at least `len` bytes.
74    /// - The memory region is not concurrently modified for the lifetime of
75    ///   the returned slice. Caller must uphold this via protocol-level
76    ///   synchronisation, e.g. descriptor ownership transfer.
77    ///
78    /// See also [`BufferOwner`]: super::BufferOwner
79    unsafe fn as_slice(&self, addr: u64, len: usize) -> Result<&[u8], Self::Error>;
80
81    /// Get a direct mutable slice into shared memory.
82    ///
83    /// # Safety
84    ///
85    /// The caller must ensure:
86    /// - `addr` is valid and points to at least `len` bytes.
87    /// - No other references (shared or mutable) to this memory region exist
88    ///   for the lifetime of the returned slice.
89    /// - Protocol-level synchronisation (e.g. descriptor ownership) guarantees
90    ///   exclusive access.
91    #[allow(clippy::mut_from_ref)]
92    unsafe fn as_mut_slice(&self, addr: u64, len: usize) -> Result<&mut [u8], Self::Error>;
93
94    /// Read a Pod type at the given pointer.
95    ///
96    /// Implementations must return an error if `addr` is not valid, aligned,
97    /// and initialized for `T`.
98    fn read_val<T: Pod>(&self, addr: u64) -> Result<T, Self::Error> {
99        let mut val = T::zeroed();
100        let bytes = bytemuck::bytes_of_mut(&mut val);
101
102        self.read(addr, bytes)?;
103        Ok(val)
104    }
105
106    /// Write a Pod type at the given pointer.
107    ///
108    /// Implementations must return an error if `addr` is not valid and aligned
109    /// for `T`.
110    fn write_val<T: Pod>(&self, addr: u64, val: T) -> Result<(), Self::Error> {
111        let bytes = bytemuck::bytes_of(&val);
112        self.write(addr, bytes)?;
113        Ok(())
114    }
115}
116
117// SAFETY: Arc delegates all memory operations to the wrapped backend, preserving
118// that backend's MemOps contract.
119unsafe impl<T: MemOps> MemOps for Arc<T> {
120    type Error = T::Error;
121
122    fn read(&self, addr: u64, dst: &mut [u8]) -> Result<(), Self::Error> {
123        (**self).read(addr, dst)
124    }
125
126    fn write(&self, addr: u64, src: &[u8]) -> Result<(), Self::Error> {
127        (**self).write(addr, src)
128    }
129
130    fn load_acquire(&self, addr: u64) -> Result<u16, Self::Error> {
131        (**self).load_acquire(addr)
132    }
133
134    fn store_release(&self, addr: u64, val: u16) -> Result<(), Self::Error> {
135        (**self).store_release(addr, val)
136    }
137
138    unsafe fn as_slice(&self, addr: u64, len: usize) -> Result<&[u8], Self::Error> {
139        unsafe { (**self).as_slice(addr, len) }
140    }
141
142    #[allow(clippy::mut_from_ref)]
143    unsafe fn as_mut_slice(&self, addr: u64, len: usize) -> Result<&mut [u8], Self::Error> {
144        unsafe { (**self).as_mut_slice(addr, len) }
145    }
146}