virtio_driver/common/queue/buffer.rs
1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Models buffers used in virtqueues.
6
7// TODO(https://fxbug.dev/504722357): Remove this in favor of more granular
8// attributes when the Rust port is completed.
9#![allow(dead_code)]
10
11use std::num::NonZero;
12
13/// Describes a buffer that can be submitted to a virtio queue.
14///
15/// A buffer describes the memory used by a virtio device to read the input and
16/// write the output for a task. The description is a list of
17/// [`VirtioMemoryRange`]. Each memory range must be contiguous in the PCI
18/// device's addressable space.
19///
20/// The driver submits a buffer to ask the virtio device to perform a task.
21/// Submitting a buffer transfers the driver's ownership of the buffer to the
22/// device. The device returns the buffer when it completes the task. Returning
23/// the buffer transfers ownership back to the driver.
24///
25/// For convenience, we say that a buffer is submitted when it is owned by the
26/// device. We also say that a memory range is submitted when the buffer that
27/// it belongs to is submitted. The device may only access the memory submitted
28/// buffers, which are the buffers that it owns.
29///
30/// The list of memory regions must first contain the regions storing the task's
31/// input (using [`VirtioMemoryRangeAccess::Input`]), followed by the regions
32/// storing the task's output (using [`VirtioMemoryRangeAccess::Output`]).
33// @cite(virtio): sec="2.7" title="Split Virtqueues"
34// @cite(virtio): sec="2.7.4" title="Message Framing"
35// @cite(virtio): sec="2.7.4.2" title="Driver Requirements: Message Framing"
36// @cite(virtio): sec="2.7.5" title="The Virtqueue Descriptor Table"
37// @cite(virtio): sec="2.7.13.1" title="Placing Buffers Into The Descriptor Table"
38// @cite(virtio): sec="2.8" title="Packed Virtqueues"
39pub struct VirtioBufferRef<'a> {
40 range_list: &'a [VirtioMemoryRange],
41}
42
43impl<'a> AsRef<[VirtioMemoryRange]> for VirtioBufferRef<'a> {
44 fn as_ref(&self) -> &[VirtioMemoryRange] {
45 self.range_list
46 }
47}
48
49impl<'a> VirtioBufferRef<'a> {
50 /// The maximum number of contiguous memory regions in a buffer.
51 ///
52 /// The upper bound is based on the fact that a buffer's memory regions must
53 /// fit in a virtqueue's descriptor table, so the number of memory regions
54 /// is capped by the virtqueue descriptor table size.
55 // @cite(virtio): sec="2.7" title="Split Virtqueues"
56 // @cite(virtio): sec="2.8" title="Packed Virtqueues"
57 pub const MAX_LENGTH: usize = 32768;
58
59 /// Panics if the list of ranges is empty, or if a range with
60 /// [`VirtioMemoryRangeAccess::Input`] access follows a range with
61 /// [`VirtioMemoryRangeAccess::Output`] access.
62 pub fn new(range_list: &'a [VirtioMemoryRange]) -> VirtioBufferRef<'a> {
63 assert!(!range_list.is_empty(), "virtio buffers must have at least one memory range");
64 assert!(
65 range_list.len() <= Self::MAX_LENGTH,
66 "virtio buffer has too many memory ranges: {}",
67 range_list.len()
68 );
69
70 // Check the memory access constraint.
71 let mut last_device_access = VirtioMemoryRangeAccess::Input;
72 for range in range_list {
73 if range.device_access == last_device_access {
74 continue;
75 }
76 assert!(
77 range.device_access == VirtioMemoryRangeAccess::Output,
78 "Range with {:?} access following range with {:?} access",
79 range.device_access,
80 last_device_access,
81 );
82 last_device_access = range.device_access;
83 }
84
85 VirtioBufferRef { range_list }
86 }
87
88 /// Returns the number of memory ranges in the buffer.
89 pub fn len(&self) -> NonZero<u16> {
90 // `as` does not truncate the length because the constructor ensures the
91 // range list has at most [`Self::MAX_LENGTH`] ranges.
92 // [`Option::unwrap()`] will not panic because the constructor ensures
93 // that the range list is not empty.
94 NonZero::<u16>::new(self.range_list.len() as u16).unwrap()
95 }
96}
97
98/// Identifies a buffer submitted to a virtqueue.
99///
100/// The identifier uniquely identifies a buffer from the moment it is submitted
101/// to the device via the queue until the moment it is returned to the driver
102/// via the queue. Once the buffer is returned to the driver, a future queue
103/// submission may reuse the identifier. The identifier is unique within one
104/// virtqueue.
105///
106/// It follows that higher-level code may use the buffer ID (potentially
107/// qualified by the virtqueue index) to track other buffer-related resources,
108/// as long as the tracking stops right after a buffer is returned.
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
110pub struct VirtioSubmittedBufferId(pub u16);
111
112/// Describes a buffer returned by a virtqueue.
113pub struct VirtioReturnedBufferInfo {
114 /// Indicates which previously submitted buffer was returned.
115 ///
116 /// [`VirtioSubmittedBufferId`] documents the validity window.
117 pub submitted_buffer_id: VirtioSubmittedBufferId,
118
119 /// The number of bytes written by the device to the buffer.
120 ///
121 /// The device writes to the buffer regions that allow it by having their
122 /// [`VirtioMemoryRangeFlags::written_by_hardware`] set to true. The device
123 /// uses the writable regions in the order in which they appear in the
124 /// submitted buffer region list.
125 // @cite(virtio): sec="2.7.8.2" title="Device Requirements: The Virtqueue Used Ring"
126 pub written_bytes: u32,
127}
128
129/// Describes a memory region accessible by a virtio device.
130///
131/// The memory region is contiguous in the PCI device's physical address space.
132/// If no IOMMU is involved, the PCI device's physical address space is the same
133/// as the CPU physical address space visible to the guest (virtualized) OS.
134///
135/// Each instance describes a memory region that is submitted (owned by the
136/// device) or is about to be submitted (owned by the device). Device ownership
137/// is conceptually equivalent to a Rust mutable reference covering the entire
138/// memory range. Here are two immediate implications:
139///
140/// 1. No Rust references may point into a range described by an instance.
141/// 2. The memory ranges described by any two instances must not overlap.
142// @cite(virtio): sec="2.7.5" title="The Virtqueue Descriptor Table"
143// @cite(virtio): sec="2.7.13.1" title="Placing Buffers Into The Descriptor Table"
144#[derive(Debug)]
145pub struct VirtioMemoryRange {
146 start_physical_address: u64,
147 size: NonZero<u32>,
148 device_access: VirtioMemoryRangeAccess,
149}
150
151impl VirtioMemoryRange {
152 /// Describes a memory range that will be submitted to a virtio device.
153 ///
154 /// # Safety
155 ///
156 /// `start_physical_address` and `size` must identify a valid range
157 /// of memory that is contiguous in the PCI virtio device's physical address
158 /// space. The newly created instance must be treated like a mutable Rust
159 /// reference covering the entire memory range.
160 pub unsafe fn new(
161 start_physical_address: u64,
162 size: NonZero<u32>,
163 device_access: VirtioMemoryRangeAccess,
164 ) -> VirtioMemoryRange {
165 VirtioMemoryRange { start_physical_address, size, device_access }
166 }
167
168 /// Points to the first byte in the memory region.
169 ///
170 /// Uses the PCI device's physical addressable space.
171 pub fn start_physical_address(&self) -> u64 {
172 self.start_physical_address
173 }
174
175 /// The number of bytes that make up the contiguous region.
176 pub fn size(&self) -> NonZero<u32> {
177 self.size
178 }
179
180 /// Constrains a device's accesses to the memory range.
181 pub fn device_access(&self) -> VirtioMemoryRangeAccess {
182 self.device_access
183 }
184}
185
186/// Describes the way a device may access a [`VirtioMemoryRange`].
187///
188/// The virtio device may only access a range's memory while the range belongs
189/// to a buffer that is submitted (owned by the device).
190// @cite(virtio): sec="2.7.5" title="The Virtqueue Descriptor Table"
191// @alias(virtio): theirs="VIRTQ_DESC_F_WRITE"
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193pub enum VirtioMemoryRangeAccess {
194 /// The device may read from the memory range while owning its buffer.
195 ///
196 /// This access mode implies that the memory range stores input parameters
197 /// for the task assigned to the device.
198 ///
199 /// The driver is expected to initialize (write) all the range's memory
200 /// before submitting the buffer to the device.
201 ///
202 /// The Rust memory model dictates that the driver may hold immutable
203 /// references to the buffer's memory while the buffer is submitted.
204 /// However, for simplicity, we use the stronger constraint in [`Output`]
205 /// for all ranges owned by the device.
206 // @cite(virtio): sec="2.7.5" title="The Virtqueue Descriptor Table"
207 // @alias(virtio): theirs="device-readable"
208 Input,
209
210 /// The device may write to the memory range while owning its buffer.
211 ///
212 /// This access mode implies that the memory range stores the output (also
213 /// called "results" and "returned values") of the task assigned to the
214 /// device.
215 ///
216 /// The driver is expected to read the output from the range after the
217 /// device returns it. The Rust memory model dictates that the driver must
218 /// not have any reference to the range's memory while the range's buffer is
219 /// owned by the device.
220 // @cite(virtio): sec="2.7.5" title="The Virtqueue Descriptor Table"
221 // @alias(virtio): theirs="device-writable"
222 Output,
223}