1 // Copyright © 2023 Tencent Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 use anyhow::anyhow; 7 use pci::{ 8 BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, 9 PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass, 10 PCI_CONFIGURATION_ID, 11 }; 12 use std::any::Any; 13 use std::result; 14 use std::sync::{Arc, Barrier, Mutex}; 15 use thiserror::Error; 16 use versionize::{VersionMap, Versionize, VersionizeResult}; 17 use versionize_derive::Versionize; 18 use vm_allocator::{AddressAllocator, SystemAllocator}; 19 use vm_device::{BusDevice, Resource}; 20 use vm_memory::{Address, GuestAddress}; 21 use vm_migration::{ 22 Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped, 23 }; 24 25 const PVPANIC_VENDOR_ID: u16 = 0x1b36; 26 const PVPANIC_DEVICE_ID: u16 = 0x0011; 27 28 pub const PVPANIC_DEVICE_MMIO_SIZE: u64 = 0x2; 29 pub const PVPANIC_DEVICE_MMIO_ALIGNMENT: u64 = 0x10; 30 31 const PVPANIC_PANICKED: u8 = 1 << 0; 32 const PVPANIC_CRASH_LOADED: u8 = 1 << 1; 33 34 #[derive(Debug, Error)] 35 pub enum PvPanicError { 36 #[error("Failed creating PvPanicDevice: {0}")] 37 CreatePvPanicDevice(#[source] anyhow::Error), 38 #[error("Failed to retrieve PciConfigurationState: {0}")] 39 RetrievePciConfigurationState(#[source] anyhow::Error), 40 } 41 42 #[derive(Copy, Clone)] 43 enum PvPanicSubclass { 44 Other = 0x80, 45 } 46 47 impl PciSubclass for PvPanicSubclass { 48 fn get_register_value(&self) -> u8 { 49 *self as u8 50 } 51 } 52 53 /// A device for handling guest panic event 54 pub struct PvPanicDevice { 55 id: String, 56 events: u8, 57 58 // PCI configuration registers. 59 configuration: PciConfiguration, 60 bar_regions: Vec<PciBarConfiguration>, 61 } 62 63 #[derive(Versionize)] 64 pub struct PvPanicDeviceState { 65 events: u8, 66 } 67 68 impl VersionMapped for PvPanicDeviceState {} 69 70 impl PvPanicDevice { 71 pub fn new(id: String, snapshot: Option<Snapshot>) -> Result<Self, PvPanicError> { 72 let pci_configuration_state = 73 vm_migration::versioned_state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID) 74 .map_err(|e| { 75 PvPanicError::RetrievePciConfigurationState(anyhow!( 76 "Failed to get PciConfigurationState from Snapshot: {}", 77 e 78 )) 79 })?; 80 81 let mut configuration = PciConfiguration::new( 82 PVPANIC_VENDOR_ID, 83 PVPANIC_DEVICE_ID, 84 0x1, // modern pci devices 85 PciClassCode::BaseSystemPeripheral, 86 &PvPanicSubclass::Other, 87 None, 88 PciHeaderType::Device, 89 0, 90 0, 91 None, 92 pci_configuration_state, 93 ); 94 95 let command: [u8; 2] = [0x03, 0x01]; 96 configuration.write_config_register(1, 0, &command); 97 98 let state: Option<PvPanicDeviceState> = snapshot 99 .as_ref() 100 .map(|s| s.to_versioned_state()) 101 .transpose() 102 .map_err(|e| { 103 PvPanicError::CreatePvPanicDevice(anyhow!( 104 "Failed to get PvPanicDeviceState from Snapshot: {}", 105 e 106 )) 107 })?; 108 let events = if let Some(state) = state { 109 state.events 110 } else { 111 PVPANIC_PANICKED | PVPANIC_CRASH_LOADED 112 }; 113 114 let pvpanic_device = PvPanicDevice { 115 id, 116 events, 117 configuration, 118 bar_regions: vec![], 119 }; 120 121 Ok(pvpanic_device) 122 } 123 124 pub fn event_to_string(&self, event: u8) -> String { 125 if event == PVPANIC_PANICKED { 126 "panic".to_string() 127 } else if event == PVPANIC_CRASH_LOADED { 128 "crash_loaded".to_string() 129 } else { 130 "unknown_event".to_string() 131 } 132 } 133 134 fn state(&self) -> PvPanicDeviceState { 135 PvPanicDeviceState { 136 events: self.events, 137 } 138 } 139 140 pub fn config_bar_addr(&self) -> u64 { 141 self.configuration.get_bar_addr(0) 142 } 143 } 144 145 impl BusDevice for PvPanicDevice { 146 fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) { 147 self.read_bar(base, offset, data) 148 } 149 150 fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 151 let event = self.event_to_string(data[0]); 152 info!("pvpanic got guest event {}", event); 153 event!("guest", "panic", "event", &event); 154 None 155 } 156 } 157 158 impl PciDevice for PvPanicDevice { 159 fn write_config_register( 160 &mut self, 161 reg_idx: usize, 162 offset: u64, 163 data: &[u8], 164 ) -> Option<Arc<Barrier>> { 165 self.configuration 166 .write_config_register(reg_idx, offset, data); 167 None 168 } 169 170 fn read_config_register(&mut self, reg_idx: usize) -> u32 { 171 self.configuration.read_reg(reg_idx) 172 } 173 174 fn detect_bar_reprogramming( 175 &mut self, 176 reg_idx: usize, 177 data: &[u8], 178 ) -> Option<BarReprogrammingParams> { 179 self.configuration.detect_bar_reprogramming(reg_idx, data) 180 } 181 182 fn allocate_bars( 183 &mut self, 184 _allocator: &Arc<Mutex<SystemAllocator>>, 185 mmio32_allocator: &mut AddressAllocator, 186 _mmio64_allocator: &mut AddressAllocator, 187 resources: Option<Vec<Resource>>, 188 ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> { 189 let mut bars = Vec::new(); 190 let region_type = PciBarRegionType::Memory32BitRegion; 191 let bar_id = 0; 192 let region_size = PVPANIC_DEVICE_MMIO_SIZE; 193 let restoring = resources.is_some(); 194 let bar_addr = mmio32_allocator 195 .allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT)) 196 .ok_or(PciDeviceError::IoAllocationFailed(region_size))?; 197 198 let bar = PciBarConfiguration::default() 199 .set_index(bar_id as usize) 200 .set_address(bar_addr.raw_value()) 201 .set_size(region_size) 202 .set_region_type(region_type) 203 .set_prefetchable(PciBarPrefetchable::NotPrefetchable); 204 205 debug!("pvpanic bar address 0x{:x}", bar_addr.0); 206 if !restoring { 207 self.configuration 208 .add_pci_bar(&bar) 209 .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; 210 } 211 212 bars.push(bar); 213 self.bar_regions = bars.clone(); 214 215 Ok(bars) 216 } 217 218 fn free_bars( 219 &mut self, 220 _allocator: &mut SystemAllocator, 221 mmio32_allocator: &mut AddressAllocator, 222 _mmio64_allocator: &mut AddressAllocator, 223 ) -> std::result::Result<(), PciDeviceError> { 224 for bar in self.bar_regions.drain(..) { 225 mmio32_allocator.free(GuestAddress(bar.addr()), bar.size()); 226 } 227 228 Ok(()) 229 } 230 231 fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { 232 for bar in self.bar_regions.iter_mut() { 233 if bar.addr() == old_base { 234 *bar = bar.set_address(new_base); 235 } 236 } 237 238 Ok(()) 239 } 240 241 fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 242 data[0] = self.events; 243 } 244 245 fn as_any(&mut self) -> &mut dyn Any { 246 self 247 } 248 249 fn id(&self) -> Option<String> { 250 Some(self.id.clone()) 251 } 252 } 253 254 impl Pausable for PvPanicDevice {} 255 256 impl Snapshottable for PvPanicDevice { 257 fn id(&self) -> String { 258 self.id.clone() 259 } 260 261 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 262 let mut snapshot = Snapshot::new_from_versioned_state(&self.state())?; 263 264 // Snapshot PciConfiguration 265 snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); 266 267 Ok(snapshot) 268 } 269 } 270 271 impl Transportable for PvPanicDevice {} 272 impl Migratable for PvPanicDevice {} 273