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 30 const PVPANIC_PANICKED: u8 = 1 << 0; 31 const PVPANIC_CRASH_LOADED: u8 = 1 << 1; 32 33 #[derive(Debug, Error)] 34 pub enum PvPanicError { 35 #[error("Failed creating PvPanicDevice: {0}")] 36 CreatePvPanicDevice(#[source] anyhow::Error), 37 #[error("Failed to retrieve PciConfigurationState: {0}")] 38 RetrievePciConfigurationState(#[source] anyhow::Error), 39 } 40 41 #[derive(Copy, Clone)] 42 enum PvPanicSubclass { 43 Other = 0x80, 44 } 45 46 impl PciSubclass for PvPanicSubclass { 47 fn get_register_value(&self) -> u8 { 48 *self as u8 49 } 50 } 51 52 /// A device for handling guest panic event 53 pub struct PvPanicDevice { 54 id: String, 55 events: u8, 56 57 // PCI configuration registers. 58 configuration: PciConfiguration, 59 bar_regions: Vec<PciBarConfiguration>, 60 } 61 62 #[derive(Versionize)] 63 pub struct PvPanicDeviceState { 64 events: u8, 65 } 66 67 impl VersionMapped for PvPanicDeviceState {} 68 69 impl PvPanicDevice { 70 pub fn new(id: String, snapshot: Option<Snapshot>) -> Result<Self, PvPanicError> { 71 let pci_configuration_state = 72 vm_migration::versioned_state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID) 73 .map_err(|e| { 74 PvPanicError::RetrievePciConfigurationState(anyhow!( 75 "Failed to get PciConfigurationState from Snapshot: {}", 76 e 77 )) 78 })?; 79 80 let mut configuration = PciConfiguration::new( 81 PVPANIC_VENDOR_ID, 82 PVPANIC_DEVICE_ID, 83 0x1, // modern pci devices 84 PciClassCode::BaseSystemPeripheral, 85 &PvPanicSubclass::Other, 86 None, 87 PciHeaderType::Device, 88 0, 89 0, 90 None, 91 pci_configuration_state, 92 ); 93 94 let command: [u8; 2] = [0x03, 0x01]; 95 configuration.write_config_register(1, 0, &command); 96 97 let state: Option<PvPanicDeviceState> = snapshot 98 .as_ref() 99 .map(|s| s.to_versioned_state()) 100 .transpose() 101 .map_err(|e| { 102 PvPanicError::CreatePvPanicDevice(anyhow!( 103 "Failed to get PvPanicDeviceState from Snapshot: {}", 104 e 105 )) 106 })?; 107 let events = if let Some(state) = state { 108 state.events 109 } else { 110 PVPANIC_PANICKED | PVPANIC_CRASH_LOADED 111 }; 112 113 let pvpanic_device = PvPanicDevice { 114 id, 115 events, 116 configuration, 117 bar_regions: vec![], 118 }; 119 120 Ok(pvpanic_device) 121 } 122 123 pub fn event_to_string(&self, event: u8) -> String { 124 if event == PVPANIC_PANICKED { 125 "panic".to_string() 126 } else if event == PVPANIC_CRASH_LOADED { 127 "crash_loaded".to_string() 128 } else { 129 "unknown_event".to_string() 130 } 131 } 132 133 fn state(&self) -> PvPanicDeviceState { 134 PvPanicDeviceState { 135 events: self.events, 136 } 137 } 138 139 pub fn config_bar_addr(&self) -> u64 { 140 self.configuration.get_bar_addr(0) 141 } 142 } 143 144 impl BusDevice for PvPanicDevice { 145 fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) { 146 self.read_bar(base, offset, data) 147 } 148 149 fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 150 let event = self.event_to_string(data[0]); 151 info!("pvpanic got guest event {}", event); 152 event!("guest", "panic", "event", &event); 153 None 154 } 155 } 156 157 impl PciDevice for PvPanicDevice { 158 fn write_config_register( 159 &mut self, 160 reg_idx: usize, 161 offset: u64, 162 data: &[u8], 163 ) -> Option<Arc<Barrier>> { 164 self.configuration 165 .write_config_register(reg_idx, offset, data); 166 None 167 } 168 169 fn read_config_register(&mut self, reg_idx: usize) -> u32 { 170 self.configuration.read_reg(reg_idx) 171 } 172 173 fn detect_bar_reprogramming( 174 &mut self, 175 reg_idx: usize, 176 data: &[u8], 177 ) -> Option<BarReprogrammingParams> { 178 self.configuration.detect_bar_reprogramming(reg_idx, data) 179 } 180 181 fn allocate_bars( 182 &mut self, 183 allocator: &Arc<Mutex<SystemAllocator>>, 184 _mmio_allocator: &mut AddressAllocator, 185 resources: Option<Vec<Resource>>, 186 ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> { 187 let mut bars = Vec::new(); 188 let region_type = PciBarRegionType::Memory32BitRegion; 189 let bar_id = 0; 190 let region_size = PVPANIC_DEVICE_MMIO_SIZE; 191 let restoring = resources.is_some(); 192 let bar_addr = allocator 193 .lock() 194 .unwrap() 195 .allocate_mmio_hole_addresses(None, region_size, None) 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 _mmio_allocator: &mut AddressAllocator, 222 ) -> std::result::Result<(), PciDeviceError> { 223 for bar in self.bar_regions.drain(..) { 224 allocator.free_mmio_hole_addresses(GuestAddress(bar.addr()), bar.size()); 225 } 226 227 Ok(()) 228 } 229 230 fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { 231 for bar in self.bar_regions.iter_mut() { 232 if bar.addr() == old_base { 233 *bar = bar.set_address(new_base); 234 } 235 } 236 237 Ok(()) 238 } 239 240 fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 241 data[0] = self.events; 242 } 243 244 fn as_any(&mut self) -> &mut dyn Any { 245 self 246 } 247 248 fn id(&self) -> Option<String> { 249 Some(self.id.clone()) 250 } 251 } 252 253 impl Pausable for PvPanicDevice {} 254 255 impl Snapshottable for PvPanicDevice { 256 fn id(&self) -> String { 257 self.id.clone() 258 } 259 260 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 261 let mut snapshot = Snapshot::new_from_versioned_state(&self.state())?; 262 263 // Snapshot PciConfiguration 264 snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); 265 266 Ok(snapshot) 267 } 268 } 269 270 impl Transportable for PvPanicDevice {} 271 impl Migratable for PvPanicDevice {} 272