diff --git a/Cargo.lock b/Cargo.lock index 194cbb461..98aaf3048 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,14 +242,19 @@ name = "devices" version = "0.1.0" dependencies = [ "acpi_tables", + "anyhow", "bitflags 1.2.1", "byteorder", "epoll", "libc", "log 0.4.8", + "serde", + "serde_derive", + "serde_json", "tempfile", "vm-device", "vm-memory", + "vm-migration", "vmm-sys-util", ] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index ed1bf733d..1c97a8c21 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -4,14 +4,19 @@ version = "0.1.0" authors = ["The Chromium OS Authors"] [dependencies] +anyhow = "1.0" bitflags = ">=1.2.1" byteorder = "1.3.4" epoll = ">=4.0.1" libc = "0.2.68" log = "0.4.8" +serde = {version = ">=1.0.27", features = ["rc"] } +serde_derive = ">=1.0.27" +serde_json = ">=1.0.9" vm-device = { path = "../vm-device" } acpi_tables = { path = "../acpi_tables", optional = true } vm-memory = "0.2.0" +vm-migration = { path = "../vm-migration" } vmm-sys-util = ">=0.3.1" [dev-dependencies] diff --git a/devices/src/ioapic.rs b/devices/src/ioapic.rs index f5417d4d8..db64e4c76 100644 --- a/devices/src/ioapic.rs +++ b/devices/src/ioapic.rs @@ -10,6 +10,7 @@ // See https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf for a specification. use crate::BusDevice; +use anyhow::anyhow; use byteorder::{ByteOrder, LittleEndian}; use std::io; use std::result; @@ -19,6 +20,14 @@ use vm_device::interrupt::{ MsiIrqGroupConfig, MsiIrqSourceConfig, }; use vm_memory::GuestAddress; +use vm_migration::{ + Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, + Transportable, +}; + +#[derive(Serialize, Deserialize)] +#[serde(remote = "GuestAddress")] +pub struct GuestAddressDef(pub u64); #[derive(Debug)] pub enum Error { @@ -150,10 +159,21 @@ pub struct Ioapic { id: u32, reg_sel: u32, reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS], + used_entries: [bool; NUM_IOAPIC_PINS], apic_address: GuestAddress, interrupt_source_group: Arc>, } +#[derive(Serialize, Deserialize)] +pub struct IoapicState { + id: u32, + reg_sel: u32, + reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS], + used_entries: [bool; NUM_IOAPIC_PINS], + #[serde(with = "GuestAddressDef")] + apic_address: GuestAddress, +} + impl BusDevice for Ioapic { fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) { assert!(data.len() == 4); @@ -209,6 +229,7 @@ impl Ioapic { id: 0, reg_sel: 0, reg_entries: [0; NUM_IOAPIC_PINS], + used_entries: [false; NUM_IOAPIC_PINS], apic_address, interrupt_source_group, }) @@ -341,6 +362,8 @@ impl Ioapic { if let Err(e) = self.update_entry(index) { error!("Failed updating IOAPIC entry: {:?}", e); } + // Store the information this IRQ is now being used. + self.used_entries[index] = true; } _ => error!("IOAPIC: invalid write to register offset"), } @@ -366,4 +389,78 @@ impl Ioapic { } } } + + fn state(&self) -> IoapicState { + IoapicState { + id: self.id, + reg_sel: self.reg_sel, + reg_entries: self.reg_entries, + used_entries: self.used_entries, + apic_address: self.apic_address, + } + } + + fn set_state(&mut self, state: &IoapicState) -> Result<()> { + self.id = state.id; + self.reg_sel = state.reg_sel; + self.reg_entries = state.reg_entries; + self.used_entries = state.used_entries; + self.apic_address = state.apic_address; + for (irq, entry) in self.used_entries.iter().enumerate() { + if *entry { + self.update_entry(irq)?; + } + } + + Ok(()) + } } + +const IOAPIC_SNAPSHOT_ID: &str = "ioapic"; +impl Snapshottable for Ioapic { + fn id(&self) -> String { + IOAPIC_SNAPSHOT_ID.to_string() + } + + fn snapshot(&self) -> std::result::Result { + let snapshot = + serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?; + + let mut ioapic_snapshot = Snapshot::new(IOAPIC_SNAPSHOT_ID); + ioapic_snapshot.add_data_section(SnapshotDataSection { + id: format!("{}-section", IOAPIC_SNAPSHOT_ID), + snapshot, + }); + + Ok(ioapic_snapshot) + } + + fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { + if let Some(ioapic_section) = snapshot + .snapshot_data + .get(&format!("{}-section", IOAPIC_SNAPSHOT_ID)) + { + let ioapic_state = match serde_json::from_slice(&ioapic_section.snapshot) { + Ok(state) => state, + Err(error) => { + return Err(MigratableError::Restore(anyhow!( + "Could not deserialize IOAPIC {}", + error + ))) + } + }; + + return self.set_state(&ioapic_state).map_err(|e| { + MigratableError::Restore(anyhow!("Could not restore IOAPIC state {:?}", e)) + }); + } + + Err(MigratableError::Restore(anyhow!( + "Could not find IOAPIC snapshot section" + ))) + } +} + +impl Pausable for Ioapic {} +impl Transportable for Ioapic {} +impl Migratable for Ioapic {} diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 042e8f064..ba553d6b5 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -6,6 +6,7 @@ // found in the LICENSE-BSD-3-Clause file. //! Emulates virtual and hardware devices. +extern crate anyhow; #[macro_use] extern crate bitflags; extern crate byteorder; @@ -15,9 +16,14 @@ extern crate libc; extern crate log; #[cfg(feature = "acpi")] extern crate acpi_tables; +extern crate serde; extern crate vm_device; extern crate vm_memory; +extern crate vm_migration; extern crate vmm_sys_util; +#[macro_use] +extern crate serde_derive; +extern crate serde_json; use std::fs::File; use std::io;