xref: /aosp_15_r20/external/crosvm/devices/src/virtio/virtio_mmio_device.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap;
6 
7 use acpi_tables::aml;
8 use acpi_tables::aml::Aml;
9 #[cfg(target_arch = "x86_64")]
10 use acpi_tables::sdt::SDT;
11 use anyhow::anyhow;
12 use anyhow::Context;
13 use base::error;
14 use base::pagesize;
15 use base::warn;
16 use base::AsRawDescriptors;
17 use base::Event;
18 use base::RawDescriptor;
19 use base::Result;
20 use hypervisor::Datamatch;
21 use resources::AllocOptions;
22 use resources::SystemAllocator;
23 use virtio_sys::virtio_config::VIRTIO_CONFIG_S_ACKNOWLEDGE;
24 use virtio_sys::virtio_config::VIRTIO_CONFIG_S_DRIVER;
25 use virtio_sys::virtio_config::VIRTIO_CONFIG_S_DRIVER_OK;
26 use virtio_sys::virtio_config::VIRTIO_CONFIG_S_FAILED;
27 use virtio_sys::virtio_config::VIRTIO_CONFIG_S_FEATURES_OK;
28 use virtio_sys::virtio_config::VIRTIO_CONFIG_S_NEEDS_RESET;
29 use virtio_sys::virtio_mmio::*;
30 use vm_memory::GuestMemory;
31 
32 use super::*;
33 use crate::pci::CrosvmDeviceId;
34 use crate::BusAccessInfo;
35 use crate::BusDevice;
36 use crate::BusDeviceObj;
37 use crate::DeviceId;
38 use crate::IrqEdgeEvent;
39 use crate::Suspendable;
40 
41 const VIRT_MAGIC: u32 = 0x74726976; /* 'virt' */
42 const VIRT_VERSION: u8 = 2;
43 const VIRT_VENDOR: u32 = 0x4D565243; /* 'CRVM' */
44 const VIRTIO_MMIO_REGION_SZ: u64 = 0x200;
45 
46 /// Implements the
47 /// [MMIO](http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-1090002)
48 /// transport for virtio devices.
49 pub struct VirtioMmioDevice {
50     device: Box<dyn VirtioDevice>,
51     device_activated: bool,
52 
53     interrupt: Option<Interrupt>,
54     interrupt_evt: Option<IrqEdgeEvent>,
55     async_intr_status: bool,
56     queues: Vec<QueueConfig>,
57     queue_evts: Vec<Event>,
58     mem: GuestMemory,
59     device_feature_select: u32,
60     driver_feature_select: u32,
61     queue_select: u16,
62     driver_status: u8,
63     mmio_base: u64,
64     irq_num: u32,
65     config_generation: u32,
66 }
67 
68 impl VirtioMmioDevice {
69     /// Constructs a new MMIO transport for the given virtio device.
new( mem: GuestMemory, device: Box<dyn VirtioDevice>, async_intr_status: bool, ) -> Result<Self>70     pub fn new(
71         mem: GuestMemory,
72         device: Box<dyn VirtioDevice>,
73         async_intr_status: bool,
74     ) -> Result<Self> {
75         let mut queue_evts = Vec::new();
76         for _ in device.queue_max_sizes() {
77             queue_evts.push(Event::new()?)
78         }
79         let queues = device
80             .queue_max_sizes()
81             .iter()
82             .map(|&s| QueueConfig::new(s, device.features()))
83             .collect();
84 
85         Ok(VirtioMmioDevice {
86             device,
87             device_activated: false,
88             interrupt: None,
89             interrupt_evt: None,
90             async_intr_status,
91             queues,
92             queue_evts,
93             mem,
94             device_feature_select: 0,
95             driver_feature_select: 0,
96             queue_select: 0,
97             driver_status: 0,
98             mmio_base: 0,
99             irq_num: 0,
100             config_generation: 0,
101         })
102     }
ioevents(&self) -> Vec<(&Event, u64, Datamatch)>103     pub fn ioevents(&self) -> Vec<(&Event, u64, Datamatch)> {
104         self.queue_evts
105             .iter()
106             .enumerate()
107             .map(|(i, event)| {
108                 (
109                     event,
110                     self.mmio_base + VIRTIO_MMIO_QUEUE_NOTIFY as u64,
111                     Datamatch::U32(Some(i.try_into().unwrap())),
112                 )
113             })
114             .collect()
115     }
116 
is_driver_ready(&self) -> bool117     fn is_driver_ready(&self) -> bool {
118         let ready_bits = (VIRTIO_CONFIG_S_ACKNOWLEDGE
119             | VIRTIO_CONFIG_S_DRIVER
120             | VIRTIO_CONFIG_S_DRIVER_OK
121             | VIRTIO_CONFIG_S_FEATURES_OK) as u8;
122         self.driver_status == ready_bits && self.driver_status & VIRTIO_CONFIG_S_FAILED as u8 == 0
123     }
124 
125     /// Determines if the driver has requested the device reset itself
is_reset_requested(&self) -> bool126     fn is_reset_requested(&self) -> bool {
127         self.driver_status == DEVICE_RESET as u8
128     }
129 
device_type(&self) -> u32130     fn device_type(&self) -> u32 {
131         self.device.device_type() as u32
132     }
133 
134     /// Activates the underlying `VirtioDevice`. `assign_irq` has to be called first.
activate(&mut self) -> anyhow::Result<()>135     fn activate(&mut self) -> anyhow::Result<()> {
136         let interrupt_evt = if let Some(ref evt) = self.interrupt_evt {
137             evt.try_clone()
138                 .with_context(|| format!("{} failed to clone interrupt_evt", self.debug_label()))?
139         } else {
140             return Err(anyhow!("{} interrupt_evt is none", self.debug_label()));
141         };
142 
143         let mem = self.mem.clone();
144         let interrupt = Interrupt::new_mmio(interrupt_evt, self.async_intr_status);
145         self.interrupt = Some(interrupt.clone());
146 
147         // Use ready queues and their events.
148         let queues = self
149             .queues
150             .iter_mut()
151             .zip(self.queue_evts.iter())
152             .enumerate()
153             .filter(|(_, (q, _))| q.ready())
154             .map(|(queue_index, (queue, evt))| {
155                 let queue_evt = evt.try_clone().context("failed to clone queue_evt")?;
156                 Ok((
157                     queue_index,
158                     queue
159                         .activate(&mem, queue_evt, interrupt.clone())
160                         .context("failed to activate queue")?,
161                 ))
162             })
163             .collect::<anyhow::Result<BTreeMap<usize, Queue>>>()?;
164 
165         if let Err(e) = self.device.activate(mem, interrupt, queues) {
166             error!("{} activate failed: {:#}", self.debug_label(), e);
167             self.driver_status |= VIRTIO_CONFIG_S_NEEDS_RESET as u8;
168         } else {
169             self.device_activated = true;
170         }
171 
172         Ok(())
173     }
174 
read_mmio(&self, info: BusAccessInfo, data: &mut [u8])175     fn read_mmio(&self, info: BusAccessInfo, data: &mut [u8]) {
176         if data.len() != std::mem::size_of::<u32>() {
177             warn!(
178                 "{}: unsupported read length {}, only support 4 bytes read",
179                 self.debug_label(),
180                 data.len()
181             );
182             return;
183         }
184 
185         if info.offset >= VIRTIO_MMIO_CONFIG as u64 {
186             self.device
187                 .read_config(info.offset - VIRTIO_MMIO_CONFIG as u64, data);
188             return;
189         }
190 
191         let val = match info.offset as u32 {
192             VIRTIO_MMIO_MAGIC_VALUE => VIRT_MAGIC,
193             VIRTIO_MMIO_VERSION => VIRT_VERSION.into(), // legacy is not supported
194             VIRTIO_MMIO_DEVICE_ID => self.device_type(),
195             VIRTIO_MMIO_VENDOR_ID => VIRT_VENDOR,
196             VIRTIO_MMIO_DEVICE_FEATURES => {
197                 if self.device_feature_select < 2 {
198                     (self.device.features() >> (self.device_feature_select * 32)) as u32
199                 } else {
200                     0
201                 }
202             }
203             VIRTIO_MMIO_QUEUE_NUM_MAX => self.with_queue(|q| q.max_size()).unwrap_or(0).into(),
204             VIRTIO_MMIO_QUEUE_PFN => {
205                 warn!(
206                     "{}: read from legacy register {}, in non-legacy mode",
207                     self.debug_label(),
208                     info.offset,
209                 );
210                 0
211             }
212             VIRTIO_MMIO_QUEUE_READY => self.with_queue(|q| q.ready()).unwrap_or(false).into(),
213             VIRTIO_MMIO_INTERRUPT_STATUS => {
214                 if let Some(interrupt) = &self.interrupt {
215                     interrupt.read_interrupt_status().into()
216                 } else {
217                     0
218                 }
219             }
220             VIRTIO_MMIO_STATUS => self.driver_status.into(),
221             VIRTIO_MMIO_CONFIG_GENERATION => self.config_generation,
222             _ => {
223                 warn!("{}: unsupported read address {}", self.debug_label(), info);
224                 return;
225             }
226         };
227 
228         let val_arr = val.to_le_bytes();
229         data.copy_from_slice(&val_arr);
230     }
231 
write_mmio(&mut self, info: BusAccessInfo, data: &[u8])232     fn write_mmio(&mut self, info: BusAccessInfo, data: &[u8]) {
233         if data.len() != std::mem::size_of::<u32>() {
234             warn!(
235                 "{}: unsupported write length {}, only support 4 bytes write",
236                 self.debug_label(),
237                 data.len()
238             );
239             return;
240         }
241 
242         if info.offset >= VIRTIO_MMIO_CONFIG as u64 {
243             self.device
244                 .write_config(info.offset - VIRTIO_MMIO_CONFIG as u64, data);
245             return;
246         }
247 
248         // This unwrap cannot fail since data.len() is checked.
249         let val = u32::from_le_bytes(data.try_into().unwrap());
250 
251         macro_rules! hi {
252             ($q:expr, $get:ident, $set:ident, $x:expr) => {
253                 $q.$set(($q.$get() & 0xffffffff) | (($x as u64) << 32))
254             };
255         }
256         macro_rules! lo {
257             ($q:expr, $get:ident, $set:ident, $x:expr) => {
258                 $q.$set(($q.$get() & !0xffffffff) | ($x as u64))
259             };
260         }
261 
262         match info.offset as u32 {
263             VIRTIO_MMIO_DEVICE_FEATURES_SEL => self.device_feature_select = val,
264             VIRTIO_MMIO_DRIVER_FEATURES_SEL => self.driver_feature_select = val,
265             VIRTIO_MMIO_DRIVER_FEATURES => {
266                 if self.driver_feature_select < 2 {
267                     let features: u64 = (val as u64) << (self.driver_feature_select * 32);
268                     self.device.ack_features(features);
269                     for queue in self.queues.iter_mut() {
270                         queue.ack_features(features);
271                     }
272                 } else {
273                     warn!(
274                         "invalid ack_features (page {}, value 0x{:x})",
275                         self.driver_feature_select, val
276                     );
277                 }
278             }
279             VIRTIO_MMIO_GUEST_PAGE_SIZE => warn!(
280                 "{}: write to legacy register {}, in non-legacy mode",
281                 self.debug_label(),
282                 info.offset,
283             ),
284             VIRTIO_MMIO_QUEUE_SEL => self.queue_select = val as u16,
285             VIRTIO_MMIO_QUEUE_NUM => self.with_queue_mut(|q| q.set_size(val as u16)),
286             VIRTIO_MMIO_QUEUE_ALIGN => warn!(
287                 "{}: write to legacy register {}, in non-legacy mode",
288                 self.debug_label(),
289                 info.offset,
290             ),
291             VIRTIO_MMIO_QUEUE_PFN => warn!(
292                 "{}: write to legacy register {}, in non-legacy mode",
293                 self.debug_label(),
294                 info.offset,
295             ),
296             VIRTIO_MMIO_QUEUE_READY => self.with_queue_mut(|q| q.set_ready(val == 1)),
297             VIRTIO_MMIO_QUEUE_NOTIFY => {} // Handled with ioevents.
298             VIRTIO_MMIO_INTERRUPT_ACK => {
299                 if let Some(interrupt) = &self.interrupt {
300                     interrupt.clear_interrupt_status_bits(val as u8)
301                 }
302             }
303             VIRTIO_MMIO_STATUS => self.driver_status = val as u8,
304             VIRTIO_MMIO_QUEUE_DESC_LOW => {
305                 self.with_queue_mut(|q| lo!(q, desc_table, set_desc_table, val))
306             }
307             VIRTIO_MMIO_QUEUE_DESC_HIGH => {
308                 self.with_queue_mut(|q| hi!(q, desc_table, set_desc_table, val))
309             }
310             VIRTIO_MMIO_QUEUE_AVAIL_LOW => {
311                 self.with_queue_mut(|q| lo!(q, avail_ring, set_avail_ring, val))
312             }
313             VIRTIO_MMIO_QUEUE_AVAIL_HIGH => {
314                 self.with_queue_mut(|q| hi!(q, avail_ring, set_avail_ring, val))
315             }
316             VIRTIO_MMIO_QUEUE_USED_LOW => {
317                 self.with_queue_mut(|q| lo!(q, used_ring, set_used_ring, val))
318             }
319             VIRTIO_MMIO_QUEUE_USED_HIGH => {
320                 self.with_queue_mut(|q| hi!(q, used_ring, set_used_ring, val))
321             }
322             _ => {
323                 warn!("{}: unsupported write address {}", self.debug_label(), info);
324                 return;
325             }
326         };
327 
328         if !self.device_activated && self.is_driver_ready() {
329             if let Err(e) = self.activate() {
330                 error!("failed to activate device: {:#}", e);
331             }
332         }
333 
334         // Device has been reset by the driver
335         if self.device_activated && self.is_reset_requested() {
336             if let Err(e) = self.device.reset() {
337                 error!("failed to reset {} device: {:#}", self.debug_label(), e);
338             } else {
339                 self.device_activated = false;
340                 // reset queues
341                 self.queues.iter_mut().for_each(QueueConfig::reset);
342                 // select queue 0 by default
343                 self.queue_select = 0;
344                 // reset interrupt
345                 self.interrupt = None;
346             }
347         }
348     }
349 
with_queue<U, F>(&self, f: F) -> Option<U> where F: FnOnce(&QueueConfig) -> U,350     fn with_queue<U, F>(&self, f: F) -> Option<U>
351     where
352         F: FnOnce(&QueueConfig) -> U,
353     {
354         self.queues.get(self.queue_select as usize).map(f)
355     }
356 
with_queue_mut<F>(&mut self, f: F) where F: FnOnce(&mut QueueConfig),357     fn with_queue_mut<F>(&mut self, f: F)
358     where
359         F: FnOnce(&mut QueueConfig),
360     {
361         if let Some(queue) = self.queues.get_mut(self.queue_select as usize) {
362             f(queue);
363         }
364     }
365 
allocate_regions( &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, resources::Error>366     pub fn allocate_regions(
367         &mut self,
368         resources: &mut SystemAllocator,
369     ) -> std::result::Result<Vec<(u64, u64)>, resources::Error> {
370         let mut ranges = Vec::new();
371         let alloc_id = resources.get_anon_alloc();
372         let start_addr = resources.allocate_mmio(
373             VIRTIO_MMIO_REGION_SZ,
374             alloc_id,
375             "virtio_mmio".to_string(),
376             AllocOptions::new().align(pagesize() as u64),
377         )?;
378         self.mmio_base = start_addr;
379         ranges.push((start_addr, VIRTIO_MMIO_REGION_SZ));
380         Ok(ranges)
381     }
382 
assign_irq(&mut self, irq_evt: &IrqEdgeEvent, irq_num: u32)383     pub fn assign_irq(&mut self, irq_evt: &IrqEdgeEvent, irq_num: u32) {
384         self.interrupt_evt = Some(irq_evt.try_clone().unwrap());
385         self.irq_num = irq_num;
386     }
387 
keep_rds(&self) -> Vec<RawDescriptor>388     pub fn keep_rds(&self) -> Vec<RawDescriptor> {
389         let mut rds = self.device.keep_rds();
390         if let Some(interrupt_evt) = &self.interrupt_evt {
391             rds.extend(interrupt_evt.as_raw_descriptors());
392         }
393         rds
394     }
395 
on_device_sandboxed(&mut self)396     fn on_device_sandboxed(&mut self) {
397         self.device.on_device_sandboxed();
398     }
399 
400     #[cfg(target_arch = "x86_64")]
generate_acpi(&mut self, mut sdts: Vec<SDT>) -> Option<Vec<SDT>>401     pub fn generate_acpi(&mut self, mut sdts: Vec<SDT>) -> Option<Vec<SDT>> {
402         const OEM_REVISION: u32 = 1;
403         const SSDT_REVISION: u8 = 0;
404 
405         let mut amls = Vec::new();
406         self.to_aml_bytes(&mut amls);
407         if amls.is_empty() {
408             return Some(sdts);
409         }
410 
411         // Use existing SSDT, otherwise create a new one.
412         let ssdt = sdts.iter_mut().find(|sdt| sdt.is_signature(b"SSDT"));
413         if let Some(ssdt) = ssdt {
414             ssdt.append_slice(&amls);
415         } else {
416             let mut ssdt = SDT::new(
417                 *b"SSDT",
418                 acpi_tables::HEADER_LEN,
419                 SSDT_REVISION,
420                 *b"CROSVM",
421                 *b"CROSVMDT",
422                 OEM_REVISION,
423             );
424 
425             ssdt.append_slice(&amls);
426             sdts.push(ssdt);
427         }
428         self.device.generate_acpi(&None, sdts)
429     }
430 }
431 
432 impl Aml for VirtioMmioDevice {
to_aml_bytes(&self, bytes: &mut Vec<u8>)433     fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
434         aml::Device::new(
435             "VIOM".into(),
436             vec![
437                 &aml::Name::new("_HID".into(), &"LNRO0005"),
438                 &aml::Name::new(
439                     "_CRS".into(),
440                     &aml::ResourceTemplate::new(vec![
441                         &aml::AddressSpace::new_memory(
442                             aml::AddressSpaceCachable::NotCacheable,
443                             true,
444                             self.mmio_base,
445                             self.mmio_base + VIRTIO_MMIO_REGION_SZ - 1,
446                         ),
447                         &aml::Interrupt::new(true, true, false, false, self.irq_num),
448                     ]),
449                 ),
450             ],
451         )
452         .to_aml_bytes(bytes);
453     }
454 }
455 
456 impl BusDeviceObj for VirtioMmioDevice {
as_virtio_mmio_device(&self) -> Option<&VirtioMmioDevice>457     fn as_virtio_mmio_device(&self) -> Option<&VirtioMmioDevice> {
458         Some(self)
459     }
as_virtio_mmio_device_mut(&mut self) -> Option<&mut VirtioMmioDevice>460     fn as_virtio_mmio_device_mut(&mut self) -> Option<&mut VirtioMmioDevice> {
461         Some(self)
462     }
into_virtio_mmio_device(self: Box<Self>) -> Option<Box<VirtioMmioDevice>>463     fn into_virtio_mmio_device(self: Box<Self>) -> Option<Box<VirtioMmioDevice>> {
464         Some(self)
465     }
466 }
467 
468 impl BusDevice for VirtioMmioDevice {
debug_label(&self) -> String469     fn debug_label(&self) -> String {
470         format!("mmio{}", self.device.debug_label())
471     }
472 
device_id(&self) -> DeviceId473     fn device_id(&self) -> DeviceId {
474         CrosvmDeviceId::VirtioMmio.into()
475     }
476 
read(&mut self, info: BusAccessInfo, data: &mut [u8])477     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
478         self.read_mmio(info, data)
479     }
480 
write(&mut self, info: BusAccessInfo, data: &[u8])481     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
482         self.write_mmio(info, data)
483     }
484 
on_sandboxed(&mut self)485     fn on_sandboxed(&mut self) {
486         self.on_device_sandboxed();
487     }
488 }
489 
490 // TODO: Mimic the Suspendable impl in ViritoPciDevice when/if someone wants it.
491 impl Suspendable for VirtioMmioDevice {}
492