axvirtio_common/queue/
mod.rs1mod available;
2mod descriptor;
3mod used;
4
5pub use available::{AvailableRing, VirtQueueAvail};
6pub use descriptor::{DescriptorTable, VirtQueueDesc};
7use log::trace;
8pub use used::{UsedRing, VirtQueueUsed, VirtqUsedElem};
9
10use crate::{
11 VirtioDeviceID,
12 error::{VirtioError, VirtioResult},
13};
14use alloc::{sync::Arc, vec::Vec};
15use axaddrspace::{GuestMemoryAccessor, GuestPhysAddr};
16
17#[derive(Debug, Clone)]
19pub struct VirtioQueue<T: GuestMemoryAccessor + Clone> {
20 pub index: u16,
22 pub size: u16,
24 pub desc_table: Option<DescriptorTable<T>>,
26 avail_ring: Option<AvailableRing<T>>,
28 used_ring: Option<UsedRing<T>>,
30 accessor: Arc<T>,
32 pub max_size: u16,
34 pub ready: bool,
36 pub desc_table_addr: GuestPhysAddr,
38 pub avail_ring_addr: GuestPhysAddr,
40 pub used_ring_addr: GuestPhysAddr,
42 next_avail: u16,
44 next_used: u16,
46 pub event_idx_enabled: bool,
48}
49
50impl<T: GuestMemoryAccessor + Clone> VirtioQueue<T> {
51 pub fn new(index: u16, size: u16, accessor: Arc<T>) -> Self {
53 Self {
54 index,
55 size,
56 desc_table: None,
57 avail_ring: None,
58 used_ring: None,
59 accessor,
60 max_size: size,
61 ready: false,
62 desc_table_addr: GuestPhysAddr::from(0),
63 avail_ring_addr: GuestPhysAddr::from(0),
64 used_ring_addr: GuestPhysAddr::from(0),
65 next_avail: 0,
66 next_used: 0,
67 event_idx_enabled: false,
68 }
69 }
70
71 pub fn set_size(&mut self, size: u16) -> VirtioResult<()> {
73 if size == 0 || size > self.max_size || (size & (size - 1)) != 0 {
74 return Err(VirtioError::InvalidQueue);
75 }
76 self.size = size;
77 Ok(())
78 }
79
80 pub fn set_desc_table_addr(&mut self, addr: GuestPhysAddr) -> VirtioResult<()> {
82 if self.desc_table_addr.as_usize() != 0 {
83 return Err(VirtioError::InvalidConfig);
84 }
85 self.desc_table_addr = addr;
86 if addr.as_usize() != 0 {
87 self.desc_table = Some(DescriptorTable::new(addr, self.size, self.accessor.clone()));
88 }
89 Ok(())
90 }
91
92 pub fn set_avail_ring_addr(&mut self, addr: GuestPhysAddr) -> VirtioResult<()> {
94 if self.avail_ring_addr.as_usize() != 0 {
95 return Err(VirtioError::InvalidConfig);
96 }
97 self.avail_ring_addr = addr;
98 if addr.as_usize() != 0 {
99 self.avail_ring = Some(AvailableRing::new(addr, self.size, self.accessor.clone()));
100 }
101 Ok(())
102 }
103
104 pub fn set_used_ring_addr(&mut self, addr: GuestPhysAddr) -> VirtioResult<()> {
106 if self.used_ring_addr.as_usize() != 0 {
107 return Err(VirtioError::InvalidConfig);
108 }
109 self.used_ring_addr = addr;
110 if addr.as_usize() != 0 {
111 self.used_ring = Some(UsedRing::new(addr, self.size, self.accessor.clone()));
112 }
113 Ok(())
114 }
115
116 pub fn set_ready(&mut self, ready: bool) {
118 self.ready = ready;
119 }
120
121 pub fn is_valid(&self) -> bool {
123 self.ready
124 && self.desc_table_addr.as_usize() != 0
125 && self.avail_ring_addr.as_usize() != 0
126 && self.used_ring_addr.as_usize() != 0
127 }
128
129 pub fn reset(&mut self) {
131 self.ready = false;
132 self.desc_table_addr = GuestPhysAddr::from(0);
133 self.avail_ring_addr = GuestPhysAddr::from(0);
134 self.used_ring_addr = GuestPhysAddr::from(0);
135 self.next_avail = 0;
136 self.next_used = 0;
137 self.desc_table = None;
138 self.avail_ring = None;
139 self.used_ring = None;
140 }
141
142 pub fn read_avail_idx(&self) -> VirtioResult<u16> {
144 if let Some(ref avail_ring) = self.avail_ring {
145 avail_ring.get_avail_idx()
146 } else {
147 Err(VirtioError::QueueNotReady)
148 }
149 }
150
151 pub fn add_used(&mut self, desc_index: u16, len: u32) -> VirtioResult<()> {
153 if !self.is_valid() {
154 return Err(VirtioError::QueueNotReady);
155 }
156
157 if let Some(ref mut used_ring) = self.used_ring {
159 used_ring.add_used(desc_index as u32, len)?;
160 self.next_used = used_ring.get_used_idx();
161 } else {
162 self.next_used = (self.next_used + 1) % self.size;
164 }
165
166 Ok(())
167 }
168
169 pub fn pop_avail(&mut self) -> VirtioResult<Option<u16>> {
171 if !self.is_valid() {
172 return Err(VirtioError::QueueNotReady);
173 }
174
175 Ok(None)
178 }
179
180 pub fn get_used_ring(&self) -> Option<&UsedRing<T>> {
182 self.used_ring.as_ref()
183 }
184
185 pub fn get_used_ring_mut(&mut self) -> Option<&mut UsedRing<T>> {
187 self.used_ring.as_mut()
188 }
189
190 pub fn get_avail_ring(&self) -> Option<&AvailableRing<T>> {
192 self.avail_ring.as_ref()
193 }
194
195 pub fn get_desc_table(&self) -> Option<&DescriptorTable<T>> {
197 self.desc_table.as_ref()
198 }
199
200 pub fn read_avail_entry(&self, ring_index: u16) -> VirtioResult<u16> {
202 if let Some(ref avail_ring) = self.avail_ring {
203 avail_ring.read_avail_ring_entry(ring_index)
204 } else {
205 Err(VirtioError::QueueNotReady)
206 }
207 }
208
209 pub fn update_last_avail_idx(&mut self, idx: u16) {
211 if let Some(ref mut avail_ring) = self.avail_ring {
212 avail_ring.update_last_avail_idx(idx);
213 } else {
214 self.next_avail = idx % self.size;
215 }
216 }
217
218 pub fn get_last_avail_idx(&self) -> u16 {
220 if let Some(avail_ring) = &self.avail_ring {
221 avail_ring.last_avail_idx
222 } else {
223 self.next_avail
224 }
225 }
226
227 pub fn validate_virtio_block_chain(
229 &self,
230 head_index: u16,
231 min_length: usize,
232 ) -> VirtioResult<bool> {
233 if let Some(ref desc_table) = self.desc_table {
234 let descriptors = desc_table.follow_chain(head_index)?;
235 Ok(descriptors.len() >= min_length)
236 } else {
237 Err(VirtioError::QueueNotReady)
238 }
239 }
240
241 pub fn get_data_buffers(
243 &self,
244 head_index: u16,
245 device_type: VirtioDeviceID,
246 ) -> VirtioResult<Vec<(axaddrspace::GuestPhysAddr, usize, bool)>> {
247 if let Some(ref desc_table) = self.desc_table {
248 desc_table.get_data_buffers(head_index, device_type)
249 } else {
250 Err(VirtioError::QueueNotReady)
251 }
252 }
253
254 pub fn get_status_addr(&self, head_index: u16) -> VirtioResult<axaddrspace::GuestPhysAddr> {
256 if let Some(ref desc_table) = self.desc_table {
257 desc_table.get_status_addr(head_index)
258 } else {
259 Err(VirtioError::QueueNotReady)
260 }
261 }
262
263 pub fn should_notify(&self) -> VirtioResult<bool> {
265 if let Some(ref used_ring) = self.used_ring {
266 used_ring.should_notify()
267 } else {
268 Err(VirtioError::QueueNotReady)
269 }
270 }
271
272 pub fn write_status_byte(&self, head_index: u16, status: u8) -> VirtioResult<()> {
277 let status_addr_guest = self.get_status_addr(head_index)?;
279
280 trace!(
281 "Writing status byte {} to guest address 0x{:x} for descriptor chain {}",
282 status,
283 status_addr_guest.as_usize(),
284 head_index
285 );
286
287 self.accessor
289 .write_obj(status_addr_guest, status)
290 .map_err(|_| VirtioError::InvalidAddress)?;
291
292 Ok(())
293 }
294}