1ef67eab8SYi Wang // Copyright © 2023 Tencent Corporation 2ef67eab8SYi Wang // 3ef67eab8SYi Wang // SPDX-License-Identifier: Apache-2.0 4ef67eab8SYi Wang // 5ef67eab8SYi Wang 688a9f799SRob Bradford use std::any::Any; 788a9f799SRob Bradford use std::result; 888a9f799SRob Bradford use std::sync::{Arc, Barrier, Mutex}; 988a9f799SRob Bradford 108e9ce3abSYi Wang use anyhow::anyhow; 113acb988cSYi Wang use pci::{ 123acb988cSYi Wang BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, 133acb988cSYi Wang PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass, 148e9ce3abSYi Wang PCI_CONFIGURATION_ID, 153acb988cSYi Wang }; 1610ab87d6SRob Bradford use serde::{Deserialize, Serialize}; 178e9ce3abSYi Wang use thiserror::Error; 183acb988cSYi Wang use vm_allocator::{AddressAllocator, SystemAllocator}; 193acb988cSYi Wang use vm_device::{BusDevice, Resource}; 203acb988cSYi Wang use vm_memory::{Address, GuestAddress}; 2110ab87d6SRob Bradford use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; 223acb988cSYi Wang 233acb988cSYi Wang const PVPANIC_VENDOR_ID: u16 = 0x1b36; 243acb988cSYi Wang const PVPANIC_DEVICE_ID: u16 = 0x0011; 253acb988cSYi Wang 263acb988cSYi Wang pub const PVPANIC_DEVICE_MMIO_SIZE: u64 = 0x2; 275f3ff3c4SThomas Barrett pub const PVPANIC_DEVICE_MMIO_ALIGNMENT: u64 = 0x10; 283acb988cSYi Wang 29ef67eab8SYi Wang const PVPANIC_PANICKED: u8 = 1 << 0; 30ef67eab8SYi Wang const PVPANIC_CRASH_LOADED: u8 = 1 << 1; 31ef67eab8SYi Wang 328e9ce3abSYi Wang #[derive(Debug, Error)] 338e9ce3abSYi Wang pub enum PvPanicError { 34*1b91aa8eSPhilipp Schuster #[error("Failed creating PvPanicDevice")] 358e9ce3abSYi Wang CreatePvPanicDevice(#[source] anyhow::Error), 36*1b91aa8eSPhilipp Schuster #[error("Failed to retrieve PciConfigurationState")] 378e9ce3abSYi Wang RetrievePciConfigurationState(#[source] anyhow::Error), 388e9ce3abSYi Wang } 398e9ce3abSYi Wang 403acb988cSYi Wang #[derive(Copy, Clone)] 41cd7eecc0SRob Bradford enum PvPanicSubclass { 423acb988cSYi Wang Other = 0x80, 433acb988cSYi Wang } 443acb988cSYi Wang 453acb988cSYi Wang impl PciSubclass for PvPanicSubclass { get_register_value(&self) -> u8463acb988cSYi Wang fn get_register_value(&self) -> u8 { 473acb988cSYi Wang *self as u8 483acb988cSYi Wang } 493acb988cSYi Wang } 503acb988cSYi Wang 51ef67eab8SYi Wang /// A device for handling guest panic event 52ef67eab8SYi Wang pub struct PvPanicDevice { 533acb988cSYi Wang id: String, 543acb988cSYi Wang events: u8, 553acb988cSYi Wang 563acb988cSYi Wang // PCI configuration registers. 573acb988cSYi Wang configuration: PciConfiguration, 583acb988cSYi Wang bar_regions: Vec<PciBarConfiguration>, 59ef67eab8SYi Wang } 60ef67eab8SYi Wang 6110ab87d6SRob Bradford #[derive(Serialize, Deserialize)] 628e9ce3abSYi Wang pub struct PvPanicDeviceState { 638e9ce3abSYi Wang events: u8, 648e9ce3abSYi Wang } 658e9ce3abSYi Wang 66ef67eab8SYi Wang impl PvPanicDevice { new(id: String, snapshot: Option<Snapshot>) -> Result<Self, PvPanicError>678e9ce3abSYi Wang pub fn new(id: String, snapshot: Option<Snapshot>) -> Result<Self, PvPanicError> { 688e9ce3abSYi Wang let pci_configuration_state = 6910ab87d6SRob Bradford vm_migration::state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID).map_err(|e| { 708e9ce3abSYi Wang PvPanicError::RetrievePciConfigurationState(anyhow!( 718e9ce3abSYi Wang "Failed to get PciConfigurationState from Snapshot: {}", 728e9ce3abSYi Wang e 738e9ce3abSYi Wang )) 748e9ce3abSYi Wang })?; 758e9ce3abSYi Wang 763acb988cSYi Wang let mut configuration = PciConfiguration::new( 773acb988cSYi Wang PVPANIC_VENDOR_ID, 783acb988cSYi Wang PVPANIC_DEVICE_ID, 793acb988cSYi Wang 0x1, // modern pci devices 803acb988cSYi Wang PciClassCode::BaseSystemPeripheral, 813acb988cSYi Wang &PvPanicSubclass::Other, 823acb988cSYi Wang None, 833acb988cSYi Wang PciHeaderType::Device, 843acb988cSYi Wang 0, 853acb988cSYi Wang 0, 863acb988cSYi Wang None, 878e9ce3abSYi Wang pci_configuration_state, 883acb988cSYi Wang ); 893acb988cSYi Wang 903acb988cSYi Wang let command: [u8; 2] = [0x03, 0x01]; 91cb52cf91SBo Chen let bar_reprogram = configuration.write_config_register(1, 0, &command); 92cb52cf91SBo Chen assert!( 93aaf86ef2SBo Chen bar_reprogram.is_empty(), 94cb52cf91SBo Chen "No bar reprogrammig is expected from writing to the COMMAND register" 95cb52cf91SBo Chen ); 963acb988cSYi Wang 978e9ce3abSYi Wang let state: Option<PvPanicDeviceState> = snapshot 988e9ce3abSYi Wang .as_ref() 9910ab87d6SRob Bradford .map(|s| s.to_state()) 1008e9ce3abSYi Wang .transpose() 1018e9ce3abSYi Wang .map_err(|e| { 1028e9ce3abSYi Wang PvPanicError::CreatePvPanicDevice(anyhow!( 1038e9ce3abSYi Wang "Failed to get PvPanicDeviceState from Snapshot: {}", 1048e9ce3abSYi Wang e 1058e9ce3abSYi Wang )) 1068e9ce3abSYi Wang })?; 1078e9ce3abSYi Wang let events = if let Some(state) = state { 1088e9ce3abSYi Wang state.events 1098e9ce3abSYi Wang } else { 1108e9ce3abSYi Wang PVPANIC_PANICKED | PVPANIC_CRASH_LOADED 1118e9ce3abSYi Wang }; 112ef67eab8SYi Wang 1138e9ce3abSYi Wang let pvpanic_device = PvPanicDevice { 1143acb988cSYi Wang id, 1153acb988cSYi Wang events, 1163acb988cSYi Wang configuration, 1173acb988cSYi Wang bar_regions: vec![], 1188e9ce3abSYi Wang }; 1198e9ce3abSYi Wang 1208e9ce3abSYi Wang Ok(pvpanic_device) 121ef67eab8SYi Wang } 1223acb988cSYi Wang event_to_string(&self, event: u8) -> String1233acb988cSYi Wang pub fn event_to_string(&self, event: u8) -> String { 1243acb988cSYi Wang if event == PVPANIC_PANICKED { 1253acb988cSYi Wang "panic".to_string() 1263acb988cSYi Wang } else if event == PVPANIC_CRASH_LOADED { 1273acb988cSYi Wang "crash_loaded".to_string() 1283acb988cSYi Wang } else { 1293acb988cSYi Wang "unknown_event".to_string() 1303acb988cSYi Wang } 1313acb988cSYi Wang } 1328e9ce3abSYi Wang state(&self) -> PvPanicDeviceState1338e9ce3abSYi Wang fn state(&self) -> PvPanicDeviceState { 1348e9ce3abSYi Wang PvPanicDeviceState { 1358e9ce3abSYi Wang events: self.events, 1368e9ce3abSYi Wang } 1378e9ce3abSYi Wang } 138d99c0c0dSYi Wang config_bar_addr(&self) -> u64139d99c0c0dSYi Wang pub fn config_bar_addr(&self) -> u64 { 140d99c0c0dSYi Wang self.configuration.get_bar_addr(0) 141d99c0c0dSYi Wang } 1423acb988cSYi Wang } 1433acb988cSYi Wang 1443acb988cSYi Wang impl BusDevice for PvPanicDevice { read(&mut self, base: u64, offset: u64, data: &mut [u8])1453acb988cSYi Wang fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) { 1463acb988cSYi Wang self.read_bar(base, offset, data) 1473acb988cSYi Wang } 1483acb988cSYi Wang write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>>1493acb988cSYi Wang fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 1503acb988cSYi Wang let event = self.event_to_string(data[0]); 1513acb988cSYi Wang info!("pvpanic got guest event {}", event); 1523acb988cSYi Wang event!("guest", "panic", "event", &event); 1533acb988cSYi Wang None 1543acb988cSYi Wang } 1553acb988cSYi Wang } 1563acb988cSYi Wang 1573acb988cSYi Wang impl PciDevice for PvPanicDevice { write_config_register( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>)1583acb988cSYi Wang fn write_config_register( 1593acb988cSYi Wang &mut self, 1603acb988cSYi Wang reg_idx: usize, 1613acb988cSYi Wang offset: u64, 1623acb988cSYi Wang data: &[u8], 163aaf86ef2SBo Chen ) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) { 164cb52cf91SBo Chen ( 1653acb988cSYi Wang self.configuration 166cb52cf91SBo Chen .write_config_register(reg_idx, offset, data), 167cb52cf91SBo Chen None, 168cb52cf91SBo Chen ) 1693acb988cSYi Wang } 1703acb988cSYi Wang read_config_register(&mut self, reg_idx: usize) -> u321713acb988cSYi Wang fn read_config_register(&mut self, reg_idx: usize) -> u32 { 1723acb988cSYi Wang self.configuration.read_reg(reg_idx) 1733acb988cSYi Wang } 1743acb988cSYi Wang allocate_bars( &mut self, _allocator: &Arc<Mutex<SystemAllocator>>, mmio32_allocator: &mut AddressAllocator, _mmio64_allocator: &mut AddressAllocator, resources: Option<Vec<Resource>>, ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError>1753acb988cSYi Wang fn allocate_bars( 1763acb988cSYi Wang &mut self, 17745b01d59SThomas Barrett _allocator: &Arc<Mutex<SystemAllocator>>, 17845b01d59SThomas Barrett mmio32_allocator: &mut AddressAllocator, 17945b01d59SThomas Barrett _mmio64_allocator: &mut AddressAllocator, 1808e9ce3abSYi Wang resources: Option<Vec<Resource>>, 1813acb988cSYi Wang ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> { 1823acb988cSYi Wang let mut bars = Vec::new(); 1833acb988cSYi Wang let region_type = PciBarRegionType::Memory32BitRegion; 1843acb988cSYi Wang let bar_id = 0; 1853acb988cSYi Wang let region_size = PVPANIC_DEVICE_MMIO_SIZE; 1868e9ce3abSYi Wang let restoring = resources.is_some(); 18745b01d59SThomas Barrett let bar_addr = mmio32_allocator 18845b01d59SThomas Barrett .allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT)) 1893acb988cSYi Wang .ok_or(PciDeviceError::IoAllocationFailed(region_size))?; 1903acb988cSYi Wang 1913acb988cSYi Wang let bar = PciBarConfiguration::default() 1923acb988cSYi Wang .set_index(bar_id as usize) 1933acb988cSYi Wang .set_address(bar_addr.raw_value()) 1943acb988cSYi Wang .set_size(region_size) 1953acb988cSYi Wang .set_region_type(region_type) 1963acb988cSYi Wang .set_prefetchable(PciBarPrefetchable::NotPrefetchable); 1973acb988cSYi Wang 1983acb988cSYi Wang debug!("pvpanic bar address 0x{:x}", bar_addr.0); 1998e9ce3abSYi Wang if !restoring { 2003acb988cSYi Wang self.configuration 2013acb988cSYi Wang .add_pci_bar(&bar) 2023acb988cSYi Wang .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; 2038e9ce3abSYi Wang } 2048e9ce3abSYi Wang 2053acb988cSYi Wang bars.push(bar); 206f1856bb2SRob Bradford self.bar_regions.clone_from(&bars); 2073acb988cSYi Wang 2083acb988cSYi Wang Ok(bars) 2093acb988cSYi Wang } 2103acb988cSYi Wang free_bars( &mut self, _allocator: &mut SystemAllocator, mmio32_allocator: &mut AddressAllocator, _mmio64_allocator: &mut AddressAllocator, ) -> std::result::Result<(), PciDeviceError>2113acb988cSYi Wang fn free_bars( 2123acb988cSYi Wang &mut self, 21345b01d59SThomas Barrett _allocator: &mut SystemAllocator, 21445b01d59SThomas Barrett mmio32_allocator: &mut AddressAllocator, 21545b01d59SThomas Barrett _mmio64_allocator: &mut AddressAllocator, 2163acb988cSYi Wang ) -> std::result::Result<(), PciDeviceError> { 2173acb988cSYi Wang for bar in self.bar_regions.drain(..) { 21845b01d59SThomas Barrett mmio32_allocator.free(GuestAddress(bar.addr()), bar.size()); 2193acb988cSYi Wang } 2203acb988cSYi Wang 2213acb988cSYi Wang Ok(()) 2223acb988cSYi Wang } 2233acb988cSYi Wang move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error>2243acb988cSYi Wang fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { 2253acb988cSYi Wang for bar in self.bar_regions.iter_mut() { 2263acb988cSYi Wang if bar.addr() == old_base { 2273acb988cSYi Wang *bar = bar.set_address(new_base); 2283acb988cSYi Wang } 2293acb988cSYi Wang } 2303acb988cSYi Wang 2313acb988cSYi Wang Ok(()) 2323acb988cSYi Wang } 2333acb988cSYi Wang read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8])2343acb988cSYi Wang fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 2353acb988cSYi Wang data[0] = self.events; 2363acb988cSYi Wang } 2373acb988cSYi Wang as_any_mut(&mut self) -> &mut dyn Any238d99f2942SWei Liu fn as_any_mut(&mut self) -> &mut dyn Any { 2393acb988cSYi Wang self 2403acb988cSYi Wang } 2413acb988cSYi Wang id(&self) -> Option<String>2423acb988cSYi Wang fn id(&self) -> Option<String> { 2433acb988cSYi Wang Some(self.id.clone()) 2443acb988cSYi Wang } 245ef67eab8SYi Wang } 2468e9ce3abSYi Wang 2478e9ce3abSYi Wang impl Pausable for PvPanicDevice {} 2488e9ce3abSYi Wang 2498e9ce3abSYi Wang impl Snapshottable for PvPanicDevice { id(&self) -> String2508e9ce3abSYi Wang fn id(&self) -> String { 2518e9ce3abSYi Wang self.id.clone() 2528e9ce3abSYi Wang } 2538e9ce3abSYi Wang snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError>2548e9ce3abSYi Wang fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 25510ab87d6SRob Bradford let mut snapshot = Snapshot::new_from_state(&self.state())?; 2568e9ce3abSYi Wang 2578e9ce3abSYi Wang // Snapshot PciConfiguration 2588e9ce3abSYi Wang snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); 2598e9ce3abSYi Wang 2608e9ce3abSYi Wang Ok(snapshot) 2618e9ce3abSYi Wang } 2628e9ce3abSYi Wang } 2638e9ce3abSYi Wang 2648e9ce3abSYi Wang impl Transportable for PvPanicDevice {} 2658e9ce3abSYi Wang impl Migratable for PvPanicDevice {} 266