• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use snapshot::AnySnapshot;
27 
28 use crate::pci::pci_configuration::PciBarConfiguration;
29 use crate::pci::pci_configuration::PciBarPrefetchable;
30 use crate::pci::pci_configuration::PciBarRegionType;
31 use crate::pci::pci_configuration::PciClassCode;
32 use crate::pci::pci_configuration::PciConfiguration;
33 use crate::pci::pci_configuration::PciHeaderType;
34 use crate::pci::pci_configuration::PciOtherSubclass;
35 use crate::pci::pci_device;
36 use crate::pci::pci_device::BarRange;
37 use crate::pci::pci_device::PciDevice;
38 use crate::pci::pci_device::Result;
39 use crate::pci::PciAddress;
40 use crate::pci::PciBarIndex;
41 use crate::pci::PciDeviceError;
42 use crate::pci::PCI_VENDOR_ID_REDHAT;
43 use crate::Suspendable;
44 
45 const PCI_DEVICE_ID_REDHAT_PVPANIC: u16 = 0x0011;
46 const PCI_PVPANIC_REVISION_ID: u8 = 1;
47 
48 const PVPANIC_BAR_INDEX: PciBarIndex = 0;
49 const PVPANIC_REG_SIZE: u64 = 0x10;
50 
51 // Guest panicked
52 pub const PVPANIC_PANICKED: u8 = 1 << 0;
53 // Guest kexeced crash kernel
54 pub const PVPANIC_CRASH_LOADED: u8 = 1 << 1;
55 
56 const PVPANIC_CAPABILITIES: u8 = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED;
57 
58 #[repr(u8)]
59 #[derive(PartialEq, Eq)]
60 pub enum PvPanicCode {
61     Panicked = PVPANIC_PANICKED,
62     CrashLoaded = PVPANIC_CRASH_LOADED,
63     Unknown = 0xFF,
64 }
65 
66 impl PvPanicCode {
from_u8(val: u8) -> PvPanicCode67     pub fn from_u8(val: u8) -> PvPanicCode {
68         match val {
69             PVPANIC_PANICKED => PvPanicCode::Panicked,
70             PVPANIC_CRASH_LOADED => PvPanicCode::CrashLoaded,
71             _ => PvPanicCode::Unknown,
72         }
73     }
74 }
75 
76 impl fmt::Display for PvPanicCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78         match self {
79             PvPanicCode::Panicked => write!(f, "Guest panicked"),
80             PvPanicCode::CrashLoaded => write!(f, "Guest panicked and crash kernel loaded"),
81             PvPanicCode::Unknown => write!(f, "Guest panicked with unknown code"),
82         }
83     }
84 }
85 
86 pub struct PvPanicPciDevice {
87     pci_address: Option<PciAddress>,
88     config_regs: PciConfiguration,
89     evt_wrtube: SendTube,
90 }
91 
92 impl PvPanicPciDevice {
new(evt_wrtube: SendTube) -> PvPanicPciDevice93     pub fn new(evt_wrtube: SendTube) -> PvPanicPciDevice {
94         let config_regs = PciConfiguration::new(
95             PCI_VENDOR_ID_REDHAT,
96             PCI_DEVICE_ID_REDHAT_PVPANIC,
97             PciClassCode::Other,
98             &PciOtherSubclass::Other,
99             None,
100             PciHeaderType::Device,
101             0xFF,
102             0xFF,
103             PCI_PVPANIC_REVISION_ID,
104         );
105 
106         Self {
107             pci_address: None,
108             config_regs,
109             evt_wrtube,
110         }
111     }
112 }
113 
114 impl PciDevice for PvPanicPciDevice {
debug_label(&self) -> String115     fn debug_label(&self) -> String {
116         "PvPanic".to_owned()
117     }
118 
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>119     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
120         if self.pci_address.is_none() {
121             self.pci_address = resources.allocate_pci(0, self.debug_label());
122         }
123         self.pci_address.ok_or(PciDeviceError::PciAllocationFailed)
124     }
125 
allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>>126     fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
127         let address = self
128             .pci_address
129             .expect("allocate_address must be called prior to allocate_io_bars");
130         let mut ranges: Vec<BarRange> = Vec::new();
131         let pvpanic_reg_addr = resources
132             .allocate_mmio(
133                 PVPANIC_REG_SIZE,
134                 Alloc::PciBar {
135                     bus: address.bus,
136                     dev: address.dev,
137                     func: address.func,
138                     bar: PVPANIC_BAR_INDEX as u8,
139                 },
140                 "pvpanic_reg".to_string(),
141                 AllocOptions::new()
142                     .max_address(u32::MAX.into())
143                     .align(PVPANIC_REG_SIZE),
144             )
145             .map_err(|e| pci_device::Error::IoAllocationFailed(PVPANIC_REG_SIZE, e))?;
146         let pvpanic_config = PciBarConfiguration::new(
147             PVPANIC_BAR_INDEX,
148             PVPANIC_REG_SIZE,
149             PciBarRegionType::Memory32BitRegion,
150             PciBarPrefetchable::NotPrefetchable,
151         )
152         .set_address(pvpanic_reg_addr);
153         self.config_regs
154             .add_pci_bar(pvpanic_config)
155             .map_err(|e| pci_device::Error::IoRegistrationFailed(pvpanic_reg_addr, e))?;
156         ranges.push(BarRange {
157             addr: pvpanic_reg_addr,
158             size: PVPANIC_REG_SIZE,
159             prefetchable: false,
160         });
161 
162         Ok(ranges)
163     }
164 
keep_rds(&self) -> Vec<RawDescriptor>165     fn keep_rds(&self) -> Vec<RawDescriptor> {
166         Vec::new()
167     }
168 
get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>169     fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
170         self.config_regs.get_bar_configuration(bar_num)
171     }
172 
read_config_register(&self, reg_idx: usize) -> u32173     fn read_config_register(&self, reg_idx: usize) -> u32 {
174         self.config_regs.read_reg(reg_idx)
175     }
176 
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])177     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
178         self.config_regs.write_reg(reg_idx, offset, data);
179     }
180 
setup_pci_config_mapping( &mut self, shmem: &SharedMemory, base: usize, len: usize, ) -> Result<bool>181     fn setup_pci_config_mapping(
182         &mut self,
183         shmem: &SharedMemory,
184         base: usize,
185         len: usize,
186     ) -> Result<bool> {
187         self.config_regs
188             .setup_mapping(shmem, base, len)
189             .map(|_| true)
190             .map_err(PciDeviceError::MmioSetup)
191     }
192 
read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8])193     fn read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8]) {
194         data[0] = if bar_index == PVPANIC_BAR_INDEX && offset == 0 && data.len() == 1 {
195             PVPANIC_CAPABILITIES
196         } else {
197             0
198         };
199     }
200 
write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8])201     fn write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8]) {
202         if bar_index != PVPANIC_BAR_INDEX || offset != 0 || data.len() != 1 {
203             return;
204         }
205 
206         if let Err(e) = self
207             .evt_wrtube
208             .send::<VmEventType>(&VmEventType::Panic(data[0]))
209         {
210             error!("Failed to write to the event tube: {}", e);
211         }
212     }
213 }
214 
215 impl Suspendable for PvPanicPciDevice {
snapshot(&mut self) -> anyhow::Result<AnySnapshot>216     fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
217         self.config_regs
218             .snapshot()
219             .context("failed to serialize PvPanicPciDevice")
220     }
221 
restore(&mut self, data: AnySnapshot) -> anyhow::Result<()>222     fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
223         self.config_regs
224             .restore(data)
225             .context("failed to deserialize PvPanicPciDevice")
226     }
227 
sleep(&mut self) -> anyhow::Result<()>228     fn sleep(&mut self) -> anyhow::Result<()> {
229         Ok(())
230     }
231 
wake(&mut self) -> anyhow::Result<()>232     fn wake(&mut self) -> anyhow::Result<()> {
233         Ok(())
234     }
235 }
236 
237 #[cfg(test)]
238 mod test {
239     use base::Tube;
240     use resources::AddressRange;
241     use resources::SystemAllocator;
242     use resources::SystemAllocatorConfig;
243 
244     use super::*;
245 
246     #[test]
pvpanic_read_write()247     fn pvpanic_read_write() {
248         let mut allocator = SystemAllocator::new(
249             SystemAllocatorConfig {
250                 io: Some(AddressRange {
251                     start: 0x1000,
252                     end: 0xffff,
253                 }),
254                 low_mmio: AddressRange {
255                     start: 0x2000_0000,
256                     end: 0x2fffffff,
257                 },
258                 high_mmio: AddressRange {
259                     start: 0x1_0000_0000,
260                     end: 0x1_0fff_ffff,
261                 },
262                 platform_mmio: None,
263                 first_irq: 5,
264             },
265             None,
266             &[],
267         )
268         .unwrap();
269 
270         let (evt_wrtube, evt_rdtube) = Tube::directional_pair().unwrap();
271         let mut device = PvPanicPciDevice::new(evt_wrtube);
272 
273         assert!(device.allocate_address(&mut allocator).is_ok());
274         assert!(device.allocate_io_bars(&mut allocator).is_ok());
275 
276         let mut data: [u8; 1] = [0; 1];
277 
278         // Read from an invalid addr
279         device.read_bar(0, 1, &mut data);
280         assert_eq!(data[0], 0);
281 
282         // Read from the valid addr
283         device.read_bar(0, 0, &mut data);
284         assert_eq!(data[0], PVPANIC_CAPABILITIES);
285 
286         // Write to the valid addr.
287         data[0] = PVPANIC_CRASH_LOADED;
288         device.write_bar(0, 0, &data);
289 
290         // Verify the event
291         let val = evt_rdtube.recv::<VmEventType>().unwrap();
292         assert_eq!(val, VmEventType::Panic(PVPANIC_CRASH_LOADED));
293     }
294 }
295