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