1 // Copyright © 2023 Tencent Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 use std::any::Any; 7 use std::result; 8 use std::sync::{Arc, Barrier, Mutex}; 9 10 use anyhow::anyhow; 11 use pci::{ 12 BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, 13 PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass, 14 PCI_CONFIGURATION_ID, 15 }; 16 use serde::{Deserialize, Serialize}; 17 use thiserror::Error; 18 use vm_allocator::{AddressAllocator, SystemAllocator}; 19 use vm_device::{BusDevice, Resource}; 20 use vm_memory::{Address, GuestAddress}; 21 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; 22 23 const PVPANIC_VENDOR_ID: u16 = 0x1b36; 24 const PVPANIC_DEVICE_ID: u16 = 0x0011; 25 26 pub const PVPANIC_DEVICE_MMIO_SIZE: u64 = 0x2; 27 pub const PVPANIC_DEVICE_MMIO_ALIGNMENT: u64 = 0x10; 28 29 const PVPANIC_PANICKED: u8 = 1 << 0; 30 const PVPANIC_CRASH_LOADED: u8 = 1 << 1; 31 32 #[derive(Debug, Error)] 33 pub enum PvPanicError { 34 #[error("Failed creating PvPanicDevice")] 35 CreatePvPanicDevice(#[source] anyhow::Error), 36 #[error("Failed to retrieve PciConfigurationState")] 37 RetrievePciConfigurationState(#[source] anyhow::Error), 38 } 39 40 #[derive(Copy, Clone)] 41 enum PvPanicSubclass { 42 Other = 0x80, 43 } 44 45 impl PciSubclass for PvPanicSubclass { 46 fn get_register_value(&self) -> u8 { 47 *self as u8 48 } 49 } 50 51 /// A device for handling guest panic event 52 pub struct PvPanicDevice { 53 id: String, 54 events: u8, 55 56 // PCI configuration registers. 57 configuration: PciConfiguration, 58 bar_regions: Vec<PciBarConfiguration>, 59 } 60 61 #[derive(Serialize, Deserialize)] 62 pub struct PvPanicDeviceState { 63 events: u8, 64 } 65 66 impl PvPanicDevice { 67 pub fn new(id: String, snapshot: Option<Snapshot>) -> Result<Self, PvPanicError> { 68 let pci_configuration_state = 69 vm_migration::state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID).map_err(|e| { 70 PvPanicError::RetrievePciConfigurationState(anyhow!( 71 "Failed to get PciConfigurationState from Snapshot: {}", 72 e 73 )) 74 })?; 75 76 let mut configuration = PciConfiguration::new( 77 PVPANIC_VENDOR_ID, 78 PVPANIC_DEVICE_ID, 79 0x1, // modern pci devices 80 PciClassCode::BaseSystemPeripheral, 81 &PvPanicSubclass::Other, 82 None, 83 PciHeaderType::Device, 84 0, 85 0, 86 None, 87 pci_configuration_state, 88 ); 89 90 let command: [u8; 2] = [0x03, 0x01]; 91 let bar_reprogram = configuration.write_config_register(1, 0, &command); 92 assert!( 93 bar_reprogram.is_empty(), 94 "No bar reprogrammig is expected from writing to the COMMAND register" 95 ); 96 97 let state: Option<PvPanicDeviceState> = snapshot 98 .as_ref() 99 .map(|s| s.to_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 ) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) { 164 ( 165 self.configuration 166 .write_config_register(reg_idx, offset, data), 167 None, 168 ) 169 } 170 171 fn read_config_register(&mut self, reg_idx: usize) -> u32 { 172 self.configuration.read_reg(reg_idx) 173 } 174 175 fn allocate_bars( 176 &mut self, 177 _allocator: &Arc<Mutex<SystemAllocator>>, 178 mmio32_allocator: &mut AddressAllocator, 179 _mmio64_allocator: &mut AddressAllocator, 180 resources: Option<Vec<Resource>>, 181 ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> { 182 let mut bars = Vec::new(); 183 let region_type = PciBarRegionType::Memory32BitRegion; 184 let bar_id = 0; 185 let region_size = PVPANIC_DEVICE_MMIO_SIZE; 186 let restoring = resources.is_some(); 187 let bar_addr = mmio32_allocator 188 .allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT)) 189 .ok_or(PciDeviceError::IoAllocationFailed(region_size))?; 190 191 let bar = PciBarConfiguration::default() 192 .set_index(bar_id as usize) 193 .set_address(bar_addr.raw_value()) 194 .set_size(region_size) 195 .set_region_type(region_type) 196 .set_prefetchable(PciBarPrefetchable::NotPrefetchable); 197 198 debug!("pvpanic bar address 0x{:x}", bar_addr.0); 199 if !restoring { 200 self.configuration 201 .add_pci_bar(&bar) 202 .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; 203 } 204 205 bars.push(bar); 206 self.bar_regions.clone_from(&bars); 207 208 Ok(bars) 209 } 210 211 fn free_bars( 212 &mut self, 213 _allocator: &mut SystemAllocator, 214 mmio32_allocator: &mut AddressAllocator, 215 _mmio64_allocator: &mut AddressAllocator, 216 ) -> std::result::Result<(), PciDeviceError> { 217 for bar in self.bar_regions.drain(..) { 218 mmio32_allocator.free(GuestAddress(bar.addr()), bar.size()); 219 } 220 221 Ok(()) 222 } 223 224 fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { 225 for bar in self.bar_regions.iter_mut() { 226 if bar.addr() == old_base { 227 *bar = bar.set_address(new_base); 228 } 229 } 230 231 Ok(()) 232 } 233 234 fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 235 data[0] = self.events; 236 } 237 238 fn as_any_mut(&mut self) -> &mut dyn Any { 239 self 240 } 241 242 fn id(&self) -> Option<String> { 243 Some(self.id.clone()) 244 } 245 } 246 247 impl Pausable for PvPanicDevice {} 248 249 impl Snapshottable for PvPanicDevice { 250 fn id(&self) -> String { 251 self.id.clone() 252 } 253 254 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 255 let mut snapshot = Snapshot::new_from_state(&self.state())?; 256 257 // Snapshot PciConfiguration 258 snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); 259 260 Ok(snapshot) 261 } 262 } 263 264 impl Transportable for PvPanicDevice {} 265 impl Migratable for PvPanicDevice {} 266