devices: pci: Add serialization/deserialization to StubPciDevice

Add simple serialization/deserialization to StubPciDevice through the
traits provided by serde by adding the snapshot() and restore()
implementations to this struct's Suspendable implementation. The
requested_address and assigned_address fields were left out of the
snapshot since they are only configurable during VM creation.

Also add a test case for validation.

BUG=b:266622321
TEST=cargo build && ./tools/run_tests

Change-Id: I8d640452ee7db7263f1ecb185041dabb50ac46fc
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4451466
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Elie Kheirallah <khei@google.com>
Commit-Queue: Jesus Sanchez-Palencia <jesussanp@google.com>
This commit is contained in:
Jesus Sanchez-Palencia 2023-04-18 14:51:28 -07:00 committed by crosvm LUCI
parent db627e80e9
commit aebac6eff3
2 changed files with 68 additions and 1 deletions

View file

@ -47,7 +47,7 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>;
/// PCI Device Address, AKA Bus:Device.Function
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PciAddress {
/// Bus number, in the range `0..=255`.
pub bus: u8,

View file

@ -12,6 +12,7 @@
//! scanned if function 0 is present. A stub PCI device is useful in that situation to present
//! something to the guest on function 0.
use anyhow::Context;
use base::RawDescriptor;
use resources::Alloc;
use resources::SystemAllocator;
@ -106,8 +107,13 @@ pub struct StubPciParameters {
pub revision: u8,
}
#[derive(Serialize, Deserialize)]
pub struct StubPciDevice {
#[serde(default)]
#[serde(skip)]
requested_address: PciAddress,
#[serde(default)]
#[serde(skip)]
assigned_address: Option<PciAddress>,
config_regs: PciConfiguration,
}
@ -210,6 +216,17 @@ impl Suspendable for StubPciDevice {
// There are no workers to sleep/wake.
Ok(())
}
fn snapshot(&self) -> anyhow::Result<serde_json::Value> {
serde_json::to_value(self).context("failed to snapshot")
}
fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
let restored_device: StubPciDevice =
serde_json::from_value(data).context("failed to restore snapshot")?;
*self = restored_device;
Ok(())
}
}
#[cfg(test)]
@ -365,4 +382,54 @@ mod test {
assert_eq!(params.class.programming_interface, 0x45);
assert_eq!(params.revision, 52);
}
#[test]
fn stub_pci_device_snapshot_restore() -> anyhow::Result<()> {
let mut device = StubPciDevice::new(&CONFIG);
let init_reg_value = device.read_config_register(1);
let snapshot_init = device.snapshot().unwrap();
// Modify config reg 1 and make sure it went through.
let new_reg_value: u32 = 0xCAFE;
device.write_config_register(1, 0, &new_reg_value.to_le_bytes());
assert_eq!(device.read_config_register(1), new_reg_value);
// Capture a snapshot after the modification.
let mut snapshot_modified = device.snapshot().unwrap();
assert_ne!(snapshot_init, snapshot_modified);
// Modify the same register and verify that it's restored correctly.
device.write_config_register(1, 0, &[0xBA, 0xBA]);
assert_ne!(device.read_config_register(1), new_reg_value);
assert_ne!(device.read_config_register(1), init_reg_value);
device.restore(snapshot_init.clone())?;
assert_eq!(device.read_config_register(1), init_reg_value);
// Capture a snapshot after restoring the initial snapshot.
let mut snapshot_restored = device.snapshot().unwrap();
assert_eq!(snapshot_init, snapshot_restored);
// Restore to the first modification and verify the values.
device.restore(snapshot_modified.clone())?;
assert_eq!(device.read_config_register(1), new_reg_value);
snapshot_restored = device.snapshot().unwrap();
assert_eq!(snapshot_modified, snapshot_restored);
/*
Restore the initial snapshot and verify that addresses are not encoded.
The addresses are only configurable during VM creation so they never
change afterwards and are not part of the snapshot. Force a change
to requested_address to confirm that.
*/
device.restore(snapshot_init.clone())?;
device.requested_address = PciAddress {
bus: 0x0d,
dev: 0x0e,
func: 0x4,
};
snapshot_modified = device.snapshot().unwrap();
assert_eq!(snapshot_init, snapshot_modified);
Ok(())
}
}