mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
x86_64/devices: acpi: Add support for ACPI shutdown & reboot
Add an I/O port "device" to handle requests from the kernel to shutdown or trigger a reboot, borrowing an I/O used for ACPI on the Q35 platform. The details of this I/O port are included in the FADT (SLEEP_STATUS_REG/SLEEP_CONTROL_REG/RESET_REG) with the details of the value to write in the FADT for reset (RESET_VALUE) and in the DSDT for shutdown (S5 -> 0x05) Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
committed by
Samuel Ortiz
parent
ae66a44d26
commit
5a187ee2c2
@@ -2,7 +2,10 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// 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::{GuestAddress, GuestMemoryMmap};
|
||||||
|
|
||||||
use vm_memory::{Address, ByteValued, Bytes};
|
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,
|
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
|
// DSDT
|
||||||
let mut dsdt = SDT::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1);
|
let mut dsdt = SDT::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1);
|
||||||
dsdt.append(pci_dsdt_data);
|
dsdt.append(pci_dsdt_data);
|
||||||
if serial_enabled {
|
if serial_enabled {
|
||||||
dsdt.append(com1_dsdt_data);
|
dsdt.append(com1_dsdt_data);
|
||||||
}
|
}
|
||||||
|
dsdt.append(s5_sleep_data);
|
||||||
|
|
||||||
dsdt
|
dsdt
|
||||||
}
|
}
|
||||||
@@ -198,15 +210,22 @@ pub fn create_acpi_tables(
|
|||||||
// Revision 6 of the ACPI FADT table is 276 bytes long
|
// 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 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);
|
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(131, 3u8); // FADT minor version
|
||||||
facp.write(140, dsdt_offset.0); // X_DSDT
|
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
|
facp.write(268, b"CLOUDHYP"); // Hypervisor Vendor Identity
|
||||||
|
|
||||||
|
|||||||
52
devices/src/acpi.rs
Normal file
52
devices/src/acpi.rs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,10 +19,12 @@ extern crate vmm_sys_util;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
|
|
||||||
|
mod acpi;
|
||||||
mod bus;
|
mod bus;
|
||||||
pub mod ioapic;
|
pub mod ioapic;
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
|
|
||||||
|
pub use self::acpi::AcpiShutdownDevice;
|
||||||
pub use self::bus::{Bus, BusDevice, Error as BusError};
|
pub use self::bus::{Bus, BusDevice, Error as BusError};
|
||||||
|
|
||||||
pub type DeviceEventT = u16;
|
pub type DeviceEventT = u16;
|
||||||
|
|||||||
@@ -608,6 +608,9 @@ struct DeviceManager {
|
|||||||
// i8042 device for i8042 reset
|
// i8042 device for i8042 reset
|
||||||
i8042: Arc<Mutex<devices::legacy::I8042Device>>,
|
i8042: Arc<Mutex<devices::legacy::I8042Device>>,
|
||||||
|
|
||||||
|
// ACPI device for reboot/shutdwon
|
||||||
|
acpi_device: Arc<Mutex<devices::AcpiShutdownDevice>>,
|
||||||
|
|
||||||
// Shutdown (exit) and reboot (reset) control
|
// Shutdown (exit) and reboot (reset) control
|
||||||
exit_evt: EventFd,
|
exit_evt: EventFd,
|
||||||
reset_evt: EventFd,
|
reset_evt: EventFd,
|
||||||
@@ -686,6 +689,10 @@ impl DeviceManager {
|
|||||||
let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new(
|
let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new(
|
||||||
exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?,
|
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 pci_root = PciRoot::new(None);
|
||||||
let mut pci = PciConfigIo::new(pci_root);
|
let mut pci = PciConfigIo::new(pci_root);
|
||||||
@@ -737,6 +744,7 @@ impl DeviceManager {
|
|||||||
serial,
|
serial,
|
||||||
console_input: console,
|
console_input: console,
|
||||||
i8042,
|
i8042,
|
||||||
|
acpi_device,
|
||||||
exit_evt,
|
exit_evt,
|
||||||
reset_evt,
|
reset_evt,
|
||||||
ioapic,
|
ioapic,
|
||||||
@@ -1284,6 +1292,10 @@ impl DeviceManager {
|
|||||||
.insert(self.i8042.clone(), 0x61, 0x4)
|
.insert(self.i8042.clone(), 0x61, 0x4)
|
||||||
.map_err(Error::BusError)?;
|
.map_err(Error::BusError)?;
|
||||||
|
|
||||||
|
self.io_bus
|
||||||
|
.insert(self.acpi_device.clone(), 0x3c0, 0x4)
|
||||||
|
.map_err(Error::BusError)?;
|
||||||
|
|
||||||
// Insert the PCI root configuration space.
|
// Insert the PCI root configuration space.
|
||||||
self.io_bus
|
self.io_bus
|
||||||
.insert(self.pci.clone(), 0xcf8, 0x8)
|
.insert(self.pci.clone(), 0xcf8, 0x8)
|
||||||
|
|||||||
Reference in New Issue
Block a user