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 //! pvpanic is a simulated device, through which a guest panic event is sent to a VMM. 6 //! This was initially developed for qemu with linux in-tree drivers and opensource 7 //! driver for windows also exist now. 8 //! <https://fossies.org/linux/qemu/docs/specs/pvpanic.txt> 9 //! 10 //! This implementation emulates pci interface for pvpanic virtual device. 11 12 // TODO(218575411): Support pvpanic on windows crosvm. 13 #![cfg_attr(windows, allow(dead_code))] 14 15 use std::fmt; 16 17 use anyhow::Context; 18 use base::error; 19 use base::RawDescriptor; 20 use base::SendTube; 21 use base::SharedMemory; 22 use base::VmEventType; 23 use resources::Alloc; 24 use resources::AllocOptions; 25 use resources::SystemAllocator; 26 use snapshot::AnySnapshot; 27 28 use crate::pci::pci_configuration::PciBarConfiguration; 29 use crate::pci::pci_configuration::PciBarPrefetchable; 30 use crate::pci::pci_configuration::PciBarRegionType; 31 use crate::pci::pci_configuration::PciClassCode; 32 use crate::pci::pci_configuration::PciConfiguration; 33 use crate::pci::pci_configuration::PciHeaderType; 34 use crate::pci::pci_configuration::PciOtherSubclass; 35 use crate::pci::pci_device; 36 use crate::pci::pci_device::BarRange; 37 use crate::pci::pci_device::PciDevice; 38 use crate::pci::pci_device::Result; 39 use crate::pci::PciAddress; 40 use crate::pci::PciBarIndex; 41 use crate::pci::PciDeviceError; 42 use crate::pci::PCI_VENDOR_ID_REDHAT; 43 use crate::Suspendable; 44 45 const PCI_DEVICE_ID_REDHAT_PVPANIC: u16 = 0x0011; 46 const PCI_PVPANIC_REVISION_ID: u8 = 1; 47 48 const PVPANIC_BAR_INDEX: PciBarIndex = 0; 49 const PVPANIC_REG_SIZE: u64 = 0x10; 50 51 // Guest panicked 52 pub const PVPANIC_PANICKED: u8 = 1 << 0; 53 // Guest kexeced crash kernel 54 pub const PVPANIC_CRASH_LOADED: u8 = 1 << 1; 55 56 const PVPANIC_CAPABILITIES: u8 = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED; 57 58 #[repr(u8)] 59 #[derive(PartialEq, Eq)] 60 pub enum PvPanicCode { 61 Panicked = PVPANIC_PANICKED, 62 CrashLoaded = PVPANIC_CRASH_LOADED, 63 Unknown = 0xFF, 64 } 65 66 impl PvPanicCode { from_u8(val: u8) -> PvPanicCode67 pub fn from_u8(val: u8) -> PvPanicCode { 68 match val { 69 PVPANIC_PANICKED => PvPanicCode::Panicked, 70 PVPANIC_CRASH_LOADED => PvPanicCode::CrashLoaded, 71 _ => PvPanicCode::Unknown, 72 } 73 } 74 } 75 76 impl fmt::Display for PvPanicCode { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 78 match self { 79 PvPanicCode::Panicked => write!(f, "Guest panicked"), 80 PvPanicCode::CrashLoaded => write!(f, "Guest panicked and crash kernel loaded"), 81 PvPanicCode::Unknown => write!(f, "Guest panicked with unknown code"), 82 } 83 } 84 } 85 86 pub struct PvPanicPciDevice { 87 pci_address: Option<PciAddress>, 88 config_regs: PciConfiguration, 89 evt_wrtube: SendTube, 90 } 91 92 impl PvPanicPciDevice { new(evt_wrtube: SendTube) -> PvPanicPciDevice93 pub fn new(evt_wrtube: SendTube) -> PvPanicPciDevice { 94 let config_regs = PciConfiguration::new( 95 PCI_VENDOR_ID_REDHAT, 96 PCI_DEVICE_ID_REDHAT_PVPANIC, 97 PciClassCode::Other, 98 &PciOtherSubclass::Other, 99 None, 100 PciHeaderType::Device, 101 0xFF, 102 0xFF, 103 PCI_PVPANIC_REVISION_ID, 104 ); 105 106 Self { 107 pci_address: None, 108 config_regs, 109 evt_wrtube, 110 } 111 } 112 } 113 114 impl PciDevice for PvPanicPciDevice { debug_label(&self) -> String115 fn debug_label(&self) -> String { 116 "PvPanic".to_owned() 117 } 118 allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>119 fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> { 120 if self.pci_address.is_none() { 121 self.pci_address = resources.allocate_pci(0, self.debug_label()); 122 } 123 self.pci_address.ok_or(PciDeviceError::PciAllocationFailed) 124 } 125 allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>>126 fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>> { 127 let address = self 128 .pci_address 129 .expect("allocate_address must be called prior to allocate_io_bars"); 130 let mut ranges: Vec<BarRange> = Vec::new(); 131 let pvpanic_reg_addr = resources 132 .allocate_mmio( 133 PVPANIC_REG_SIZE, 134 Alloc::PciBar { 135 bus: address.bus, 136 dev: address.dev, 137 func: address.func, 138 bar: PVPANIC_BAR_INDEX as u8, 139 }, 140 "pvpanic_reg".to_string(), 141 AllocOptions::new() 142 .max_address(u32::MAX.into()) 143 .align(PVPANIC_REG_SIZE), 144 ) 145 .map_err(|e| pci_device::Error::IoAllocationFailed(PVPANIC_REG_SIZE, e))?; 146 let pvpanic_config = PciBarConfiguration::new( 147 PVPANIC_BAR_INDEX, 148 PVPANIC_REG_SIZE, 149 PciBarRegionType::Memory32BitRegion, 150 PciBarPrefetchable::NotPrefetchable, 151 ) 152 .set_address(pvpanic_reg_addr); 153 self.config_regs 154 .add_pci_bar(pvpanic_config) 155 .map_err(|e| pci_device::Error::IoRegistrationFailed(pvpanic_reg_addr, e))?; 156 ranges.push(BarRange { 157 addr: pvpanic_reg_addr, 158 size: PVPANIC_REG_SIZE, 159 prefetchable: false, 160 }); 161 162 Ok(ranges) 163 } 164 keep_rds(&self) -> Vec<RawDescriptor>165 fn keep_rds(&self) -> Vec<RawDescriptor> { 166 Vec::new() 167 } 168 get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>169 fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> { 170 self.config_regs.get_bar_configuration(bar_num) 171 } 172 read_config_register(&self, reg_idx: usize) -> u32173 fn read_config_register(&self, reg_idx: usize) -> u32 { 174 self.config_regs.read_reg(reg_idx) 175 } 176 write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])177 fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) { 178 self.config_regs.write_reg(reg_idx, offset, data); 179 } 180 setup_pci_config_mapping( &mut self, shmem: &SharedMemory, base: usize, len: usize, ) -> Result<bool>181 fn setup_pci_config_mapping( 182 &mut self, 183 shmem: &SharedMemory, 184 base: usize, 185 len: usize, 186 ) -> Result<bool> { 187 self.config_regs 188 .setup_mapping(shmem, base, len) 189 .map(|_| true) 190 .map_err(PciDeviceError::MmioSetup) 191 } 192 read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8])193 fn read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8]) { 194 data[0] = if bar_index == PVPANIC_BAR_INDEX && offset == 0 && data.len() == 1 { 195 PVPANIC_CAPABILITIES 196 } else { 197 0 198 }; 199 } 200 write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8])201 fn write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8]) { 202 if bar_index != PVPANIC_BAR_INDEX || offset != 0 || data.len() != 1 { 203 return; 204 } 205 206 if let Err(e) = self 207 .evt_wrtube 208 .send::<VmEventType>(&VmEventType::Panic(data[0])) 209 { 210 error!("Failed to write to the event tube: {}", e); 211 } 212 } 213 } 214 215 impl Suspendable for PvPanicPciDevice { snapshot(&mut self) -> anyhow::Result<AnySnapshot>216 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> { 217 self.config_regs 218 .snapshot() 219 .context("failed to serialize PvPanicPciDevice") 220 } 221 restore(&mut self, data: AnySnapshot) -> anyhow::Result<()>222 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> { 223 self.config_regs 224 .restore(data) 225 .context("failed to deserialize PvPanicPciDevice") 226 } 227 sleep(&mut self) -> anyhow::Result<()>228 fn sleep(&mut self) -> anyhow::Result<()> { 229 Ok(()) 230 } 231 wake(&mut self) -> anyhow::Result<()>232 fn wake(&mut self) -> anyhow::Result<()> { 233 Ok(()) 234 } 235 } 236 237 #[cfg(test)] 238 mod test { 239 use base::Tube; 240 use resources::AddressRange; 241 use resources::SystemAllocator; 242 use resources::SystemAllocatorConfig; 243 244 use super::*; 245 246 #[test] pvpanic_read_write()247 fn pvpanic_read_write() { 248 let mut allocator = SystemAllocator::new( 249 SystemAllocatorConfig { 250 io: Some(AddressRange { 251 start: 0x1000, 252 end: 0xffff, 253 }), 254 low_mmio: AddressRange { 255 start: 0x2000_0000, 256 end: 0x2fffffff, 257 }, 258 high_mmio: AddressRange { 259 start: 0x1_0000_0000, 260 end: 0x1_0fff_ffff, 261 }, 262 platform_mmio: None, 263 first_irq: 5, 264 }, 265 None, 266 &[], 267 ) 268 .unwrap(); 269 270 let (evt_wrtube, evt_rdtube) = Tube::directional_pair().unwrap(); 271 let mut device = PvPanicPciDevice::new(evt_wrtube); 272 273 assert!(device.allocate_address(&mut allocator).is_ok()); 274 assert!(device.allocate_io_bars(&mut allocator).is_ok()); 275 276 let mut data: [u8; 1] = [0; 1]; 277 278 // Read from an invalid addr 279 device.read_bar(0, 1, &mut data); 280 assert_eq!(data[0], 0); 281 282 // Read from the valid addr 283 device.read_bar(0, 0, &mut data); 284 assert_eq!(data[0], PVPANIC_CAPABILITIES); 285 286 // Write to the valid addr. 287 data[0] = PVPANIC_CRASH_LOADED; 288 device.write_bar(0, 0, &data); 289 290 // Verify the event 291 let val = evt_rdtube.recv::<VmEventType>().unwrap(); 292 assert_eq!(val, VmEventType::Panic(PVPANIC_CRASH_LOADED)); 293 } 294 } 295