1use crate::QUEUE_MAX_SIZE;
5use crate::QueueResources;
6use crate::Resources;
7use crate::VirtioDevice;
8use crate::VirtioDoorbells;
9use crate::queue::QueueParams;
10use crate::spec::VIRTIO_MMIO_INTERRUPT_STATUS_CONFIG_CHANGE;
11use crate::spec::VIRTIO_MMIO_INTERRUPT_STATUS_USED_BUFFER;
12use crate::spec::VirtioDeviceFeatures;
13use crate::spec::VirtioDeviceStatus;
14use crate::spec::mmio::VirtioMmioRegister;
15use chipset_device::ChipsetDevice;
16use chipset_device::io::IoResult;
17use chipset_device::mmio::MmioIntercept;
18use chipset_device::poll_device::PollDevice;
19use device_emulators::ReadWriteRequestType;
20use device_emulators::read_as_u32_chunks;
21use device_emulators::write_as_u32_chunks;
22use guestmem::DoorbellRegistration;
23use inspect::Inspect;
24use inspect::InspectMut;
25use parking_lot::Mutex;
26use std::fmt;
27use std::ops::RangeInclusive;
28use std::sync::Arc;
29use vmcore::device_state::ChangeDeviceState;
30use vmcore::interrupt::Interrupt;
31use vmcore::line_interrupt::LineInterrupt;
32use vmcore::save_restore::NoSavedState;
33use vmcore::save_restore::RestoreError;
34use vmcore::save_restore::SaveError;
35use vmcore::save_restore::SaveRestore;
36
37#[derive(InspectMut)]
39pub struct VirtioMmioDevice {
40 #[inspect(skip)]
41 fixed_mmio_region: (&'static str, RangeInclusive<u64>),
42
43 #[inspect(mut)]
44 device: Box<dyn VirtioDevice>,
45 #[inspect(hex)]
46 device_id: u32,
47 #[inspect(hex)]
48 vendor_id: u32,
49 #[inspect(skip)]
50 device_feature: VirtioDeviceFeatures,
51 device_feature_select: u32,
52 #[inspect(skip)]
53 driver_feature: VirtioDeviceFeatures,
54 driver_feature_select: u32,
55 queue_select: u32,
56 #[inspect(skip)]
57 events: Vec<pal_event::Event>,
58 #[inspect(skip)]
59 queues: Vec<QueueParams>,
60 device_status: VirtioDeviceStatus,
61 disabling: bool,
62 #[inspect(skip)]
63 poll_waker: Option<std::task::Waker>,
64 config_generation: u32,
65 #[inspect(skip)]
66 doorbells: VirtioDoorbells,
67 interrupt_state: Arc<Mutex<InterruptState>>,
68}
69
70#[derive(Inspect)]
71struct InterruptState {
72 interrupt: LineInterrupt,
73 status: u32,
74}
75
76impl InterruptState {
77 fn update(&mut self, is_set: bool, bits: u32) {
78 if is_set {
79 self.status |= bits;
80 } else {
81 self.status &= !bits;
82 }
83 self.interrupt.set_level(self.status != 0);
84 }
85}
86
87impl fmt::Debug for VirtioMmioDevice {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 f.debug_struct("VirtioMmioDevice").finish()
91 }
92}
93
94impl VirtioMmioDevice {
95 pub fn new(
96 device: Box<dyn VirtioDevice>,
97 interrupt: LineInterrupt,
98 doorbell_registration: Option<Arc<dyn DoorbellRegistration>>,
99 mmio_gpa: u64,
100 mmio_len: u64,
101 ) -> Self {
102 let traits = device.traits();
103 let queues = (0..traits.max_queues)
104 .map(|_| QueueParams {
105 size: QUEUE_MAX_SIZE,
106 ..Default::default()
107 })
108 .collect();
109 let events = (0..traits.max_queues)
110 .map(|_| pal_event::Event::new())
111 .collect();
112 let interrupt_state = Arc::new(Mutex::new(InterruptState {
113 interrupt,
114 status: 0,
115 }));
116
117 let mut device_feature = traits.device_features.clone();
118 device_feature.set_bank(
119 0,
120 device_feature
121 .bank0()
122 .with_ring_event_idx(true)
123 .with_ring_indirect_desc(true)
124 .into_bits(),
125 );
126 device_feature.set_bank(1, device_feature.bank1().with_version_1(true).into_bits());
127 Self {
128 fixed_mmio_region: ("virtio-chipset", mmio_gpa..=(mmio_gpa + mmio_len - 1)),
129 device,
130 device_id: traits.device_id as u32,
131 vendor_id: 0x1af4,
132 device_feature,
133 device_feature_select: 0,
134 driver_feature: VirtioDeviceFeatures::new(),
135 driver_feature_select: 0,
136 queue_select: 0,
137 events,
138 queues,
139 device_status: VirtioDeviceStatus::new(),
140 disabling: false,
141 poll_waker: None,
142 config_generation: 0,
143 doorbells: VirtioDoorbells::new(doorbell_registration),
144 interrupt_state,
145 }
146 }
147
148 fn update_config_generation(&mut self) {
149 self.config_generation = self.config_generation.wrapping_add(1);
150 if self.device_status.driver_ok() {
151 self.interrupt_state
152 .lock()
153 .update(true, VIRTIO_MMIO_INTERRUPT_STATUS_CONFIG_CHANGE);
154 }
155 }
156}
157
158impl VirtioMmioDevice {
159 pub(crate) fn read_u32(&self, address: u64) -> u32 {
160 let offset = (address & 0xfff) as u16;
161 assert!(offset & 3 == 0);
162 match VirtioMmioRegister(offset) {
163 VirtioMmioRegister::MAGIC_VALUE => u32::from_le_bytes(*b"virt"),
164 VirtioMmioRegister::VERSION => 2,
165 VirtioMmioRegister::DEVICE_ID => self.device_id,
166 VirtioMmioRegister::VENDOR_ID => self.vendor_id,
167 VirtioMmioRegister::DEVICE_FEATURES => {
168 let feature_select = self.device_feature_select as usize;
169 self.device_feature.bank(feature_select)
170 }
171 VirtioMmioRegister::DEVICE_FEATURES_SEL => self.device_feature_select,
172 VirtioMmioRegister::DRIVER_FEATURES => {
173 let feature_select = self.driver_feature_select as usize;
174 self.driver_feature.bank(feature_select)
175 }
176 VirtioMmioRegister::DRIVER_FEATURES_SEL => self.driver_feature_select,
177 VirtioMmioRegister::QUEUE_SEL => self.queue_select,
178 VirtioMmioRegister::QUEUE_NUM_MAX => {
180 let queue_select = self.queue_select as usize;
181 if queue_select < self.queues.len() {
182 QUEUE_MAX_SIZE.into()
183 } else {
184 0
185 }
186 }
187 VirtioMmioRegister::QUEUE_NUM => {
188 let queue_select = self.queue_select as usize;
189 if queue_select < self.queues.len() {
190 self.queues[queue_select].size as u32
191 } else {
192 0
193 }
194 }
195 VirtioMmioRegister::QUEUE_READY => {
196 let queue_select = self.queue_select as usize;
197 if queue_select < self.queues.len() {
198 if self.queues[queue_select].enable {
199 1
200 } else {
201 0
202 }
203 } else {
204 0
205 }
206 }
207 VirtioMmioRegister::QUEUE_NOTIFY => 0,
208 VirtioMmioRegister::INTERRUPT_STATUS => self.interrupt_state.lock().status,
209 VirtioMmioRegister::INTERRUPT_ACK => 0,
210 VirtioMmioRegister::STATUS => self.device_status.as_u32(),
211 VirtioMmioRegister::QUEUE_DESC_LOW => {
212 let queue_select = self.queue_select as usize;
213 if queue_select < self.queues.len() {
214 self.queues[queue_select].desc_addr as u32
215 } else {
216 0
217 }
218 }
219 VirtioMmioRegister::QUEUE_DESC_HIGH => {
220 let queue_select = self.queue_select as usize;
221 if queue_select < self.queues.len() {
222 (self.queues[queue_select].desc_addr >> 32) as u32
223 } else {
224 0
225 }
226 }
227 VirtioMmioRegister::QUEUE_AVAIL_LOW => {
228 let queue_select = self.queue_select as usize;
229 if queue_select < self.queues.len() {
230 self.queues[queue_select].avail_addr as u32
231 } else {
232 0
233 }
234 }
235 VirtioMmioRegister::QUEUE_AVAIL_HIGH => {
236 let queue_select = self.queue_select as usize;
237 if queue_select < self.queues.len() {
238 (self.queues[queue_select].avail_addr >> 32) as u32
239 } else {
240 0
241 }
242 }
243 VirtioMmioRegister::QUEUE_USED_LOW => {
244 let queue_select = self.queue_select as usize;
245 if queue_select < self.queues.len() {
246 self.queues[queue_select].used_addr as u32
247 } else {
248 0
249 }
250 }
251 VirtioMmioRegister::QUEUE_USED_HIGH => {
252 let queue_select = self.queue_select as usize;
253 if queue_select < self.queues.len() {
254 (self.queues[queue_select].used_addr >> 32) as u32
255 } else {
256 0
257 }
258 }
259 VirtioMmioRegister::CONFIG_GENERATION => self.config_generation,
260 VirtioMmioRegister(offset) if offset >= VirtioMmioRegister::CONFIG.0 => self
261 .device
262 .read_registers_u32(offset - VirtioMmioRegister::CONFIG.0),
263 _ => 0xffffffff,
264 }
265 }
266
267 pub(crate) fn write_u32(&mut self, address: u64, val: u32) {
268 let offset = (address & 0xfff) as u16;
269 assert!(offset & 3 == 0);
270 let queue_select = self.queue_select as usize;
271 let queues_locked = self.device_status.driver_ok();
272 let features_locked = queues_locked || self.device_status.features_ok();
273 match VirtioMmioRegister(offset) {
274 VirtioMmioRegister::DEVICE_FEATURES_SEL => self.device_feature_select = val,
275 VirtioMmioRegister::DRIVER_FEATURES => {
276 let bank = self.driver_feature_select as usize;
277 if features_locked || bank >= self.device_feature.len() {
278 } else {
280 self.driver_feature
281 .set_bank(bank, val & self.device_feature.bank(bank));
282 }
283 }
284 VirtioMmioRegister::DRIVER_FEATURES_SEL => self.driver_feature_select = val,
285 VirtioMmioRegister::QUEUE_SEL => self.queue_select = val,
286 VirtioMmioRegister::QUEUE_NUM => {
287 if !queues_locked && queue_select < self.queues.len() {
288 let val = val as u16;
289 let queue = &mut self.queues[queue_select];
290 if val > QUEUE_MAX_SIZE {
291 queue.size = QUEUE_MAX_SIZE;
292 } else {
293 queue.size = val;
294 }
295 }
296 }
297 VirtioMmioRegister::QUEUE_READY => {
298 if !queues_locked && queue_select < self.queues.len() {
299 let queue = &mut self.queues[queue_select];
300 queue.enable = val != 0;
301 }
302 }
303 VirtioMmioRegister::QUEUE_NOTIFY => {
304 if (val as usize) < self.events.len() {
305 self.events[val as usize].signal();
306 }
307 }
308 VirtioMmioRegister::INTERRUPT_ACK => {
309 self.interrupt_state.lock().update(false, val);
310 }
311 VirtioMmioRegister::STATUS => {
312 if val == 0 {
313 if self.disabling {
314 return;
315 }
316 let started = self.device_status.driver_ok();
317 self.config_generation = 0;
318 if started {
319 self.doorbells.clear();
320 let waker = std::task::Waker::noop();
323 let mut cx = std::task::Context::from_waker(waker);
324 if self.device.poll_disable(&mut cx).is_pending() {
325 self.disabling = true;
326 if let Some(waker) = self.poll_waker.take() {
329 waker.wake();
330 }
331 return;
332 }
333 }
334 self.device_status = VirtioDeviceStatus::new();
336 self.interrupt_state.lock().update(false, !0);
337 return;
338 }
339
340 let new_status = VirtioDeviceStatus::from(val as u8);
341 if new_status.acknowledge() {
342 self.device_status.set_acknowledge(true);
343 }
344 if new_status.driver() {
345 self.device_status.set_driver(true);
346 }
347 if new_status.failed() {
348 self.device_status.set_failed(true);
349 }
350
351 if !self.device_status.features_ok() && new_status.features_ok() {
352 self.device_status.set_features_ok(true);
353 self.update_config_generation();
354 }
355
356 if !self.device_status.driver_ok() && new_status.driver_ok() {
357 let notification_address =
358 (address & !0xfff) + VirtioMmioRegister::QUEUE_NOTIFY.0 as u64;
359 for i in 0..self.events.len() {
360 self.doorbells.add(
361 notification_address,
362 Some(i as u64),
363 Some(4),
364 &self.events[i],
365 );
366 }
367 let queues = self
368 .queues
369 .iter()
370 .zip(self.events.iter().cloned())
371 .map(|(queue, event)| {
372 let interrupt_state = self.interrupt_state.clone();
373 let notify = Interrupt::from_fn(move || {
374 interrupt_state
375 .lock()
376 .update(true, VIRTIO_MMIO_INTERRUPT_STATUS_USED_BUFFER);
377 });
378 QueueResources {
379 params: *queue,
380 notify,
381 event,
382 }
383 })
384 .collect();
385
386 match self.device.enable(Resources {
387 features: self.driver_feature.clone(),
388 queues,
389 shared_memory_region: None,
390 shared_memory_size: 0,
391 }) {
392 Ok(()) => {
393 self.device_status.set_driver_ok(true);
394 }
395 Err(err) => {
396 self.doorbells.clear();
397 tracelimit::error_ratelimited!(
402 error = &*err as &dyn std::error::Error,
403 "virtio device enable failed"
404 );
405 }
406 }
407 self.update_config_generation();
408 }
409 }
410 VirtioMmioRegister::QUEUE_DESC_LOW => {
411 if !queues_locked && queue_select < self.queues.len() {
412 let queue = &mut self.queues[queue_select];
413 queue.desc_addr = queue.desc_addr & 0xffffffff00000000 | val as u64;
414 }
415 }
416 VirtioMmioRegister::QUEUE_DESC_HIGH => {
417 if !queues_locked && queue_select < self.queues.len() {
418 let queue = &mut self.queues[queue_select];
419 queue.desc_addr = (val as u64) << 32 | queue.desc_addr & 0xffffffff;
420 }
421 }
422 VirtioMmioRegister::QUEUE_AVAIL_LOW => {
423 if !queues_locked && queue_select < self.queues.len() {
424 let queue = &mut self.queues[queue_select];
425 queue.avail_addr = queue.avail_addr & 0xffffffff00000000 | val as u64;
426 }
427 }
428 VirtioMmioRegister::QUEUE_AVAIL_HIGH => {
429 if !queues_locked && queue_select < self.queues.len() {
430 let queue = &mut self.queues[queue_select];
431 queue.avail_addr = (val as u64) << 32 | queue.avail_addr & 0xffffffff;
432 }
433 }
434 VirtioMmioRegister::QUEUE_USED_LOW => {
435 if !queues_locked && (queue_select) < self.queues.len() {
436 let queue = &mut self.queues[queue_select];
437 queue.used_addr = queue.used_addr & 0xffffffff00000000 | val as u64;
438 }
439 }
440 VirtioMmioRegister::QUEUE_USED_HIGH => {
441 if !queues_locked && queue_select < self.queues.len() {
442 let queue = &mut self.queues[queue_select];
443 queue.used_addr = (val as u64) << 32 | queue.used_addr & 0xffffffff;
444 }
445 }
446 VirtioMmioRegister(offset) if offset >= VirtioMmioRegister::CONFIG.0 => self
447 .device
448 .write_registers_u32(offset - VirtioMmioRegister::CONFIG.0, val),
449 _ => (),
450 }
451 }
452}
453
454impl ChangeDeviceState for VirtioMmioDevice {
455 fn start(&mut self) {}
456
457 async fn stop(&mut self) {}
458
459 async fn reset(&mut self) {
460 if self.device_status.driver_ok() || self.disabling {
461 self.doorbells.clear();
462 std::future::poll_fn(|cx| self.device.poll_disable(cx)).await;
463 }
464 self.device_status = VirtioDeviceStatus::new();
465 self.disabling = false;
466 self.config_generation = 0;
467 self.interrupt_state.lock().update(false, !0);
468 }
469}
470
471impl PollDevice for VirtioMmioDevice {
472 fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
473 self.poll_waker = Some(cx.waker().clone());
474 if self.disabling {
475 if self.device.poll_disable(cx).is_ready() {
476 self.device_status = VirtioDeviceStatus::new();
477 self.disabling = false;
478 self.interrupt_state.lock().update(false, !0);
479 }
480 }
481 }
482}
483
484impl ChipsetDevice for VirtioMmioDevice {
485 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
486 Some(self)
487 }
488
489 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
490 Some(self)
491 }
492}
493
494impl SaveRestore for VirtioMmioDevice {
495 type SavedState = NoSavedState; fn save(&mut self) -> Result<Self::SavedState, SaveError> {
498 Ok(NoSavedState)
499 }
500
501 fn restore(&mut self, NoSavedState: Self::SavedState) -> Result<(), RestoreError> {
502 Ok(())
503 }
504}
505
506impl MmioIntercept for VirtioMmioDevice {
507 fn mmio_read(&mut self, address: u64, data: &mut [u8]) -> IoResult {
508 read_as_u32_chunks(address, data, |address| self.read_u32(address));
509 IoResult::Ok
510 }
511
512 fn mmio_write(&mut self, address: u64, data: &[u8]) -> IoResult {
513 write_as_u32_chunks(address, data, |address, request_type| match request_type {
514 ReadWriteRequestType::Write(value) => {
515 self.write_u32(address, value);
516 None
517 }
518 ReadWriteRequestType::Read => Some(self.read_u32(address)),
519 });
520 IoResult::Ok
521 }
522
523 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
524 std::slice::from_ref(&self.fixed_mmio_region)
525 }
526}