use chipset_device::io::IoResult;
use chipset_device::pio::PortIoIntercept;
use chipset_device::pio::RegisterPortIoIntercept;
use chipset_device::poll_device::PollDevice;
use chipset_device::ChipsetDevice;
use inspect::InspectMut;
use pci_bus::GenericPciBus;
use vmcore::device_state::ChangeDeviceState;
mod io_ports {
pub const PCI_ADDR_START: u16 = 0xCF8;
pub const RESET_CF9: u16 = 0xCF9; pub const PCI_DATA_START: u16 = 0xCFC;
}
#[derive(InspectMut)]
pub struct Piix4PciBus {
#[inspect(skip)]
reset_evt: Box<dyn Fn() + Send + Sync>,
#[inspect(mut)]
bus: GenericPciBus,
}
impl Piix4PciBus {
pub fn new(
register_pio: &mut dyn RegisterPortIoIntercept,
reset_evt: Box<dyn Fn() + Send + Sync>,
) -> Self {
Piix4PciBus {
reset_evt,
bus: GenericPciBus::new(
register_pio,
io_ports::PCI_ADDR_START,
io_ports::PCI_DATA_START,
),
}
}
pub fn as_pci_bus(&mut self) -> &mut GenericPciBus {
&mut self.bus
}
fn handle_reset_cf9_read(&mut self, data: &mut [u8]) {
if data.len() != 1 {
tracelimit::warn_ratelimited!(len = ?data.len(), "unexpected RESET_CF9 read len");
return;
}
tracelimit::warn_ratelimited!("read from the RESET_CF9 io port");
data[0] = 0;
}
fn handle_reset_cf9_write(&mut self, data: &[u8]) {
if data.len() != 1 {
tracelimit::warn_ratelimited!(len = ?data.len(), "unexpected RESET_CF9 write len");
return;
}
if (data[0] & 0x6) != 0 {
tracing::info!("initiating guest reset via RESET_CF9");
(self.reset_evt)();
}
}
}
impl ChangeDeviceState for Piix4PciBus {
fn start(&mut self) {}
async fn stop(&mut self) {}
async fn reset(&mut self) {
self.bus.reset().await;
}
}
impl ChipsetDevice for Piix4PciBus {
fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
Some(self)
}
fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
Some(self.as_pci_bus())
}
}
impl PortIoIntercept for Piix4PciBus {
fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
if data.len() == 1 && io_port == io_ports::RESET_CF9 {
self.handle_reset_cf9_read(data);
return IoResult::Ok;
}
self.bus.io_read(io_port, data)
}
fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
if data.len() == 1 && io_port == io_ports::RESET_CF9 {
self.handle_reset_cf9_write(data);
return IoResult::Ok;
}
self.bus.io_write(io_port, data)
}
}
mod save_restore {
use super::*;
use vmcore::save_restore::RestoreError;
use vmcore::save_restore::SaveError;
use vmcore::save_restore::SaveRestore;
mod state {
use mesh::payload::Protobuf;
use pci_bus::GenericPciBus;
use vmcore::save_restore::SaveRestore;
use vmcore::save_restore::SavedStateRoot;
#[derive(Protobuf, SavedStateRoot)]
#[mesh(package = "chipset.piix4.pci_bus")]
pub struct SavedState {
#[mesh(1)]
pub bus: <GenericPciBus as SaveRestore>::SavedState,
}
}
impl SaveRestore for Piix4PciBus {
type SavedState = state::SavedState;
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
let Piix4PciBus { reset_evt: _, bus } = self;
let saved_state = state::SavedState { bus: bus.save()? };
Ok(saved_state)
}
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
let state::SavedState { bus } = state;
self.bus.restore(bus)?;
Ok(())
}
}
}