axvirtio_common/queue/
mod.rs

1mod 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/// VirtIO queue implementation
18#[derive(Debug, Clone)]
19pub struct VirtioQueue<T: GuestMemoryAccessor + Clone> {
20    /// Queue index
21    pub index: u16,
22    /// Queue size
23    pub size: u16,
24    /// Descriptor table
25    pub desc_table: Option<DescriptorTable<T>>,
26    /// Available ring
27    avail_ring: Option<AvailableRing<T>>,
28    /// Used ring
29    used_ring: Option<UsedRing<T>>,
30    /// Guest memory accessor
31    accessor: Arc<T>,
32    /// Maximum queue size
33    pub max_size: u16,
34    /// Queue ready flag
35    pub ready: bool,
36    /// Descriptor table address (guest physical)
37    pub desc_table_addr: GuestPhysAddr,
38    /// Available ring address (guest physical)
39    pub avail_ring_addr: GuestPhysAddr,
40    /// Used ring address (guest physical)
41    pub used_ring_addr: GuestPhysAddr,
42    /// Next available index
43    next_avail: u16,
44    /// Next used index
45    next_used: u16,
46    /// Event index enabled
47    pub event_idx_enabled: bool,
48}
49
50impl<T: GuestMemoryAccessor + Clone> VirtioQueue<T> {
51    /// Create a new VirtIO queue
52    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    /// Set queue size
72    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    /// Set descriptor table address
81    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    /// Set available ring address
93    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    /// Set used ring address
105    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    /// Mark queue as ready
117    pub fn set_ready(&mut self, ready: bool) {
118        self.ready = ready;
119    }
120
121    /// Check if queue is valid and ready
122    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    /// Reset the queue
130    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    /// Read available ring index
143    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    /// Add a used buffer to the used ring
152    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        // Use the UsedRing to properly manage the used ring
158        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            // Fallback: just update the index
163            self.next_used = (self.next_used + 1) % self.size;
164        }
165
166        Ok(())
167    }
168
169    /// Get next available descriptor
170    pub fn pop_avail(&mut self) -> VirtioResult<Option<u16>> {
171        if !self.is_valid() {
172            return Err(VirtioError::QueueNotReady);
173        }
174
175        // In a real implementation, this would read from guest memory
176        // For now, return None to indicate no available descriptors
177        Ok(None)
178    }
179
180    /// Get the used ring reference
181    pub fn get_used_ring(&self) -> Option<&UsedRing<T>> {
182        self.used_ring.as_ref()
183    }
184
185    /// Get the used ring mutable reference
186    pub fn get_used_ring_mut(&mut self) -> Option<&mut UsedRing<T>> {
187        self.used_ring.as_mut()
188    }
189
190    /// Get the available ring reference
191    pub fn get_avail_ring(&self) -> Option<&AvailableRing<T>> {
192        self.avail_ring.as_ref()
193    }
194
195    /// Get the descriptor table reference
196    pub fn get_desc_table(&self) -> Option<&DescriptorTable<T>> {
197        self.desc_table.as_ref()
198    }
199
200    /// Read available ring entry
201    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    /// Update last available index
210    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    /// Get last available index
219    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    /// Validate VirtIO block chain
228    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    /// Get data buffers from descriptor chain
242    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    /// Get status address from descriptor chain
255    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    /// Check if should notify
264    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    /// Write status byte to the status buffer of a descriptor chain
273    ///
274    /// This method writes the status byte to the last descriptor in the chain,
275    /// which should be a write-only descriptor according to VirtIO specification.
276    pub fn write_status_byte(&self, head_index: u16, status: u8) -> VirtioResult<()> {
277        // Get the status descriptor address (last descriptor in chain)
278        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        // Write the status byte to guest memory using the new memory access interface
288        self.accessor
289            .write_obj(status_addr_guest, status)
290            .map_err(|_| VirtioError::InvalidAddress)?;
291
292        Ok(())
293    }
294}