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