xref: /cloud-hypervisor/devices/src/pvpanic.rs (revision 1b91aa8ef3ba490a12853d41783ed558cf7b7060)
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