axvirtio_common/pci/transport/
state.rs1use alloc::{format, sync::Arc};
2use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
3
4use axdevice_base::{DeviceError, DeviceResult};
5
6use super::VirtioQueueGeneration;
7use crate::{
8 NoGuestMemoryAccessor, VirtioQueue,
9 constants::{
10 VIRTIO_STATUS_ACKNOWLEDGE, VIRTIO_STATUS_DEVICE_NEEDS_RESET, VIRTIO_STATUS_DRIVER,
11 VIRTIO_STATUS_DRIVER_OK, VIRTIO_STATUS_FAILED, VIRTIO_STATUS_FEATURES_OK,
12 },
13};
14
15const DRIVER_PHASE_BITS: u8 = (VIRTIO_STATUS_ACKNOWLEDGE
16 | VIRTIO_STATUS_DRIVER
17 | VIRTIO_STATUS_FEATURES_OK
18 | VIRTIO_STATUS_DRIVER_OK) as u8;
19const DRIVER_STATUS_BITS: u8 = DRIVER_PHASE_BITS | VIRTIO_STATUS_FAILED as u8;
20const KNOWN_STATUS_BITS: u8 = DRIVER_STATUS_BITS | VIRTIO_STATUS_DEVICE_NEEDS_RESET as u8;
21
22#[derive(Debug)]
23pub(super) struct QueueActivity {
24 pub(super) accepting: AtomicBool,
25 pub(super) active: AtomicUsize,
26 pub(super) resetting: AtomicBool,
27}
28
29impl QueueActivity {
30 pub(super) const fn new() -> Self {
31 Self {
32 accepting: AtomicBool::new(true),
33 active: AtomicUsize::new(0),
34 resetting: AtomicBool::new(false),
35 }
36 }
37
38 pub(super) fn begin_reset(&self) -> bool {
39 self.resetting
40 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
41 .is_ok()
42 }
43
44 pub(super) fn acquire(
45 self: &Arc<Self>,
46 generation: VirtioQueueGeneration,
47 ) -> Option<ActivityPermit> {
48 if !self.accepting.load(Ordering::Acquire) {
49 return None;
50 }
51 self.active.fetch_add(1, Ordering::AcqRel);
52 if self.accepting.load(Ordering::Acquire) {
53 Some(ActivityPermit {
54 activity: Arc::clone(self),
55 generation,
56 })
57 } else {
58 self.active.fetch_sub(1, Ordering::AcqRel);
59 None
60 }
61 }
62
63 pub(super) fn close_and_drain(&self) -> bool {
64 self.accepting.store(false, Ordering::Release);
65 for _ in 0..super::RESET_DRAIN_SPIN_LIMIT {
66 if self.active.load(Ordering::Acquire) == 0 {
67 return true;
68 }
69 core::hint::spin_loop();
70 }
71 false
72 }
73
74 pub(super) fn reopen(&self) {
75 self.accepting.store(true, Ordering::Release);
76 }
77
78 pub(super) fn finish_reset(&self) {
79 self.resetting.store(false, Ordering::Release);
80 }
81}
82
83#[derive(Debug)]
85pub struct ActivityPermit {
86 pub(super) activity: Arc<QueueActivity>,
87 pub(super) generation: VirtioQueueGeneration,
88}
89
90impl Drop for ActivityPermit {
91 fn drop(&mut self) {
92 self.activity.active.fetch_sub(1, Ordering::Release);
93 }
94}
95
96#[derive(Debug)]
97pub(super) struct QueueState {
98 pub(super) queue: VirtioQueue<NoGuestMemoryAccessor>,
99 pub(super) enabled: bool,
100 pub(super) processing: bool,
101 pub(super) notification_pending: bool,
102}
103
104pub(super) struct TransportState {
105 pub(super) device_feature_select: u32,
106 pub(super) driver_feature_select: u32,
107 pub(super) driver_features: u64,
108 pub(super) status: u8,
109 pub(super) queue_select: u16,
110 pub(super) queue_size: u16,
111 pub(super) queues: alloc::vec::Vec<QueueState>,
112 pub(super) queue_generation: u64,
113 pub(super) config_generation: u8,
114 pub(super) fault_reported: bool,
115 pub(super) device_needs_reset: bool,
116 pub(super) reset_pending: bool,
117}
118
119impl TransportState {
120 pub(super) fn new(queue_num_max: u16, queue_size_max: u16) -> Self {
121 let queues = (0..queue_num_max)
122 .map(|index| QueueState {
123 queue: VirtioQueue::new(index, queue_size_max, Arc::new(NoGuestMemoryAccessor)),
124 enabled: false,
125 processing: false,
126 notification_pending: false,
127 })
128 .collect();
129 Self {
130 device_feature_select: 0,
131 driver_feature_select: 0,
132 driver_features: 0,
133 status: 0,
134 queue_select: 0,
135 queue_size: queue_size_max,
136 queues,
137 queue_generation: 0,
138 config_generation: 0,
139 fault_reported: false,
140 device_needs_reset: false,
141 reset_pending: false,
142 }
143 }
144
145 pub(super) fn reset(&mut self, queue_size_max: u16) {
146 self.device_feature_select = 0;
147 self.driver_feature_select = 0;
148 self.driver_features = 0;
149 self.status = VIRTIO_STATUS_DEVICE_NEEDS_RESET as u8;
150 self.queue_select = 0;
151 self.queue_size = queue_size_max;
152 self.queue_generation = self.queue_generation.wrapping_add(1);
153 self.config_generation = self.config_generation.wrapping_add(1);
154 self.fault_reported = false;
155 self.device_needs_reset = true;
156 self.reset_pending = true;
157 for queue in &mut self.queues {
158 queue.enabled = false;
159 queue.processing = false;
160 queue.notification_pending = false;
161 queue.queue.reset();
162 }
163 }
164
165 pub(super) fn write_driver_status(
166 &mut self,
167 requested: u8,
168 device_features: u64,
169 ) -> DeviceResult {
170 if requested & !KNOWN_STATUS_BITS != 0 {
171 return Err(DeviceError::InvalidInput {
172 operation: "virtio-pci status",
173 detail: format!("unknown status bits: {requested:#x}"),
174 });
175 }
176
177 let current_driver = self.status & DRIVER_STATUS_BITS;
178 let requested_driver = requested & DRIVER_STATUS_BITS;
179 if current_driver & !requested_driver != 0 {
180 return Err(DeviceError::InvalidState {
181 operation: "update virtio-pci status",
182 detail: format!(
183 "nonzero status writes cannot clear driver bits: {current_driver:#x} -> \
184 {requested_driver:#x}"
185 ),
186 });
187 }
188 if current_driver & VIRTIO_STATUS_FAILED as u8 != 0 && requested_driver != current_driver {
189 return Err(DeviceError::InvalidState {
190 operation: "update virtio-pci status",
191 detail: "FAILED status requires a device reset before further progress".into(),
192 });
193 }
194
195 validate_driver_status_phase(requested_driver)?;
196
197 let device_status = if self.device_needs_reset {
198 VIRTIO_STATUS_DEVICE_NEEDS_RESET as u8
199 } else {
200 0
201 };
202 let mut accepted_driver = requested_driver;
203 if current_driver & VIRTIO_STATUS_FEATURES_OK as u8 == 0
204 && requested_driver & VIRTIO_STATUS_FEATURES_OK as u8 != 0
205 && self.driver_features & !device_features != 0
206 {
207 accepted_driver &= !(VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK) as u8;
210 }
211
212 self.status = accepted_driver | device_status;
213 Ok(())
214 }
215
216 pub(super) fn ensure_feature_negotiation_open(&self) -> DeviceResult {
217 if self.status & (VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_FAILED) as u8 != 0 {
218 Err(DeviceError::InvalidState {
219 operation: "update virtio-pci driver features",
220 detail: "driver features are frozen after feature negotiation closes".into(),
221 })
222 } else {
223 Ok(())
224 }
225 }
226}
227
228fn validate_driver_status_phase(status: u8) -> DeviceResult {
229 let acknowledge = VIRTIO_STATUS_ACKNOWLEDGE as u8;
230 let driver = VIRTIO_STATUS_DRIVER as u8;
231 let features_ok = VIRTIO_STATUS_FEATURES_OK as u8;
232 let driver_ok = VIRTIO_STATUS_DRIVER_OK as u8;
233 let valid = status & driver == 0 || status & acknowledge != 0;
234 let features_valid =
235 status & features_ok == 0 || status & (acknowledge | driver) == acknowledge | driver;
236 let driver_ok_valid = status & driver_ok == 0
237 || status & (acknowledge | driver | features_ok) == acknowledge | driver | features_ok;
238 if valid && features_valid && driver_ok_valid {
239 Ok(())
240 } else {
241 Err(DeviceError::InvalidState {
242 operation: "update virtio-pci status",
243 detail: format!("driver status phase is out of order: {status:#x}"),
244 })
245 }
246}