2019-08-29 13:58:25 +00:00
|
|
|
// Copyright © 2019 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
|
|
|
|
2020-02-25 07:20:59 +00:00
|
|
|
use acpi_tables::{aml, aml::Aml};
|
2020-01-20 15:00:18 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
use vm_device::interrupt::InterruptSourceGroup;
|
2019-08-29 13:58:25 +00:00
|
|
|
use vmm_sys_util::eventfd::EventFd;
|
|
|
|
use BusDevice;
|
2020-01-14 10:17:23 +00:00
|
|
|
use HotPlugNotificationFlags;
|
2019-08-29 13:58:25 +00:00
|
|
|
|
|
|
|
/// 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");
|
2020-01-14 10:17:23 +00:00
|
|
|
extern crate bitflags;
|
2019-08-29 13:58:25 +00:00
|
|
|
if let Err(e) = self.exit_evt.write(1) {
|
|
|
|
error!("Error triggering ACPI shutdown event: {}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-27 15:27:09 +00:00
|
|
|
|
|
|
|
/// A device for handling ACPI GED event generation
|
|
|
|
pub struct AcpiGEDDevice {
|
2020-01-20 15:00:18 +00:00
|
|
|
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
2020-01-14 10:17:23 +00:00
|
|
|
notification_type: HotPlugNotificationFlags,
|
2019-12-09 15:07:31 +00:00
|
|
|
ged_irq: u32,
|
2019-11-27 15:27:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AcpiGEDDevice {
|
2020-01-24 10:36:39 +00:00
|
|
|
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>, ged_irq: u32) -> AcpiGEDDevice {
|
2019-11-27 15:27:09 +00:00
|
|
|
AcpiGEDDevice {
|
|
|
|
interrupt,
|
2020-01-14 10:17:23 +00:00
|
|
|
notification_type: HotPlugNotificationFlags::NO_DEVICES_CHANGED,
|
2019-12-09 15:07:31 +00:00
|
|
|
ged_irq,
|
2019-11-27 15:27:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn notify(
|
|
|
|
&mut self,
|
2020-01-14 10:17:23 +00:00
|
|
|
notification_type: HotPlugNotificationFlags,
|
2019-11-27 15:27:09 +00:00
|
|
|
) -> Result<(), std::io::Error> {
|
2020-01-14 10:17:23 +00:00
|
|
|
self.notification_type |= notification_type;
|
2020-01-20 15:00:18 +00:00
|
|
|
self.interrupt.trigger(0)
|
2019-11-27 15:27:09 +00:00
|
|
|
}
|
2019-12-09 15:07:31 +00:00
|
|
|
|
|
|
|
pub fn irq(&self) -> u32 {
|
|
|
|
self.ged_irq
|
|
|
|
}
|
2019-11-27 15:27:09 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 10:36:39 +00:00
|
|
|
// I/O port reports what type of notification was made
|
2019-11-27 15:27:09 +00:00
|
|
|
impl BusDevice for AcpiGEDDevice {
|
|
|
|
// Spec has all fields as zero
|
|
|
|
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
2020-01-14 10:17:23 +00:00
|
|
|
data[0] = self.notification_type.bits();
|
|
|
|
self.notification_type = HotPlugNotificationFlags::NO_DEVICES_CHANGED;
|
2019-11-27 15:27:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
|
|
|
|
}
|
2020-02-25 07:20:59 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "acpi")]
|
|
|
|
impl Aml for AcpiGEDDevice {
|
|
|
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
|
|
|
aml::Device::new(
|
|
|
|
"_SB_.GED_".into(),
|
|
|
|
vec![
|
|
|
|
&aml::Name::new("_HID".into(), &"ACPI0013"),
|
|
|
|
&aml::Name::new("_UID".into(), &aml::ZERO),
|
|
|
|
&aml::Name::new(
|
|
|
|
"_CRS".into(),
|
|
|
|
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
self.ged_irq,
|
|
|
|
)]),
|
|
|
|
),
|
|
|
|
&aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1),
|
|
|
|
&aml::Field::new(
|
|
|
|
"GDST".into(),
|
|
|
|
aml::FieldAccessType::Byte,
|
|
|
|
aml::FieldUpdateRule::WriteAsZeroes,
|
|
|
|
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
|
|
|
),
|
|
|
|
&aml::Method::new(
|
|
|
|
"_EVT".into(),
|
|
|
|
1,
|
|
|
|
true,
|
|
|
|
vec![
|
|
|
|
&aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
|
|
|
|
&aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE),
|
|
|
|
&aml::If::new(
|
|
|
|
&aml::Equal::new(&aml::Local(1), &aml::ONE),
|
|
|
|
vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])],
|
|
|
|
),
|
|
|
|
&aml::And::new(&aml::Local(1), &aml::Local(0), &2usize),
|
|
|
|
&aml::If::new(
|
|
|
|
&aml::Equal::new(&aml::Local(1), &2usize),
|
|
|
|
vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])],
|
|
|
|
),
|
2020-02-26 16:45:06 +00:00
|
|
|
&aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
|
|
|
|
&aml::If::new(
|
|
|
|
&aml::Equal::new(&aml::Local(1), &4usize),
|
|
|
|
vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
|
|
|
|
),
|
2020-02-25 07:20:59 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
.to_aml_bytes()
|
|
|
|
}
|
|
|
|
}
|