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