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: {0}")] 35 CreatePvPanicDevice(#[source] anyhow::Error), 36 #[error("Failed to retrieve PciConfigurationState: {0}")] 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 configuration.write_config_register(1, 0, &command); 92 93 let state: Option<PvPanicDeviceState> = snapshot 94 .as_ref() 95 .map(|s| s.to_state()) 96 .transpose() 97 .map_err(|e| { 98 PvPanicError::CreatePvPanicDevice(anyhow!( 99 "Failed to get PvPanicDeviceState from Snapshot: {}", 100 e 101 )) 102 })?; 103 let events = if let Some(state) = state { 104 state.events 105 } else { 106 PVPANIC_PANICKED | PVPANIC_CRASH_LOADED 107 }; 108 109 let pvpanic_device = PvPanicDevice { 110 id, 111 events, 112 configuration, 113 bar_regions: vec![], 114 }; 115 116 Ok(pvpanic_device) 117 } 118 119 pub fn event_to_string(&self, event: u8) -> String { 120 if event == PVPANIC_PANICKED { 121 "panic".to_string() 122 } else if event == PVPANIC_CRASH_LOADED { 123 "crash_loaded".to_string() 124 } else { 125 "unknown_event".to_string() 126 } 127 } 128 129 fn state(&self) -> PvPanicDeviceState { 130 PvPanicDeviceState { 131 events: self.events, 132 } 133 } 134 135 pub fn config_bar_addr(&self) -> u64 { 136 self.configuration.get_bar_addr(0) 137 } 138 } 139 140 impl BusDevice for PvPanicDevice { 141 fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) { 142 self.read_bar(base, offset, data) 143 } 144 145 fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 146 let event = self.event_to_string(data[0]); 147 info!("pvpanic got guest event {}", event); 148 event!("guest", "panic", "event", &event); 149 None 150 } 151 } 152 153 impl PciDevice for PvPanicDevice { 154 fn write_config_register( 155 &mut self, 156 reg_idx: usize, 157 offset: u64, 158 data: &[u8], 159 ) -> Option<Arc<Barrier>> { 160 self.configuration 161 .write_config_register(reg_idx, offset, data); 162 None 163 } 164 165 fn read_config_register(&mut self, reg_idx: usize) -> u32 { 166 self.configuration.read_reg(reg_idx) 167 } 168 169 fn detect_bar_reprogramming( 170 &mut self, 171 reg_idx: usize, 172 data: &[u8], 173 ) -> Option<BarReprogrammingParams> { 174 self.configuration.detect_bar_reprogramming(reg_idx, data) 175 } 176 177 fn allocate_bars( 178 &mut self, 179 _allocator: &Arc<Mutex<SystemAllocator>>, 180 mmio32_allocator: &mut AddressAllocator, 181 _mmio64_allocator: &mut AddressAllocator, 182 resources: Option<Vec<Resource>>, 183 ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> { 184 let mut bars = Vec::new(); 185 let region_type = PciBarRegionType::Memory32BitRegion; 186 let bar_id = 0; 187 let region_size = PVPANIC_DEVICE_MMIO_SIZE; 188 let restoring = resources.is_some(); 189 let bar_addr = mmio32_allocator 190 .allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT)) 191 .ok_or(PciDeviceError::IoAllocationFailed(region_size))?; 192 193 let bar = PciBarConfiguration::default() 194 .set_index(bar_id as usize) 195 .set_address(bar_addr.raw_value()) 196 .set_size(region_size) 197 .set_region_type(region_type) 198 .set_prefetchable(PciBarPrefetchable::NotPrefetchable); 199 200 debug!("pvpanic bar address 0x{:x}", bar_addr.0); 201 if !restoring { 202 self.configuration 203 .add_pci_bar(&bar) 204 .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; 205 } 206 207 bars.push(bar); 208 self.bar_regions.clone_from(&bars); 209 210 Ok(bars) 211 } 212 213 fn free_bars( 214 &mut self, 215 _allocator: &mut SystemAllocator, 216 mmio32_allocator: &mut AddressAllocator, 217 _mmio64_allocator: &mut AddressAllocator, 218 ) -> std::result::Result<(), PciDeviceError> { 219 for bar in self.bar_regions.drain(..) { 220 mmio32_allocator.free(GuestAddress(bar.addr()), bar.size()); 221 } 222 223 Ok(()) 224 } 225 226 fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { 227 for bar in self.bar_regions.iter_mut() { 228 if bar.addr() == old_base { 229 *bar = bar.set_address(new_base); 230 } 231 } 232 233 Ok(()) 234 } 235 236 fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 237 data[0] = self.events; 238 } 239 240 fn as_any_mut(&mut self) -> &mut dyn Any { 241 self 242 } 243 244 fn id(&self) -> Option<String> { 245 Some(self.id.clone()) 246 } 247 } 248 249 impl Pausable for PvPanicDevice {} 250 251 impl Snapshottable for PvPanicDevice { 252 fn id(&self) -> String { 253 self.id.clone() 254 } 255 256 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 257 let mut snapshot = Snapshot::new_from_state(&self.state())?; 258 259 // Snapshot PciConfiguration 260 snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); 261 262 Ok(snapshot) 263 } 264 } 265 266 impl Transportable for PvPanicDevice {} 267 impl Migratable for PvPanicDevice {} 268