Coverage Report

Created: 2025-12-31 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 {}