/src/cloud-hypervisor/devices/src/pvpanic.rs
Line | Count | Source |
1 | | // Copyright © 2023 Tencent Corporation |
2 | | // |
3 | | // SPDX-License-Identifier: Apache-2.0 |
4 | | // |
5 | | |
6 | | use std::any::Any; |
7 | | use std::result; |
8 | | use std::sync::{Arc, Barrier, Mutex}; |
9 | | |
10 | | use anyhow::anyhow; |
11 | | use event_monitor::event; |
12 | | use log::{debug, info}; |
13 | | use pci::{ |
14 | | BarReprogrammingParams, PCI_CONFIGURATION_ID, PciBarConfiguration, PciBarPrefetchable, |
15 | | PciBarRegionType, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, |
16 | | PciSubclass, |
17 | | }; |
18 | | use serde::{Deserialize, Serialize}; |
19 | | use thiserror::Error; |
20 | | use vm_allocator::{AddressAllocator, SystemAllocator}; |
21 | | use vm_device::{BusDevice, Resource}; |
22 | | use vm_memory::{Address, GuestAddress}; |
23 | | use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; |
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")] |
37 | | CreatePvPanicDevice(#[source] anyhow::Error), |
38 | | #[error("Failed to retrieve PciConfigurationState")] |
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 | 0 | fn get_register_value(&self) -> u8 { |
49 | 0 | *self as u8 |
50 | 0 | } |
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(Serialize, Deserialize)] |
64 | | pub struct PvPanicDeviceState { |
65 | | events: u8, |
66 | | } |
67 | | |
68 | | impl PvPanicDevice { |
69 | 0 | pub fn new(id: String, snapshot: Option<&Snapshot>) -> Result<Self, PvPanicError> { |
70 | 0 | let pci_configuration_state = vm_migration::state_from_id(snapshot, PCI_CONFIGURATION_ID) |
71 | 0 | .map_err(|e| { |
72 | 0 | PvPanicError::RetrievePciConfigurationState(anyhow!( |
73 | 0 | "Failed to get PciConfigurationState from Snapshot: {e}" |
74 | 0 | )) |
75 | 0 | })?; |
76 | | |
77 | 0 | let mut configuration = PciConfiguration::new( |
78 | | PVPANIC_VENDOR_ID, |
79 | | PVPANIC_DEVICE_ID, |
80 | | 0x1, // modern pci devices |
81 | 0 | PciClassCode::BaseSystemPeripheral, |
82 | 0 | &PvPanicSubclass::Other, |
83 | 0 | None, |
84 | 0 | PciHeaderType::Device, |
85 | | 0, |
86 | | 0, |
87 | 0 | None, |
88 | 0 | pci_configuration_state, |
89 | | ); |
90 | | |
91 | 0 | let command: [u8; 2] = [0x03, 0x01]; |
92 | 0 | let bar_reprogram = configuration.write_config_register(1, 0, &command); |
93 | 0 | assert!( |
94 | 0 | bar_reprogram.is_empty(), |
95 | | "No bar reprogrammig is expected from writing to the COMMAND register" |
96 | | ); |
97 | | |
98 | 0 | let state: Option<PvPanicDeviceState> = snapshot |
99 | 0 | .as_ref() |
100 | 0 | .map(|s| s.to_state()) |
101 | 0 | .transpose() |
102 | 0 | .map_err(|e| { |
103 | 0 | PvPanicError::CreatePvPanicDevice(anyhow!( |
104 | 0 | "Failed to get PvPanicDeviceState from Snapshot: {e}" |
105 | 0 | )) |
106 | 0 | })?; |
107 | 0 | let events = if let Some(state) = state { |
108 | 0 | state.events |
109 | | } else { |
110 | 0 | PVPANIC_PANICKED | PVPANIC_CRASH_LOADED |
111 | | }; |
112 | | |
113 | 0 | let pvpanic_device = PvPanicDevice { |
114 | 0 | id, |
115 | 0 | events, |
116 | 0 | configuration, |
117 | 0 | bar_regions: vec![], |
118 | 0 | }; |
119 | | |
120 | 0 | Ok(pvpanic_device) |
121 | 0 | } |
122 | | |
123 | 0 | pub fn event_to_string(&self, event: u8) -> String { |
124 | 0 | if event == PVPANIC_PANICKED { |
125 | 0 | "panic".to_string() |
126 | 0 | } else if event == PVPANIC_CRASH_LOADED { |
127 | 0 | "crash_loaded".to_string() |
128 | | } else { |
129 | 0 | "unknown_event".to_string() |
130 | | } |
131 | 0 | } |
132 | | |
133 | 0 | fn state(&self) -> PvPanicDeviceState { |
134 | 0 | PvPanicDeviceState { |
135 | 0 | events: self.events, |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | 0 | pub fn config_bar_addr(&self) -> u64 { |
140 | 0 | self.configuration.get_bar_addr(0) |
141 | 0 | } |
142 | | } |
143 | | |
144 | | impl BusDevice for PvPanicDevice { |
145 | 0 | fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) { |
146 | 0 | self.read_bar(base, offset, data); |
147 | 0 | } |
148 | | |
149 | 0 | fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { |
150 | 0 | let event = self.event_to_string(data[0]); |
151 | 0 | info!("pvpanic got guest event {event}"); |
152 | 0 | event!("guest", "panic", "event", &event); |
153 | 0 | None |
154 | 0 | } |
155 | | } |
156 | | |
157 | | impl PciDevice for PvPanicDevice { |
158 | 0 | fn write_config_register( |
159 | 0 | &mut self, |
160 | 0 | reg_idx: usize, |
161 | 0 | offset: u64, |
162 | 0 | data: &[u8], |
163 | 0 | ) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) { |
164 | 0 | ( |
165 | 0 | self.configuration |
166 | 0 | .write_config_register(reg_idx, offset, data), |
167 | 0 | None, |
168 | 0 | ) |
169 | 0 | } |
170 | | |
171 | 0 | fn read_config_register(&mut self, reg_idx: usize) -> u32 { |
172 | 0 | self.configuration.read_reg(reg_idx) |
173 | 0 | } |
174 | | |
175 | 0 | fn allocate_bars( |
176 | 0 | &mut self, |
177 | 0 | _allocator: &Arc<Mutex<SystemAllocator>>, |
178 | 0 | mmio32_allocator: &mut AddressAllocator, |
179 | 0 | _mmio64_allocator: &mut AddressAllocator, |
180 | 0 | resources: Option<Vec<Resource>>, |
181 | 0 | ) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> { |
182 | 0 | let mut bars = Vec::new(); |
183 | 0 | let region_type = PciBarRegionType::Memory32BitRegion; |
184 | 0 | let bar_id = 0; |
185 | 0 | let region_size = PVPANIC_DEVICE_MMIO_SIZE; |
186 | 0 | let restoring = resources.is_some(); |
187 | 0 | let bar_addr = mmio32_allocator |
188 | 0 | .allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT)) |
189 | 0 | .ok_or(PciDeviceError::IoAllocationFailed(region_size))?; |
190 | | |
191 | 0 | let bar = PciBarConfiguration::default() |
192 | 0 | .set_index(bar_id as usize) |
193 | 0 | .set_address(bar_addr.raw_value()) |
194 | 0 | .set_size(region_size) |
195 | 0 | .set_region_type(region_type) |
196 | 0 | .set_prefetchable(PciBarPrefetchable::NotPrefetchable); |
197 | | |
198 | 0 | debug!("pvpanic bar address 0x{:x}", bar_addr.0); |
199 | 0 | if !restoring { |
200 | 0 | self.configuration |
201 | 0 | .add_pci_bar(&bar) |
202 | 0 | .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; |
203 | 0 | } |
204 | | |
205 | 0 | bars.push(bar); |
206 | 0 | self.bar_regions.clone_from(&bars); |
207 | | |
208 | 0 | Ok(bars) |
209 | 0 | } |
210 | | |
211 | 0 | fn free_bars( |
212 | 0 | &mut self, |
213 | 0 | _allocator: &mut SystemAllocator, |
214 | 0 | mmio32_allocator: &mut AddressAllocator, |
215 | 0 | _mmio64_allocator: &mut AddressAllocator, |
216 | 0 | ) -> std::result::Result<(), PciDeviceError> { |
217 | 0 | for bar in self.bar_regions.drain(..) { |
218 | 0 | mmio32_allocator.free(GuestAddress(bar.addr()), bar.size()); |
219 | 0 | } |
220 | | |
221 | 0 | Ok(()) |
222 | 0 | } |
223 | | |
224 | 0 | fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { |
225 | 0 | for bar in self.bar_regions.iter_mut() { |
226 | 0 | if bar.addr() == old_base { |
227 | 0 | *bar = bar.set_address(new_base); |
228 | 0 | } |
229 | | } |
230 | | |
231 | 0 | Ok(()) |
232 | 0 | } |
233 | | |
234 | 0 | fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { |
235 | 0 | data[0] = self.events; |
236 | 0 | } |
237 | | |
238 | 0 | fn as_any_mut(&mut self) -> &mut dyn Any { |
239 | 0 | self |
240 | 0 | } |
241 | | |
242 | 0 | fn id(&self) -> Option<String> { |
243 | 0 | Some(self.id.clone()) |
244 | 0 | } |
245 | | } |
246 | | |
247 | | impl Pausable for PvPanicDevice {} |
248 | | |
249 | | impl Snapshottable for PvPanicDevice { |
250 | 0 | fn id(&self) -> String { |
251 | 0 | self.id.clone() |
252 | 0 | } |
253 | | |
254 | 0 | fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { |
255 | 0 | let mut snapshot = Snapshot::new_from_state(&self.state())?; |
256 | | |
257 | | // Snapshot PciConfiguration |
258 | 0 | snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?); |
259 | | |
260 | 0 | Ok(snapshot) |
261 | 0 | } |
262 | | } |
263 | | |
264 | | impl Transportable for PvPanicDevice {} |
265 | | impl Migratable for PvPanicDevice {} |