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