cloud-hypervisor/devices/src/acpi.rs
Rob Bradford 7310ab6fa7 devices, vmm: Use a bit field for ACPI GED interrupt type
Use independent bits for storing whether there is a CPU or memory device
changed when reporting changes via ACPI GED interrupt. This prevents a
later notification squashing an earlier one and ensure that hotplugging
both CPU and memory at the same time succeeds.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
2020-01-15 20:21:22 +01:00

96 lines
2.7 KiB
Rust

// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use vmm_sys_util::eventfd::EventFd;
use BusDevice;
use HotPlugNotificationFlags;
use Interrupt;
/// A device for handling ACPI shutdown and reboot
pub struct AcpiShutdownDevice {
exit_evt: EventFd,
reset_evt: EventFd,
}
impl AcpiShutdownDevice {
/// Constructs a device that will signal the given event when the guest requests it.
pub fn new(exit_evt: EventFd, reset_evt: EventFd) -> AcpiShutdownDevice {
AcpiShutdownDevice {
exit_evt,
reset_evt,
}
}
}
// Same I/O port used for shutdown and reboot
impl BusDevice for AcpiShutdownDevice {
// Spec has all fields as zero
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
for i in data.iter_mut() {
*i = 0;
}
}
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) {
if data[0] == 1 {
debug!("ACPI Reboot signalled");
if let Err(e) = self.reset_evt.write(1) {
error!("Error triggering ACPI reset event: {}", e);
}
}
// The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5
const S5_SLEEP_VALUE: u8 = 5;
const SLEEP_STATUS_EN_BIT: u8 = 5;
const SLEEP_VALUE_BIT: u8 = 2;
if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) {
debug!("ACPI Shutdown signalled");
extern crate bitflags;
if let Err(e) = self.exit_evt.write(1) {
error!("Error triggering ACPI shutdown event: {}", e);
}
}
}
}
/// A device for handling ACPI GED event generation
pub struct AcpiGEDDevice {
interrupt: Box<dyn Interrupt>,
notification_type: HotPlugNotificationFlags,
ged_irq: u32,
}
impl AcpiGEDDevice {
pub fn new(interrupt: Box<dyn Interrupt>, ged_irq: u32) -> AcpiGEDDevice {
AcpiGEDDevice {
interrupt,
notification_type: HotPlugNotificationFlags::NO_DEVICES_CHANGED,
ged_irq,
}
}
pub fn notify(
&mut self,
notification_type: HotPlugNotificationFlags,
) -> Result<(), std::io::Error> {
self.notification_type |= notification_type;
self.interrupt.deliver()
}
pub fn irq(&self) -> u32 {
self.ged_irq
}
}
// I/O port reports what type of notification was made
impl BusDevice for AcpiGEDDevice {
// Spec has all fields as zero
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
data[0] = self.notification_type.bits();
self.notification_type = HotPlugNotificationFlags::NO_DEVICES_CHANGED;
}
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
}