xref: /aosp_15_r20/external/crosvm/devices/src/pci/pvpanic.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! pvpanic is a simulated device, through which a guest panic event is sent to a VMM.
6 //! This was initially developed for qemu with linux in-tree drivers and opensource
7 //! driver for windows also exist now.
8 //! <https://fossies.org/linux/qemu/docs/specs/pvpanic.txt>
9 //!
10 //! This implementation emulates pci interface for pvpanic virtual device.
11 
12 // TODO(218575411): Support pvpanic on windows crosvm.
13 #![cfg_attr(windows, allow(dead_code))]
14 
15 use std::fmt;
16 
17 use anyhow::Context;
18 use base::error;
19 use base::RawDescriptor;
20 use base::SendTube;
21 use base::SharedMemory;
22 use base::VmEventType;
23 use resources::Alloc;
24 use resources::AllocOptions;
25 use resources::SystemAllocator;
26 
27 use crate::pci::pci_configuration::PciBarConfiguration;
28 use crate::pci::pci_configuration::PciBarPrefetchable;
29 use crate::pci::pci_configuration::PciBarRegionType;
30 use crate::pci::pci_configuration::PciClassCode;
31 use crate::pci::pci_configuration::PciConfiguration;
32 use crate::pci::pci_configuration::PciHeaderType;
33 use crate::pci::pci_configuration::PciOtherSubclass;
34 use crate::pci::pci_device;
35 use crate::pci::pci_device::BarRange;
36 use crate::pci::pci_device::PciDevice;
37 use crate::pci::pci_device::Result;
38 use crate::pci::PciAddress;
39 use crate::pci::PciBarIndex;
40 use crate::pci::PciDeviceError;
41 use crate::pci::PCI_VENDOR_ID_REDHAT;
42 use crate::Suspendable;
43 
44 const PCI_DEVICE_ID_REDHAT_PVPANIC: u16 = 0x0011;
45 const PCI_PVPANIC_REVISION_ID: u8 = 1;
46 
47 const PVPANIC_BAR_INDEX: PciBarIndex = 0;
48 const PVPANIC_REG_SIZE: u64 = 0x10;
49 
50 // Guest panicked
51 pub const PVPANIC_PANICKED: u8 = 1 << 0;
52 // Guest kexeced crash kernel
53 pub const PVPANIC_CRASH_LOADED: u8 = 1 << 1;
54 
55 const PVPANIC_CAPABILITIES: u8 = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED;
56 
57 #[repr(u8)]
58 #[derive(PartialEq, Eq)]
59 pub enum PvPanicCode {
60     Panicked = PVPANIC_PANICKED,
61     CrashLoaded = PVPANIC_CRASH_LOADED,
62     Unknown = 0xFF,
63 }
64 
65 impl PvPanicCode {
from_u8(val: u8) -> PvPanicCode66     pub fn from_u8(val: u8) -> PvPanicCode {
67         match val {
68             PVPANIC_PANICKED => PvPanicCode::Panicked,
69             PVPANIC_CRASH_LOADED => PvPanicCode::CrashLoaded,
70             _ => PvPanicCode::Unknown,
71         }
72     }
73 }
74 
75 impl fmt::Display for PvPanicCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result76     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77         match self {
78             PvPanicCode::Panicked => write!(f, "Guest panicked"),
79             PvPanicCode::CrashLoaded => write!(f, "Guest panicked and crash kernel loaded"),
80             PvPanicCode::Unknown => write!(f, "Guest panicked with unknown code"),
81         }
82     }
83 }
84 
85 pub struct PvPanicPciDevice {
86     pci_address: Option<PciAddress>,
87     config_regs: PciConfiguration,
88     evt_wrtube: SendTube,
89 }
90 
91 impl PvPanicPciDevice {
new(evt_wrtube: SendTube) -> PvPanicPciDevice92     pub fn new(evt_wrtube: SendTube) -> PvPanicPciDevice {
93         let config_regs = PciConfiguration::new(
94             PCI_VENDOR_ID_REDHAT,
95             PCI_DEVICE_ID_REDHAT_PVPANIC,
96             PciClassCode::Other,
97             &PciOtherSubclass::Other,
98             None,
99             PciHeaderType::Device,
100             0xFF,
101             0xFF,
102             PCI_PVPANIC_REVISION_ID,
103         );
104 
105         Self {
106             pci_address: None,
107             config_regs,
108             evt_wrtube,
109         }
110     }
111 }
112 
113 impl PciDevice for PvPanicPciDevice {
debug_label(&self) -> String114     fn debug_label(&self) -> String {
115         "PvPanic".to_owned()
116     }
117 
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>118     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
119         if self.pci_address.is_none() {
120             self.pci_address = match resources.allocate_pci(0, self.debug_label()) {
121                 Some(Alloc::PciBar {
122                     bus,
123                     dev,
124                     func,
125                     bar: _,
126                 }) => Some(PciAddress { bus, dev, func }),
127                 _ => None,
128             }
129         }
130         self.pci_address.ok_or(PciDeviceError::PciAllocationFailed)
131     }
132 
allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>>133     fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
134         let address = self
135             .pci_address
136             .expect("allocate_address must be called prior to allocate_io_bars");
137         let mut ranges: Vec<BarRange> = Vec::new();
138         let pvpanic_reg_addr = resources
139             .allocate_mmio(
140                 PVPANIC_REG_SIZE,
141                 Alloc::PciBar {
142                     bus: address.bus,
143                     dev: address.dev,
144                     func: address.func,
145                     bar: PVPANIC_BAR_INDEX as u8,
146                 },
147                 "pvpanic_reg".to_string(),
148                 AllocOptions::new()
149                     .max_address(u32::MAX.into())
150                     .align(PVPANIC_REG_SIZE),
151             )
152             .map_err(|e| pci_device::Error::IoAllocationFailed(PVPANIC_REG_SIZE, e))?;
153         let pvpanic_config = PciBarConfiguration::new(
154             PVPANIC_BAR_INDEX,
155             PVPANIC_REG_SIZE,
156             PciBarRegionType::Memory32BitRegion,
157             PciBarPrefetchable::NotPrefetchable,
158         )
159         .set_address(pvpanic_reg_addr);
160         self.config_regs
161             .add_pci_bar(pvpanic_config)
162             .map_err(|e| pci_device::Error::IoRegistrationFailed(pvpanic_reg_addr, e))?;
163         ranges.push(BarRange {
164             addr: pvpanic_reg_addr,
165             size: PVPANIC_REG_SIZE,
166             prefetchable: false,
167         });
168 
169         Ok(ranges)
170     }
171 
keep_rds(&self) -> Vec<RawDescriptor>172     fn keep_rds(&self) -> Vec<RawDescriptor> {
173         Vec::new()
174     }
175 
get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>176     fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
177         self.config_regs.get_bar_configuration(bar_num)
178     }
179 
read_config_register(&self, reg_idx: usize) -> u32180     fn read_config_register(&self, reg_idx: usize) -> u32 {
181         self.config_regs.read_reg(reg_idx)
182     }
183 
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])184     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
185         self.config_regs.write_reg(reg_idx, offset, data);
186     }
187 
setup_pci_config_mapping( &mut self, shmem: &SharedMemory, base: usize, len: usize, ) -> Result<bool>188     fn setup_pci_config_mapping(
189         &mut self,
190         shmem: &SharedMemory,
191         base: usize,
192         len: usize,
193     ) -> Result<bool> {
194         self.config_regs
195             .setup_mapping(shmem, base, len)
196             .map(|_| true)
197             .map_err(PciDeviceError::MmioSetup)
198     }
199 
read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8])200     fn read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8]) {
201         data[0] = if bar_index == PVPANIC_BAR_INDEX && offset == 0 && data.len() == 1 {
202             PVPANIC_CAPABILITIES
203         } else {
204             0
205         };
206     }
207 
write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8])208     fn write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8]) {
209         if bar_index != PVPANIC_BAR_INDEX || offset != 0 || data.len() != 1 {
210             return;
211         }
212 
213         if let Err(e) = self
214             .evt_wrtube
215             .send::<VmEventType>(&VmEventType::Panic(data[0]))
216         {
217             error!("Failed to write to the event tube: {}", e);
218         }
219     }
220 }
221 
222 impl Suspendable for PvPanicPciDevice {
snapshot(&mut self) -> anyhow::Result<serde_json::Value>223     fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
224         self.config_regs
225             .snapshot()
226             .context("failed to serialize PvPanicPciDevice")
227     }
228 
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>229     fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
230         self.config_regs
231             .restore(data)
232             .context("failed to deserialize PvPanicPciDevice")
233     }
234 
sleep(&mut self) -> anyhow::Result<()>235     fn sleep(&mut self) -> anyhow::Result<()> {
236         Ok(())
237     }
238 
wake(&mut self) -> anyhow::Result<()>239     fn wake(&mut self) -> anyhow::Result<()> {
240         Ok(())
241     }
242 }
243 
244 #[cfg(test)]
245 mod test {
246     use base::Tube;
247     use resources::AddressRange;
248     use resources::SystemAllocator;
249     use resources::SystemAllocatorConfig;
250 
251     use super::*;
252 
253     #[test]
pvpanic_read_write()254     fn pvpanic_read_write() {
255         let mut allocator = SystemAllocator::new(
256             SystemAllocatorConfig {
257                 io: Some(AddressRange {
258                     start: 0x1000,
259                     end: 0xffff,
260                 }),
261                 low_mmio: AddressRange {
262                     start: 0x2000_0000,
263                     end: 0x2fffffff,
264                 },
265                 high_mmio: AddressRange {
266                     start: 0x1_0000_0000,
267                     end: 0x1_0fff_ffff,
268                 },
269                 platform_mmio: None,
270                 first_irq: 5,
271             },
272             None,
273             &[],
274         )
275         .unwrap();
276 
277         let (evt_wrtube, evt_rdtube) = Tube::directional_pair().unwrap();
278         let mut device = PvPanicPciDevice::new(evt_wrtube);
279 
280         assert!(device.allocate_address(&mut allocator).is_ok());
281         assert!(device.allocate_io_bars(&mut allocator).is_ok());
282 
283         let mut data: [u8; 1] = [0; 1];
284 
285         // Read from an invalid addr
286         device.read_bar(0, 1, &mut data);
287         assert_eq!(data[0], 0);
288 
289         // Read from the valid addr
290         device.read_bar(0, 0, &mut data);
291         assert_eq!(data[0], PVPANIC_CAPABILITIES);
292 
293         // Write to the valid addr.
294         data[0] = PVPANIC_CRASH_LOADED;
295         device.write_bar(0, 0, &data);
296 
297         // Verify the event
298         let val = evt_rdtube.recv::<VmEventType>().unwrap();
299         assert_eq!(val, VmEventType::Panic(PVPANIC_CRASH_LOADED));
300     }
301 }
302