Skip to main content

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