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