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    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            // The device reports rejected feature negotiation by withholding
208            // FEATURES_OK. FAILED remains owned by the driver.
209            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}