diff --git a/pci/src/msi.rs b/pci/src/msi.rs index fe94d691c..fdaa36562 100644 --- a/pci/src/msi.rs +++ b/pci/src/msi.rs @@ -3,11 +3,17 @@ // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause // +use anyhow::anyhow; use byteorder::{ByteOrder, LittleEndian}; +use std::io; use std::sync::Arc; +use thiserror::Error; +use versionize::{VersionMap, Versionize, VersionizeResult}; +use versionize_derive::Versionize; use vm_device::interrupt::{ InterruptIndex, InterruptSourceConfig, InterruptSourceGroup, MsiIrqSourceConfig, }; +use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped}; // MSI control masks const MSI_CTL_ENABLE: u16 = 0x1; @@ -32,7 +38,15 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize { 1 << field } -#[derive(Clone, Copy, Default)] +#[derive(Error, Debug)] +enum Error { + #[error("Failed enabling the interrupt route: {0}")] + EnableInterruptRoute(io::Error), + #[error("Failed updating the interrupt route: {0}")] + UpdateInterruptRoute(io::Error), +} + +#[derive(Clone, Copy, Default, Versionize)] pub struct MsiCap { // Message Control Register // 0: MSI enable. @@ -157,6 +171,13 @@ impl MsiCap { } } +#[derive(Versionize)] +struct MsiConfigState { + cap: MsiCap, +} + +impl VersionMapped for MsiConfigState {} + pub struct MsiConfig { cap: MsiCap, interrupt_source_group: Arc, @@ -175,6 +196,39 @@ impl MsiConfig { } } + fn state(&self) -> MsiConfigState { + MsiConfigState { cap: self.cap } + } + + fn set_state(&mut self, state: &MsiConfigState) -> Result<(), Error> { + self.cap = state.cap; + + if self.enabled() { + for idx in 0..self.num_enabled_vectors() { + let config = MsiIrqSourceConfig { + high_addr: self.cap.msg_addr_hi, + low_addr: self.cap.msg_addr_lo, + data: self.cap.msg_data as u32, + devid: 0, + }; + + self.interrupt_source_group + .update( + idx as InterruptIndex, + InterruptSourceConfig::MsiIrq(config), + self.cap.vector_masked(idx), + ) + .map_err(Error::UpdateInterruptRoute)?; + } + + self.interrupt_source_group + .enable() + .map_err(Error::EnableInterruptRoute)?; + } + + Ok(()) + } + pub fn enabled(&self) -> bool { self.cap.enabled() } @@ -222,3 +276,26 @@ impl MsiConfig { } } } + +impl Pausable for MsiConfig {} + +impl Snapshottable for MsiConfig { + fn id(&self) -> String { + String::from("msi_config") + } + + fn snapshot(&mut self) -> std::result::Result { + Snapshot::new_from_versioned_state(&self.id(), &self.state()) + } + + fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { + self.set_state(&snapshot.to_versioned_state(&self.id())?) + .map_err(|e| { + MigratableError::Restore(anyhow!( + "Could not restore state for {}: {:?}", + self.id(), + e + )) + }) + } +}