diff --git a/arch/src/x86_64/acpi.rs b/arch/src/x86_64/acpi.rs index 85c010344..589243065 100644 --- a/arch/src/x86_64/acpi.rs +++ b/arch/src/x86_64/acpi.rs @@ -2,7 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 // -use acpi_tables::{rsdp::RSDP, sdt::SDT}; +use acpi_tables::{ + rsdp::RSDP, + sdt::{GenericAddress, SDT}, +}; use vm_memory::{GuestAddress, GuestMemoryMmap}; use vm_memory::{Address, ByteValued, Bytes}; @@ -169,12 +172,21 @@ pub fn create_dsdt_table(serial_enabled: bool) -> SDT { 0x00, 0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x79, 0x00, ]; + /* + Name (\_S5, Package (0x01) // _S5_: S5 System State + { + 0x05 + }) + */ + let s5_sleep_data = [0x08u8, 0x5F, 0x53, 0x35, 0x5F, 0x12, 0x04, 0x01, 0x0A, 0x05]; + // DSDT let mut dsdt = SDT::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1); dsdt.append(pci_dsdt_data); if serial_enabled { dsdt.append(com1_dsdt_data); } + dsdt.append(s5_sleep_data); dsdt } @@ -198,15 +210,22 @@ pub fn create_acpi_tables( // Revision 6 of the ACPI FADT table is 276 bytes long let mut facp = SDT::new(*b"FACP", 276, 6, *b"CLOUDH", *b"CHFACP ", 1); - let fadt_flags: u32 = 1 << 20; // HW_REDUCED_ACPI + // HW_REDUCED_ACPI and RESET_REG_SUP + let fadt_flags: u32 = 1 << 20 | 1 << 10; facp.write(112, fadt_flags); - // TODO: RESET_REG/RESET_VALUE @ offset 116/128 + // RESET_REG + facp.write(116, GenericAddress::io_port_address(0x3c0)); + // RESET_VALUE + facp.write(128, 1u8); facp.write(131, 3u8); // FADT minor version facp.write(140, dsdt_offset.0); // X_DSDT - // TODO: SLEEP_CONTROL_REG/SLEEP_STATUS_REG @ offset 244/256 + // SLEEP_CONTROL_REG + facp.write(244, GenericAddress::io_port_address(0x3c0)); + // SLEEP_STATUS_REG + facp.write(256, GenericAddress::io_port_address(0x3c0)); facp.write(268, b"CLOUDHYP"); // Hypervisor Vendor Identity diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs new file mode 100644 index 000000000..3165b504b --- /dev/null +++ b/devices/src/acpi.rs @@ -0,0 +1,52 @@ +// Copyright © 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use vmm_sys_util::eventfd::EventFd; +use BusDevice; + +/// 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"); + if let Err(e) = self.exit_evt.write(1) { + error!("Error triggering ACPI shutdown event: {}", e); + } + } + } +} diff --git a/devices/src/lib.rs b/devices/src/lib.rs index c81c98424..d516bf4b2 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -19,10 +19,12 @@ extern crate vmm_sys_util; use std::fs::File; use std::{io, result}; +mod acpi; mod bus; pub mod ioapic; pub mod legacy; +pub use self::acpi::AcpiShutdownDevice; pub use self::bus::{Bus, BusDevice, Error as BusError}; pub type DeviceEventT = u16; diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 208c3e94d..398a63ce7 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -608,6 +608,9 @@ struct DeviceManager { // i8042 device for i8042 reset i8042: Arc>, + // ACPI device for reboot/shutdwon + acpi_device: Arc>, + // Shutdown (exit) and reboot (reset) control exit_evt: EventFd, reset_evt: EventFd, @@ -686,6 +689,10 @@ impl DeviceManager { let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new( exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?, ))); + let acpi_device = Arc::new(Mutex::new(devices::AcpiShutdownDevice::new( + exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?, + reset_evt.try_clone().map_err(DeviceManagerError::EventFd)?, + ))); let pci_root = PciRoot::new(None); let mut pci = PciConfigIo::new(pci_root); @@ -737,6 +744,7 @@ impl DeviceManager { serial, console_input: console, i8042, + acpi_device, exit_evt, reset_evt, ioapic, @@ -1284,6 +1292,10 @@ impl DeviceManager { .insert(self.i8042.clone(), 0x61, 0x4) .map_err(Error::BusError)?; + self.io_bus + .insert(self.acpi_device.clone(), 0x3c0, 0x4) + .map_err(Error::BusError)?; + // Insert the PCI root configuration space. self.io_bus .insert(self.pci.clone(), 0xcf8, 0x8)