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