Skip to main content

axvirtio_common/pci/transport/
state.rs

1use 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/// Permit covering synchronous queue activity through completion publication.
84#[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}
102
103pub(super) struct TransportState {
104    pub(super) device_feature_select: u32,
105    pub(super) driver_feature_select: u32,
106    pub(super) driver_features: u64,
107    pub(super) status: u8,
108    pub(super) queue_select: u16,
109    pub(super) queue_size: u16,
110    pub(super) queues: alloc::vec::Vec<QueueState>,
111    pub(super) queue_generation: u64,
112    pub(super) config_generation: u8,
113    pub(super) fault_reported: bool,
114    pub(super) device_needs_reset: bool,
115    pub(super) reset_pending: bool,
116}
117
118impl TransportState {
119    pub(super) fn new(queue_num_max: u16, queue_size_max: u16) -> Self {
120        let queues = (0..queue_num_max)
121            .map(|index| QueueState {
122                queue: VirtioQueue::new(index, queue_size_max, Arc::new(NoGuestMemoryAccessor)),
123                enabled: false,
124                processing: false,
125            })
126            .collect();
127        Self {
128            device_feature_select: 0,
129            driver_feature_select: 0,
130            driver_features: 0,
131            status: 0,
132            queue_select: 0,
133            queue_size: queue_size_max,
134            queues,
135            queue_generation: 0,
136            config_generation: 0,
137            fault_reported: false,
138            device_needs_reset: false,
139            reset_pending: false,
140        }
141    }
142
143    pub(super) fn reset(&mut self, queue_size_max: u16) {
144        self.device_feature_select = 0;
145        self.driver_feature_select = 0;
146        self.driver_features = 0;
147        self.status = VIRTIO_STATUS_DEVICE_NEEDS_RESET as u8;
148        self.queue_select = 0;
149        self.queue_size = queue_size_max;
150        self.queue_generation = self.queue_generation.wrapping_add(1);
151        self.config_generation = self.config_generation.wrapping_add(1);
152        self.fault_reported = false;
153        self.device_needs_reset = true;
154        self.reset_pending = true;
155        for queue in &mut self.queues {
156            queue.enabled = false;
157            queue.processing = false;
158            queue.queue.reset();
159        }
160    }
161
162    pub(super) fn write_driver_status(
163        &mut self,
164        requested: u8,
165        device_features: u64,
166    ) -> DeviceResult {
167        if requested & !KNOWN_STATUS_BITS != 0 {
168            return Err(DeviceError::InvalidInput {
169                operation: "virtio-pci status",
170                detail: format!("unknown status bits: {requested:#x}"),
171            });
172        }
173
174        let current_driver = self.status & DRIVER_STATUS_BITS;
175        let requested_driver = requested & DRIVER_STATUS_BITS;
176        if current_driver & !requested_driver != 0 {
177            return Err(DeviceError::InvalidState {
178                operation: "update virtio-pci status",
179                detail: format!(
180                    "nonzero status writes cannot clear driver bits: {current_driver:#x} -> \
181                     {requested_driver:#x}"
182                ),
183            });
184        }
185        if current_driver & VIRTIO_STATUS_FAILED as u8 != 0 && requested_driver != current_driver {
186            return Err(DeviceError::InvalidState {
187                operation: "update virtio-pci status",
188                detail: "FAILED status requires a device reset before further progress".into(),
189            });
190        }
191
192        validate_driver_status_phase(requested_driver)?;
193
194        let device_status = if self.device_needs_reset {
195            VIRTIO_STATUS_DEVICE_NEEDS_RESET as u8
196        } else {
197            0
198        };
199        let mut accepted_driver = requested_driver;
200        if current_driver & VIRTIO_STATUS_FEATURES_OK as u8 == 0
201            && requested_driver & VIRTIO_STATUS_FEATURES_OK as u8 != 0
202            && self.driver_features & !device_features != 0
203        {
204            // The device reports rejected feature negotiation by withholding
205            // FEATURES_OK. FAILED remains owned by the driver.
206            accepted_driver &= !(VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK) as u8;
207        }
208
209        self.status = accepted_driver | device_status;
210        Ok(())
211    }
212
213    pub(super) fn ensure_feature_negotiation_open(&self) -> DeviceResult {
214        if self.status & (VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_FAILED) as u8 != 0 {
215            Err(DeviceError::InvalidState {
216                operation: "update virtio-pci driver features",
217                detail: "driver features are frozen after feature negotiation closes".into(),
218            })
219        } else {
220            Ok(())
221        }
222    }
223}
224
225fn validate_driver_status_phase(status: u8) -> DeviceResult {
226    let acknowledge = VIRTIO_STATUS_ACKNOWLEDGE as u8;
227    let driver = VIRTIO_STATUS_DRIVER as u8;
228    let features_ok = VIRTIO_STATUS_FEATURES_OK as u8;
229    let driver_ok = VIRTIO_STATUS_DRIVER_OK as u8;
230    let valid = status & driver == 0 || status & acknowledge != 0;
231    let features_valid =
232        status & features_ok == 0 || status & (acknowledge | driver) == acknowledge | driver;
233    let driver_ok_valid = status & driver_ok == 0
234        || status & (acknowledge | driver | features_ok) == acknowledge | driver | features_ok;
235    if valid && features_valid && driver_ok_valid {
236        Ok(())
237    } else {
238        Err(DeviceError::InvalidState {
239            operation: "update virtio-pci status",
240            detail: format!("driver status phase is out of order: {status:#x}"),
241        })
242    }
243}