1use std::collections::BTreeMap;
6
7use acpi_tables::aml;
8use acpi_tables::aml::Aml;
9use anyhow::anyhow;
10use anyhow::Context;
11use base::error;
12use base::pagesize;
13use base::warn;
14use base::AsRawDescriptors;
15use base::Event;
16use base::RawDescriptor;
17use base::Result;
18use hypervisor::Datamatch;
19use resources::AllocOptions;
20use resources::SystemAllocator;
21use virtio_sys::virtio_config::VIRTIO_CONFIG_S_ACKNOWLEDGE;
22use virtio_sys::virtio_config::VIRTIO_CONFIG_S_DRIVER;
23use virtio_sys::virtio_config::VIRTIO_CONFIG_S_DRIVER_OK;
24use virtio_sys::virtio_config::VIRTIO_CONFIG_S_FAILED;
25use virtio_sys::virtio_config::VIRTIO_CONFIG_S_FEATURES_OK;
26use virtio_sys::virtio_config::VIRTIO_CONFIG_S_NEEDS_RESET;
27use virtio_sys::virtio_mmio::*;
28use vm_control::DeviceId;
29use vm_control::PlatformDeviceId;
30use vm_memory::GuestMemory;
31
32use super::*;
33use crate::BusAccessInfo;
34use crate::BusDevice;
35use crate::BusDeviceObj;
36use crate::IrqEdgeEvent;
37use crate::Suspendable;
38
39const VIRT_MAGIC: u32 = 0x74726976; const VIRT_VERSION: u8 = 2;
41const VIRT_VENDOR: u32 = 0x4D565243; const VIRTIO_MMIO_REGION_SZ: u64 = 0x200;
43
44pub struct VirtioMmioDevice {
48 device: Box<dyn VirtioDevice>,
49 device_activated: bool,
50
51 interrupt: Option<Interrupt>,
52 interrupt_evt: Option<IrqEdgeEvent>,
53 async_intr_status: bool,
54 queues: Vec<QueueConfig>,
55 queue_evts: Vec<Event>,
56 mem: GuestMemory,
57 device_feature_select: u32,
58 driver_feature_select: u32,
59 queue_select: u16,
60 driver_status: u8,
61 mmio_base: u64,
62 irq_num: u32,
63 config_generation: u32,
64}
65
66impl VirtioMmioDevice {
67 pub fn new(
69 mem: GuestMemory,
70 device: Box<dyn VirtioDevice>,
71 async_intr_status: bool,
72 ) -> Result<Self> {
73 let mut queue_evts = Vec::new();
74 for _ in device.queue_max_sizes() {
75 queue_evts.push(Event::new()?)
76 }
77 let queues = device
78 .queue_max_sizes()
79 .iter()
80 .map(|&s| QueueConfig::new(s, device.features()))
81 .collect();
82
83 Ok(VirtioMmioDevice {
84 device,
85 device_activated: false,
86 interrupt: None,
87 interrupt_evt: None,
88 async_intr_status,
89 queues,
90 queue_evts,
91 mem,
92 device_feature_select: 0,
93 driver_feature_select: 0,
94 queue_select: 0,
95 driver_status: 0,
96 mmio_base: 0,
97 irq_num: 0,
98 config_generation: 0,
99 })
100 }
101 pub fn ioevents(&self) -> Vec<(&Event, u64, Datamatch)> {
102 self.queue_evts
103 .iter()
104 .enumerate()
105 .map(|(i, event)| {
106 (
107 event,
108 self.mmio_base + VIRTIO_MMIO_QUEUE_NOTIFY as u64,
109 Datamatch::U32(Some(i.try_into().unwrap())),
110 )
111 })
112 .collect()
113 }
114
115 fn is_driver_ready(&self) -> bool {
116 let ready_bits = (VIRTIO_CONFIG_S_ACKNOWLEDGE
117 | VIRTIO_CONFIG_S_DRIVER
118 | VIRTIO_CONFIG_S_DRIVER_OK
119 | VIRTIO_CONFIG_S_FEATURES_OK) as u8;
120 self.driver_status == ready_bits && self.driver_status & VIRTIO_CONFIG_S_FAILED as u8 == 0
121 }
122
123 fn is_reset_requested(&self) -> bool {
125 self.driver_status == DEVICE_RESET as u8
126 }
127
128 fn device_type(&self) -> u32 {
129 self.device.device_type() as u32
130 }
131
132 fn activate(&mut self) -> anyhow::Result<()> {
134 let interrupt_evt = if let Some(ref evt) = self.interrupt_evt {
135 evt.try_clone()
136 .with_context(|| format!("{} failed to clone interrupt_evt", self.debug_label()))?
137 } else {
138 return Err(anyhow!("{} interrupt_evt is none", self.debug_label()));
139 };
140
141 let mem = self.mem.clone();
142 let interrupt = Interrupt::new_mmio(interrupt_evt, self.async_intr_status);
143 self.interrupt = Some(interrupt.clone());
144
145 let queues = self
147 .queues
148 .iter_mut()
149 .zip(self.queue_evts.iter())
150 .enumerate()
151 .filter(|(_, (q, _))| q.ready())
152 .map(|(queue_index, (queue, evt))| {
153 let queue_evt = evt.try_clone().context("failed to clone queue_evt")?;
154 Ok((
155 queue_index,
156 queue
157 .activate(&mem, queue_evt, interrupt.clone())
158 .context("failed to activate queue")?,
159 ))
160 })
161 .collect::<anyhow::Result<BTreeMap<usize, Queue>>>()?;
162
163 if let Err(e) = self.device.activate(mem, interrupt, queues) {
164 error!("{} activate failed: {:#}", self.debug_label(), e);
165 self.driver_status |= VIRTIO_CONFIG_S_NEEDS_RESET as u8;
166 } else {
167 self.device_activated = true;
168 }
169
170 Ok(())
171 }
172
173 fn read_mmio(&self, info: BusAccessInfo, data: &mut [u8]) {
174 if data.len() != std::mem::size_of::<u32>() {
175 warn!(
176 "{}: unsupported read length {}, only support 4 bytes read",
177 self.debug_label(),
178 data.len()
179 );
180 return;
181 }
182
183 if info.offset >= VIRTIO_MMIO_CONFIG as u64 {
184 self.device
185 .read_config(info.offset - VIRTIO_MMIO_CONFIG as u64, data);
186 return;
187 }
188
189 let val = match info.offset as u32 {
190 VIRTIO_MMIO_MAGIC_VALUE => VIRT_MAGIC,
191 VIRTIO_MMIO_VERSION => VIRT_VERSION.into(), VIRTIO_MMIO_DEVICE_ID => self.device_type(),
193 VIRTIO_MMIO_VENDOR_ID => VIRT_VENDOR,
194 VIRTIO_MMIO_DEVICE_FEATURES => {
195 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
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 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 {
269 let features: u64 = (val as u64) << (self.driver_feature_select * 32);
270 self.device.ack_features(features);
271 for queue in self.queues.iter_mut() {
272 queue.ack_features(features);
273 }
274 } else {
275 if val != 0 {
278 warn!(
279 "invalid ack_features (page {}, value 0x{:x})",
280 self.driver_feature_select, val
281 );
282 }
283 }
284 }
285 VIRTIO_MMIO_GUEST_PAGE_SIZE => warn!(
286 "{}: write to legacy register {}, in non-legacy mode",
287 self.debug_label(),
288 info.offset,
289 ),
290 VIRTIO_MMIO_QUEUE_SEL => self.queue_select = val as u16,
291 VIRTIO_MMIO_QUEUE_NUM => self.with_queue_mut(|q| q.set_size(val as u16)),
292 VIRTIO_MMIO_QUEUE_ALIGN => warn!(
293 "{}: write to legacy register {}, in non-legacy mode",
294 self.debug_label(),
295 info.offset,
296 ),
297 VIRTIO_MMIO_QUEUE_PFN => warn!(
298 "{}: write to legacy register {}, in non-legacy mode",
299 self.debug_label(),
300 info.offset,
301 ),
302 VIRTIO_MMIO_QUEUE_READY => self.with_queue_mut(|q| q.set_ready(val == 1)),
303 VIRTIO_MMIO_QUEUE_NOTIFY => {} VIRTIO_MMIO_INTERRUPT_ACK => {
305 if let Some(interrupt) = &self.interrupt {
306 interrupt.clear_interrupt_status_bits(val as u8)
307 }
308 }
309 VIRTIO_MMIO_STATUS => self.driver_status = val as u8,
310 VIRTIO_MMIO_QUEUE_DESC_LOW => {
311 self.with_queue_mut(|q| lo!(q, desc_table, set_desc_table, val))
312 }
313 VIRTIO_MMIO_QUEUE_DESC_HIGH => {
314 self.with_queue_mut(|q| hi!(q, desc_table, set_desc_table, val))
315 }
316 VIRTIO_MMIO_QUEUE_AVAIL_LOW => {
317 self.with_queue_mut(|q| lo!(q, avail_ring, set_avail_ring, val))
318 }
319 VIRTIO_MMIO_QUEUE_AVAIL_HIGH => {
320 self.with_queue_mut(|q| hi!(q, avail_ring, set_avail_ring, val))
321 }
322 VIRTIO_MMIO_QUEUE_USED_LOW => {
323 self.with_queue_mut(|q| lo!(q, used_ring, set_used_ring, val))
324 }
325 VIRTIO_MMIO_QUEUE_USED_HIGH => {
326 self.with_queue_mut(|q| hi!(q, used_ring, set_used_ring, val))
327 }
328 _ => {
329 warn!("{}: unsupported write address {}", self.debug_label(), info);
330 return;
331 }
332 };
333
334 if !self.device_activated && self.is_driver_ready() {
335 if let Err(e) = self.activate() {
336 error!("failed to activate device: {:#}", e);
337 }
338 }
339
340 if self.device_activated && self.is_reset_requested() {
342 if let Err(e) = self.device.reset() {
343 error!("failed to reset {} device: {:#}", self.debug_label(), e);
344 } else {
345 self.device_activated = false;
346 self.queues.iter_mut().for_each(QueueConfig::reset);
348 self.queue_select = 0;
350 self.interrupt = None;
352 }
353 }
354 }
355
356 fn with_queue<U, F>(&self, f: F) -> Option<U>
357 where
358 F: FnOnce(&QueueConfig) -> U,
359 {
360 self.queues.get(self.queue_select as usize).map(f)
361 }
362
363 fn with_queue_mut<F>(&mut self, f: F)
364 where
365 F: FnOnce(&mut QueueConfig),
366 {
367 if let Some(queue) = self.queues.get_mut(self.queue_select as usize) {
368 f(queue);
369 }
370 }
371
372 pub fn allocate_regions(
373 &mut self,
374 resources: &mut SystemAllocator,
375 ) -> std::result::Result<Vec<(u64, u64)>, resources::Error> {
376 let mut ranges = Vec::new();
377 let alloc_id = resources.get_anon_alloc();
378 let start_addr = resources.allocate_mmio(
379 VIRTIO_MMIO_REGION_SZ,
380 alloc_id,
381 "virtio_mmio".to_string(),
382 AllocOptions::new().align(pagesize() as u64),
383 )?;
384 self.mmio_base = start_addr;
385 ranges.push((start_addr, VIRTIO_MMIO_REGION_SZ));
386 Ok(ranges)
387 }
388
389 pub fn assign_irq(&mut self, irq_evt: &IrqEdgeEvent, irq_num: u32) {
390 self.interrupt_evt = Some(irq_evt.try_clone().unwrap());
391 self.irq_num = irq_num;
392 }
393
394 pub fn keep_rds(&self) -> Vec<RawDescriptor> {
395 let mut rds = self.device.keep_rds();
396 if let Some(interrupt_evt) = &self.interrupt_evt {
397 rds.extend(interrupt_evt.as_raw_descriptors());
398 }
399 rds
400 }
401
402 fn on_device_sandboxed(&mut self) {
403 self.device.on_device_sandboxed();
404 }
405}
406
407impl Aml for VirtioMmioDevice {
408 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
409 aml::Device::new(
410 "VIOM".into(),
411 vec![
412 &aml::Name::new("_HID".into(), &"LNRO0005"),
413 &aml::Name::new(
414 "_CRS".into(),
415 &aml::ResourceTemplate::new(vec![
416 &aml::AddressSpace::new_memory(
417 aml::AddressSpaceCachable::NotCacheable,
418 true,
419 self.mmio_base,
420 self.mmio_base + VIRTIO_MMIO_REGION_SZ - 1,
421 ),
422 &aml::Interrupt::new(true, true, false, false, self.irq_num),
423 ]),
424 ),
425 ],
426 )
427 .to_aml_bytes(bytes);
428 }
429}
430
431impl BusDeviceObj for VirtioMmioDevice {}
432
433impl BusDevice for VirtioMmioDevice {
434 fn debug_label(&self) -> String {
435 format!("mmio{}", self.device.debug_label())
436 }
437
438 fn device_id(&self) -> DeviceId {
439 PlatformDeviceId::VirtioMmio.into()
440 }
441
442 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
443 self.read_mmio(info, data)
444 }
445
446 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
447 self.write_mmio(info, data)
448 }
449
450 fn on_sandboxed(&mut self) {
451 self.on_device_sandboxed();
452 }
453}
454
455impl Suspendable for VirtioMmioDevice {}