From 3acb988c1ab1450e5f0186e692f7a978c9391d0c Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Wed, 14 Jun 2023 21:04:07 +0800 Subject: [PATCH] devices: pvpanic: Add pci configuration for pvpanic device Implemetion of BusDevice and PciDevice trait for pvpanic device. Pvpanic device can be implemented as an ISA device or as a PCI device, we choose PCI device for now. Signed-off-by: Yi Wang --- Cargo.lock | 3 + devices/Cargo.toml | 3 + devices/src/lib.rs | 2 + devices/src/pvpanic.rs | 176 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 179 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6abbb9f59..676a4dcb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,13 +473,16 @@ dependencies = [ "arch", "bitflags 2.3.3", "byteorder", + "event_monitor", "hypervisor", "libc", "log", + "pci", "thiserror", "tpm", "versionize", "versionize_derive", + "vm-allocator", "vm-device", "vm-memory", "vm-migration", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 52a40471f..af272fcf8 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -10,13 +10,16 @@ anyhow = "1.0.71" arch = { path = "../arch" } bitflags = "2.3.3" byteorder = "1.4.3" +event_monitor = { path = "../event_monitor" } hypervisor = { path = "../hypervisor" } libc = "0.2.139" log = "0.4.17" +pci = { path = "../pci" } thiserror = "1.0.40" tpm = { path = "../tpm" } versionize = "0.1.10" versionize_derive = "0.1.4" +vm-allocator = { path = "../vm-allocator" } vm-device = { path = "../vm-device" } vm-memory = "0.11.0" vm-migration = { path = "../vm-migration" } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index d886ce479..4f49682be 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -10,6 +10,8 @@ #[macro_use] extern crate bitflags; #[macro_use] +extern crate event_monitor; +#[macro_use] extern crate log; pub mod acpi; diff --git a/devices/src/pvpanic.rs b/devices/src/pvpanic.rs index 0d065bab0..879ca4720 100644 --- a/devices/src/pvpanic.rs +++ b/devices/src/pvpanic.rs @@ -3,22 +3,188 @@ // SPDX-License-Identifier: Apache-2.0 // +use pci::{ + BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, + PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass, +}; +use std::any::Any; +use std::result; +use std::sync::{Arc, Barrier, Mutex}; +use vm_allocator::{AddressAllocator, SystemAllocator}; +use vm_device::{BusDevice, Resource}; +use vm_memory::{Address, GuestAddress}; + +const PVPANIC_VENDOR_ID: u16 = 0x1b36; +const PVPANIC_DEVICE_ID: u16 = 0x0011; + +pub const PVPANIC_DEVICE_MMIO_SIZE: u64 = 0x2; + const PVPANIC_PANICKED: u8 = 1 << 0; const PVPANIC_CRASH_LOADED: u8 = 1 << 1; +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum PvPanicSubclass { + Other = 0x80, +} + +impl PciSubclass for PvPanicSubclass { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + /// A device for handling guest panic event pub struct PvPanicDevice { - _id: String, - _events: u8, + id: String, + events: u8, + + // PCI configuration registers. + configuration: PciConfiguration, + bar_regions: Vec, } impl PvPanicDevice { - pub fn new() -> PvPanicDevice { + pub fn new(id: String) -> PvPanicDevice { + let mut configuration = PciConfiguration::new( + PVPANIC_VENDOR_ID, + PVPANIC_DEVICE_ID, + 0x1, // modern pci devices + PciClassCode::BaseSystemPeripheral, + &PvPanicSubclass::Other, + None, + PciHeaderType::Device, + 0, + 0, + None, + None, + ); + + let command: [u8; 2] = [0x03, 0x01]; + configuration.write_config_register(1, 0, &command); + let events = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED; PvPanicDevice { - _id: String::from("_pvpanic"), - _events: events, + id, + events, + configuration, + bar_regions: vec![], + } + } + + pub fn event_to_string(&self, event: u8) -> String { + if event == PVPANIC_PANICKED { + "panic".to_string() + } else if event == PVPANIC_CRASH_LOADED { + "crash_loaded".to_string() + } else { + "unknown_event".to_string() } } } + +impl BusDevice for PvPanicDevice { + fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) { + self.read_bar(base, offset, data) + } + + fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option> { + let event = self.event_to_string(data[0]); + info!("pvpanic got guest event {}", event); + event!("guest", "panic", "event", &event); + None + } +} + +impl PciDevice for PvPanicDevice { + fn write_config_register( + &mut self, + reg_idx: usize, + offset: u64, + data: &[u8], + ) -> Option> { + self.configuration + .write_config_register(reg_idx, offset, data); + None + } + + fn read_config_register(&mut self, reg_idx: usize) -> u32 { + self.configuration.read_reg(reg_idx) + } + + fn detect_bar_reprogramming( + &mut self, + reg_idx: usize, + data: &[u8], + ) -> Option { + self.configuration.detect_bar_reprogramming(reg_idx, data) + } + + fn allocate_bars( + &mut self, + allocator: &Arc>, + _mmio_allocator: &mut AddressAllocator, + _resources: Option>, + ) -> std::result::Result, PciDeviceError> { + let mut bars = Vec::new(); + let region_type = PciBarRegionType::Memory32BitRegion; + let bar_id = 0; + let region_size = PVPANIC_DEVICE_MMIO_SIZE; + let bar_addr = allocator + .lock() + .unwrap() + .allocate_mmio_hole_addresses(None, region_size, None) + .ok_or(PciDeviceError::IoAllocationFailed(region_size))?; + + let bar = PciBarConfiguration::default() + .set_index(bar_id as usize) + .set_address(bar_addr.raw_value()) + .set_size(region_size) + .set_region_type(region_type) + .set_prefetchable(PciBarPrefetchable::NotPrefetchable); + + debug!("pvpanic bar address 0x{:x}", bar_addr.0); + self.configuration + .add_pci_bar(&bar) + .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; + bars.push(bar); + self.bar_regions = bars.clone(); + + Ok(bars) + } + + fn free_bars( + &mut self, + allocator: &mut SystemAllocator, + _mmio_allocator: &mut AddressAllocator, + ) -> std::result::Result<(), PciDeviceError> { + for bar in self.bar_regions.drain(..) { + allocator.free_mmio_hole_addresses(GuestAddress(bar.addr()), bar.size()); + } + + Ok(()) + } + + fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), std::io::Error> { + for bar in self.bar_regions.iter_mut() { + if bar.addr() == old_base { + *bar = bar.set_address(new_base); + } + } + + Ok(()) + } + + fn read_bar(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { + data[0] = self.events; + } + + fn as_any(&mut self) -> &mut dyn Any { + self + } + + fn id(&self) -> Option { + Some(self.id.clone()) + } +}