virtio/transport/
mmio.rs

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