mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 10:10:41 +00:00
acpi: support fixed power button in vPM1
- Add a new trait, PmResource, for PM-related public interfaces. - Use SCI_INT in FADT as vSCI if FADT is forwarded. - Inject vSCI if ACPI fixed power button is enabled and a power button event is received. - Disable MPTable generation if FADT is forwarded [1]. [1] MPTable generation in mptable.rs makes certain assumptions about SCI which is incompatible with FADT forwarding. MADT takes precedence over MPTable in the Linux kernel so hopefully things should work correctly. BUG=b:199383670 TEST=boot Linux kernel and shut down Change-Id: Icc93c3e7492e44b3a5badc5e75373c472c9b9791 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3350491 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Tomasz Nowicki <tnowicki@google.com>
This commit is contained in:
parent
8f833c1fca
commit
072c103be6
6 changed files with 99 additions and 27 deletions
|
@ -96,13 +96,23 @@ impl SDT {
|
|||
self.write(LENGTH_OFFSET, self.data.len() as u32);
|
||||
}
|
||||
|
||||
/// Read a value at the given offset
|
||||
pub fn read<T: DataInit + Default>(&self, offset: usize) -> T {
|
||||
let value_len = std::mem::size_of::<T>();
|
||||
*T::from_slice(
|
||||
self.as_slice()
|
||||
.get(offset..offset + value_len)
|
||||
.unwrap_or(T::default().as_slice()),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Write a value at the given offset
|
||||
pub fn write<T: DataInit>(&mut self, offset: usize, value: T) {
|
||||
let value_len = std::mem::size_of::<T>();
|
||||
if (offset + value_len) > self.data.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.data[offset..offset + value_len].copy_from_slice(value.as_slice());
|
||||
self.update_checksum();
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
use crate::{BusAccessInfo, BusDevice, BusResumeDevice};
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use base::{error, warn, Event};
|
||||
use vm_control::PmResource;
|
||||
|
||||
/// ACPI PM resource for handling OS suspend/resume request
|
||||
#[allow(dead_code)]
|
||||
pub struct ACPIPMResource {
|
||||
sci_evt: Event,
|
||||
suspend_evt: Event,
|
||||
exit_evt: Event,
|
||||
pm1_status: u16,
|
||||
|
@ -19,8 +21,9 @@ pub struct ACPIPMResource {
|
|||
impl ACPIPMResource {
|
||||
/// Constructs ACPI Power Management Resouce.
|
||||
#[allow(dead_code)]
|
||||
pub fn new(suspend_evt: Event, exit_evt: Event) -> ACPIPMResource {
|
||||
pub fn new(sci_evt: Event, suspend_evt: Event, exit_evt: Event) -> ACPIPMResource {
|
||||
ACPIPMResource {
|
||||
sci_evt,
|
||||
suspend_evt,
|
||||
exit_evt,
|
||||
pm1_status: 0,
|
||||
|
@ -28,6 +31,21 @@ impl ACPIPMResource {
|
|||
pm1_control: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn pm_sci(&self) {
|
||||
if self.pm1_status
|
||||
& self.pm1_enable
|
||||
& (BITMASK_PM1EN_GBL_EN
|
||||
| BITMASK_PM1EN_PWRBTN_EN
|
||||
| BITMASK_PM1EN_SLPBTN_EN
|
||||
| BITMASK_PM1EN_RTC_EN)
|
||||
!= 0
|
||||
{
|
||||
if let Err(e) = self.sci_evt.write(1) {
|
||||
error!("ACPIPM: failed to trigger sci event: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the ACPI PM register length.
|
||||
|
@ -53,6 +71,11 @@ const PM1_ENABLE: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2);
|
|||
/// Size: PM1_CNT_LEN (defined in FADT)
|
||||
const PM1_CONTROL: u16 = PM1_STATUS + ACPIPM_RESOURCE_EVENTBLK_LEN as u16;
|
||||
|
||||
const BITMASK_PM1STS_PWRBTN_STS: u16 = 1 << 8;
|
||||
const BITMASK_PM1EN_GBL_EN: u16 = 1 << 5;
|
||||
const BITMASK_PM1EN_PWRBTN_EN: u16 = 1 << 8;
|
||||
const BITMASK_PM1EN_SLPBTN_EN: u16 = 1 << 9;
|
||||
const BITMASK_PM1EN_RTC_EN: u16 = 1 << 10;
|
||||
const BITMASK_PM1CNT_SLEEP_ENABLE: u16 = 0x2000;
|
||||
const BITMASK_PM1CNT_WAKE_STATUS: u16 = 0x8000;
|
||||
|
||||
|
@ -63,6 +86,13 @@ const SLEEP_TYPE_S1: u16 = 1 << 10;
|
|||
#[cfg(not(feature = "direct"))]
|
||||
const SLEEP_TYPE_S5: u16 = 0 << 10;
|
||||
|
||||
impl PmResource for ACPIPMResource {
|
||||
fn pwrbtn_evt(&mut self) {
|
||||
self.pm1_status |= BITMASK_PM1STS_PWRBTN_STS;
|
||||
self.pm_sci();
|
||||
}
|
||||
}
|
||||
|
||||
const PM1_STATUS_LAST: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;
|
||||
const PM1_ENABLE_LAST: u16 = PM1_ENABLE + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;
|
||||
const PM1_CONTROL_LAST: u16 = PM1_CONTROL + ACPIPM_RESOURCE_CONTROLBLK_LEN as u16 - 1;
|
||||
|
@ -141,6 +171,7 @@ impl BusDevice for ACPIPMResource {
|
|||
v[j] = data[i];
|
||||
}
|
||||
self.pm1_enable = u16::from_ne_bytes(v);
|
||||
self.pm_sci(); // TODO take care of spurious interrupts
|
||||
}
|
||||
PM1_CONTROL..=PM1_CONTROL_LAST => {
|
||||
if data.len() > std::mem::size_of::<u16>()
|
||||
|
|
|
@ -107,6 +107,10 @@ impl Default for VmRunMode {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait PmResource {
|
||||
fn pwrbtn_evt(&mut self) {}
|
||||
}
|
||||
|
||||
/// The maximum number of devices that can be listed in one `UsbControlCommand`.
|
||||
///
|
||||
/// This value was set to be equal to `xhci_regs::MAX_PORTS` for convenience, but it is not
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::collections::BTreeMap;
|
|||
|
||||
use acpi_tables::{facs::FACS, rsdp::RSDP, sdt::SDT};
|
||||
use arch::VcpuAffinity;
|
||||
use base::error;
|
||||
use base::{error, warn};
|
||||
use data_model::DataInit;
|
||||
use devices::{PciAddress, PciInterruptPin};
|
||||
use vm_memory::{GuestAddress, GuestMemory};
|
||||
|
@ -105,7 +105,7 @@ const FADT_LOW_POWER_S2IDLE: u32 = 1 << 21;
|
|||
// FADT fields offset
|
||||
const FADT_FIELD_FACS_ADDR32: usize = 36;
|
||||
const FADT_FIELD_DSDT_ADDR32: usize = 40;
|
||||
const FADT_FIELD_SCI_INTERRUPT: usize = 46;
|
||||
pub const FADT_FIELD_SCI_INTERRUPT: usize = 46;
|
||||
const FADT_FIELD_SMI_COMMAND: usize = 48;
|
||||
const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
|
||||
const FADT_FIELD_PM1B_EVENT_BLK_ADDR: usize = 60;
|
||||
|
@ -188,7 +188,7 @@ fn create_facp_table(sci_irq: u16, reset_port: u32, reset_value: u8, force_s2idl
|
|||
OEM_REVISION,
|
||||
);
|
||||
|
||||
let mut fadt_flags: u32 = FADT_POWER_BUTTON | FADT_SLEEP_BUTTON | // mask POWER and SLEEP BUTTON
|
||||
let mut fadt_flags: u32 = FADT_SLEEP_BUTTON | // mask SLEEP BUTTON
|
||||
FADT_RESET_REGISTER; // indicate we support FADT RESET_REG
|
||||
|
||||
if force_s2idle {
|
||||
|
@ -485,9 +485,19 @@ pub fn create_acpi_tables(
|
|||
};
|
||||
|
||||
// FACP aka FADT
|
||||
let mut facp = facp.unwrap_or_else(|| {
|
||||
create_facp_table(sci_irq as u16, reset_port, reset_value, force_s2idle)
|
||||
});
|
||||
let mut facp = facp.map_or_else(
|
||||
|| create_facp_table(sci_irq as u16, reset_port, reset_value, force_s2idle),
|
||||
|facp| {
|
||||
let fadt_flags: u32 = facp.read(FADT_FIELD_FLAGS);
|
||||
if fadt_flags & FADT_POWER_BUTTON != 0 {
|
||||
warn!(
|
||||
"Control Method Power Button is not supported. FADT flags = 0x{:x}",
|
||||
fadt_flags
|
||||
);
|
||||
}
|
||||
facp
|
||||
},
|
||||
);
|
||||
|
||||
write_facp_overrides(
|
||||
&mut facp,
|
||||
|
|
|
@ -56,7 +56,7 @@ use crate::bootparam::boot_params;
|
|||
use acpi_tables::sdt::SDT;
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use arch::{get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, VmComponents, VmImage};
|
||||
use base::Event;
|
||||
use base::{warn, Event};
|
||||
use devices::serial_device::{SerialHardware, SerialParameters};
|
||||
use devices::{
|
||||
BusDeviceObj, BusResumeDevice, IrqChip, IrqChipX86_64, PciAddress, PciConfigIo, PciConfigMmio,
|
||||
|
@ -453,6 +453,24 @@ impl arch::LinuxArch for X8664arch {
|
|||
let tss_addr = GuestAddress(TSS_ADDR);
|
||||
vm.set_tss_addr(tss_addr).map_err(Error::SetTssAddr)?;
|
||||
|
||||
// Use IRQ info in ACPI if provided by the user.
|
||||
let mut noirq = true;
|
||||
let mut mptable = true;
|
||||
let mut sci_irq = X86_64_SCI_IRQ;
|
||||
|
||||
for sdt in components.acpi_sdts.iter() {
|
||||
if sdt.is_signature(b"DSDT") || sdt.is_signature(b"APIC") {
|
||||
noirq = false;
|
||||
} else if sdt.is_signature(b"FACP") {
|
||||
mptable = false;
|
||||
let sci_irq_fadt: u16 = sdt.read(acpi::FADT_FIELD_SCI_INTERRUPT);
|
||||
sci_irq = sci_irq_fadt.into();
|
||||
if !system_allocator.reserve_irq(sci_irq) {
|
||||
warn!("sci irq {} already reserved.", sci_irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mmio_bus = Arc::new(devices::Bus::new());
|
||||
let io_bus = Arc::new(devices::Bus::new());
|
||||
|
||||
|
@ -532,18 +550,13 @@ impl arch::LinuxArch for X8664arch {
|
|||
exit_evt.try_clone().map_err(Error::CloneEvent)?,
|
||||
components.acpi_sdts,
|
||||
irq_chip.as_irq_chip_mut(),
|
||||
sci_irq,
|
||||
battery,
|
||||
&mmio_bus,
|
||||
max_bus,
|
||||
&mut resume_notify_devices,
|
||||
)?;
|
||||
|
||||
// Use IRQ info in ACPI if provided by the user.
|
||||
let noirq = !acpi_dev_resource
|
||||
.sdts
|
||||
.iter()
|
||||
.any(|sdt| sdt.is_signature(b"DSDT") || sdt.is_signature(b"APIC"));
|
||||
|
||||
irq_chip
|
||||
.finalize_devices(system_allocator, &io_bus, &mmio_bus)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
|
@ -556,8 +569,11 @@ impl arch::LinuxArch for X8664arch {
|
|||
// If another guest does need a way to pass these tables down to it's BIOS, this approach
|
||||
// should be rethought.
|
||||
|
||||
// Note that this puts the mptable at 0x9FC00 in guest physical memory.
|
||||
mptable::setup_mptable(&mem, vcpu_count as u8, &pci_irqs).map_err(Error::SetupMptable)?;
|
||||
if mptable {
|
||||
// Note that this puts the mptable at 0x9FC00 in guest physical memory.
|
||||
mptable::setup_mptable(&mem, vcpu_count as u8, &pci_irqs)
|
||||
.map_err(Error::SetupMptable)?;
|
||||
}
|
||||
smbios::setup_smbios(&mem, components.dmi_path).map_err(Error::SetupSmbios)?;
|
||||
|
||||
let host_cpus = if components.host_cpu_topology {
|
||||
|
@ -570,7 +586,7 @@ impl arch::LinuxArch for X8664arch {
|
|||
acpi::create_acpi_tables(
|
||||
&mem,
|
||||
vcpu_count as u8,
|
||||
X86_64_SCI_IRQ,
|
||||
sci_irq,
|
||||
0xcf9,
|
||||
6, // RST_CPU|SYS_RST
|
||||
acpi_dev_resource,
|
||||
|
@ -1266,6 +1282,7 @@ impl X8664arch {
|
|||
exit_evt: Event,
|
||||
sdts: Vec<SDT>,
|
||||
irq_chip: &mut dyn IrqChip,
|
||||
sci_irq: u32,
|
||||
battery: (&Option<BatteryType>, Option<Minijail>),
|
||||
mmio_bus: &devices::Bus,
|
||||
max_bus: u8,
|
||||
|
@ -1288,10 +1305,14 @@ impl X8664arch {
|
|||
};
|
||||
|
||||
let pcie_vcfg = aml::Name::new("VCFG".into(), &Self::get_pcie_vcfg_mmio_base(mem));
|
||||
Aml::to_aml_bytes(&pcie_vcfg, &mut amls);
|
||||
pcie_vcfg.to_aml_bytes(&mut amls);
|
||||
|
||||
let pmresource = devices::ACPIPMResource::new(suspend_evt, exit_evt);
|
||||
Aml::to_aml_bytes(&pmresource, &mut amls);
|
||||
let pm_sci_evt = Event::new().map_err(Error::CreateEvent)?;
|
||||
irq_chip
|
||||
.register_irq_event(sci_irq, &pm_sci_evt, None)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
let pmresource = devices::ACPIPMResource::new(pm_sci_evt, suspend_evt, exit_evt);
|
||||
pmresource.to_aml_bytes(&mut amls);
|
||||
|
||||
let mut pci_dsdt_inner_data: Vec<&dyn aml::Aml> = Vec::new();
|
||||
let hid = aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08"));
|
||||
|
@ -1346,12 +1367,7 @@ impl X8664arch {
|
|||
match battery_type {
|
||||
BatteryType::Goldfish => {
|
||||
let control_tube = arch::add_goldfish_battery(
|
||||
&mut amls,
|
||||
battery.1,
|
||||
mmio_bus,
|
||||
irq_chip,
|
||||
X86_64_SCI_IRQ,
|
||||
resources,
|
||||
&mut amls, battery.1, mmio_bus, irq_chip, sci_irq, resources,
|
||||
)
|
||||
.map_err(Error::CreateBatDevices)?;
|
||||
Some(BatControl {
|
||||
|
|
|
@ -193,6 +193,7 @@ where
|
|||
exit_evt.try_clone().expect("unable to clone exit_evt"),
|
||||
Default::default(),
|
||||
&mut irq_chip,
|
||||
X86_64_SCI_IRQ,
|
||||
(&None, None),
|
||||
&mmio_bus,
|
||||
max_bus,
|
||||
|
|
Loading…
Reference in a new issue