Coverage Report

Created: 2025-12-31 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/crosvm/devices/src/pci/pvpanic.rs
Line
Count
Source
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 {
67
0
    pub fn from_u8(val: u8) -> PvPanicCode {
68
0
        match val {
69
0
            PVPANIC_PANICKED => PvPanicCode::Panicked,
70
0
            PVPANIC_CRASH_LOADED => PvPanicCode::CrashLoaded,
71
0
            _ => PvPanicCode::Unknown,
72
        }
73
0
    }
74
}
75
76
impl fmt::Display for PvPanicCode {
77
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78
0
        match self {
79
0
            PvPanicCode::Panicked => write!(f, "Guest panicked"),
80
0
            PvPanicCode::CrashLoaded => write!(f, "Guest panicked and crash kernel loaded"),
81
0
            PvPanicCode::Unknown => write!(f, "Guest panicked with unknown code"),
82
        }
83
0
    }
84
}
85
86
pub struct PvPanicPciDevice {
87
    pci_address: Option<PciAddress>,
88
    config_regs: PciConfiguration,
89
    evt_wrtube: SendTube,
90
}
91
92
impl PvPanicPciDevice {
93
0
    pub fn new(evt_wrtube: SendTube) -> PvPanicPciDevice {
94
0
        let config_regs = PciConfiguration::new(
95
            PCI_VENDOR_ID_REDHAT,
96
            PCI_DEVICE_ID_REDHAT_PVPANIC,
97
0
            PciClassCode::Other,
98
0
            &PciOtherSubclass::Other,
99
0
            None,
100
0
            PciHeaderType::Device,
101
            0xFF,
102
            0xFF,
103
            PCI_PVPANIC_REVISION_ID,
104
        );
105
106
0
        Self {
107
0
            pci_address: None,
108
0
            config_regs,
109
0
            evt_wrtube,
110
0
        }
111
0
    }
112
}
113
114
impl PciDevice for PvPanicPciDevice {
115
0
    fn debug_label(&self) -> String {
116
0
        "PvPanic".to_owned()
117
0
    }
118
119
0
    fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
120
0
        if self.pci_address.is_none() {
121
0
            self.pci_address = resources.allocate_pci(0, self.debug_label());
122
0
        }
123
0
        self.pci_address.ok_or(PciDeviceError::PciAllocationFailed)
124
0
    }
125
126
0
    fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
127
0
        let address = self
128
0
            .pci_address
129
0
            .expect("allocate_address must be called prior to allocate_io_bars");
130
0
        let mut ranges: Vec<BarRange> = Vec::new();
131
0
        let pvpanic_reg_addr = resources
132
0
            .allocate_mmio(
133
                PVPANIC_REG_SIZE,
134
0
                Alloc::PciBar {
135
0
                    bus: address.bus,
136
0
                    dev: address.dev,
137
0
                    func: address.func,
138
0
                    bar: PVPANIC_BAR_INDEX as u8,
139
0
                },
140
0
                "pvpanic_reg".to_string(),
141
0
                AllocOptions::new()
142
0
                    .max_address(u32::MAX.into())
143
0
                    .align(PVPANIC_REG_SIZE),
144
            )
145
0
            .map_err(|e| pci_device::Error::IoAllocationFailed(PVPANIC_REG_SIZE, e))?;
146
0
        let pvpanic_config = PciBarConfiguration::new(
147
            PVPANIC_BAR_INDEX,
148
            PVPANIC_REG_SIZE,
149
0
            PciBarRegionType::Memory32BitRegion,
150
0
            PciBarPrefetchable::NotPrefetchable,
151
        )
152
0
        .set_address(pvpanic_reg_addr);
153
0
        self.config_regs
154
0
            .add_pci_bar(pvpanic_config)
155
0
            .map_err(|e| pci_device::Error::IoRegistrationFailed(pvpanic_reg_addr, e))?;
156
0
        ranges.push(BarRange {
157
0
            addr: pvpanic_reg_addr,
158
0
            size: PVPANIC_REG_SIZE,
159
0
            prefetchable: false,
160
0
        });
161
162
0
        Ok(ranges)
163
0
    }
164
165
0
    fn keep_rds(&self) -> Vec<RawDescriptor> {
166
0
        Vec::new()
167
0
    }
168
169
0
    fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
170
0
        self.config_regs.get_bar_configuration(bar_num)
171
0
    }
172
173
0
    fn read_config_register(&self, reg_idx: usize) -> u32 {
174
0
        self.config_regs.read_reg(reg_idx)
175
0
    }
176
177
0
    fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
178
0
        self.config_regs.write_reg(reg_idx, offset, data);
179
0
    }
180
181
0
    fn setup_pci_config_mapping(
182
0
        &mut self,
183
0
        shmem: &SharedMemory,
184
0
        base: usize,
185
0
        len: usize,
186
0
    ) -> Result<bool> {
187
0
        self.config_regs
188
0
            .setup_mapping(shmem, base, len)
189
0
            .map(|_| true)
190
0
            .map_err(PciDeviceError::MmioSetup)
191
0
    }
192
193
0
    fn read_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &mut [u8]) {
194
0
        data[0] = if bar_index == PVPANIC_BAR_INDEX && offset == 0 && data.len() == 1 {
195
0
            PVPANIC_CAPABILITIES
196
        } else {
197
0
            0
198
        };
199
0
    }
200
201
0
    fn write_bar(&mut self, bar_index: PciBarIndex, offset: u64, data: &[u8]) {
202
0
        if bar_index != PVPANIC_BAR_INDEX || offset != 0 || data.len() != 1 {
203
0
            return;
204
0
        }
205
206
0
        if let Err(e) = self
207
0
            .evt_wrtube
208
0
            .send::<VmEventType>(&VmEventType::Panic(data[0]))
209
        {
210
0
            error!("Failed to write to the event tube: {}", e);
211
0
        }
212
0
    }
213
}
214
215
impl Suspendable for PvPanicPciDevice {
216
0
    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
217
0
        self.config_regs
218
0
            .snapshot()
219
0
            .context("failed to serialize PvPanicPciDevice")
220
0
    }
221
222
0
    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
223
0
        self.config_regs
224
0
            .restore(data)
225
0
            .context("failed to deserialize PvPanicPciDevice")
226
0
    }
227
228
0
    fn sleep(&mut self) -> anyhow::Result<()> {
229
0
        Ok(())
230
0
    }
231
232
0
    fn wake(&mut self) -> anyhow::Result<()> {
233
0
        Ok(())
234
0
    }
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]
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
}