acpi: support vGPE

- Add vGPE registers to ACPIPMResource and inject vSCI when a GPE is
  enabled and its event is received.
- Add a new interface, gpe_evt(), to trait PmResource.
- Always use vGPE, regardless of FADT forwarding.
- Always advertise support for 256 GPEs [1] to reduce code complexity.

[1] "Up to 256 GPEx_STS bits and matching GPEx_EN bits can be
    implemented." 5.6.1, ACPI Spec Version 6.4

BUG=b:199383670
TEST=boot Linux kernel and inspect /sys/firmware/acpi/interrupts/

Change-Id: I97687326e9313c26b84dfacade5c8741719e7841
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3350493
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:
Peter Fang 2021-12-20 02:48:54 -08:00 committed by Commit Bot
parent 6ca0323c87
commit eb16dd5118
3 changed files with 133 additions and 1 deletions

View file

@ -16,6 +16,8 @@ pub struct ACPIPMResource {
pm1_status: u16,
pm1_enable: u16,
pm1_control: u16,
gpe0_status: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
gpe0_enable: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
}
impl ACPIPMResource {
@ -29,6 +31,8 @@ impl ACPIPMResource {
pm1_status: 0,
pm1_enable: 0,
pm1_control: 0,
gpe0_status: Default::default(),
gpe0_enable: Default::default(),
}
}
@ -46,12 +50,24 @@ impl ACPIPMResource {
}
}
}
fn gpe_sci(&self) {
for i in 0..self.gpe0_status.len() {
if self.gpe0_status[i] & self.gpe0_enable[i] != 0 {
if let Err(e) = self.sci_evt.write(1) {
error!("ACPIPM: failed to trigger sci event: {}", e);
}
return;
}
}
}
}
/// the ACPI PM register length.
pub const ACPIPM_RESOURCE_LEN: u8 = 8;
pub const ACPIPM_RESOURCE_EVENTBLK_LEN: u8 = 4;
pub const ACPIPM_RESOURCE_CONTROLBLK_LEN: u8 = 2;
pub const ACPIPM_RESOURCE_GPE0_BLK_LEN: u8 = 64;
pub const ACPIPM_RESOURCE_LEN: u8 = ACPIPM_RESOURCE_EVENTBLK_LEN + 4 + ACPIPM_RESOURCE_GPE0_BLK_LEN;
/// ACPI PM register value definitions
@ -71,6 +87,26 @@ 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;
/// 4.8.5.1 General-Purpose Event Register Blocks, ACPI Spec Version 6.4
/// - Each register block contains two registers: an enable and a status register.
/// - Each register block is 32-bit aligned.
/// - Each register in the block is accessed as a byte.
/// 4.8.5.1.1 General-Purpose Event 0 Register Block, ACPI Spec Version 6.4
/// This register block consists of two registers: The GPE0_STS and the GPE0_EN registers. Each
/// registers length is defined to be half the length of the GPE0 register block, and is described
/// in the ACPI FADTs GPE0_BLK and GPE0_BLK_LEN operators.
/// 4.8.5.1.1.1 General-Purpose Event 0 Status Register, ACPI Spec Version 6.4
/// Register Location: <GPE0_STS> System I/O or System Memory Space (defined in FADT)
/// Size: GPE0_BLK_LEN/2 (defined in FADT)
const GPE0_STATUS: u16 = PM1_STATUS + ACPIPM_RESOURCE_EVENTBLK_LEN as u16 + 4; // ensure alignment
/// 4.8.5.1.1.2 General-Purpose Event 0 Enable Register, ACPI Spec Version 6.4
/// Register Location: <GPE0_EN> System I/O or System Memory Space (defined in FADT)
/// Size: GPE0_BLK_LEN/2 (defined in FADT)
const GPE0_ENABLE: u16 = GPE0_STATUS + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2);
const BITMASK_PM1STS_PWRBTN_STS: u16 = 1 << 8;
const BITMASK_PM1EN_GBL_EN: u16 = 1 << 5;
const BITMASK_PM1EN_PWRBTN_EN: u16 = 1 << 8;
@ -91,11 +127,23 @@ impl PmResource for ACPIPMResource {
self.pm1_status |= BITMASK_PM1STS_PWRBTN_STS;
self.pm_sci();
}
fn gpe_evt(&mut self, gpe: u32) {
let byte = gpe as usize / 8;
if byte >= self.gpe0_status.len() {
error!("gpe_evt: GPE register {} does not exist", byte);
return;
}
self.gpe0_status[byte] |= 1 << (gpe % 8);
self.gpe_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;
const GPE0_STATUS_LAST: u16 = GPE0_STATUS + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2) - 1;
const GPE0_ENABLE_LAST: u16 = GPE0_ENABLE + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2) - 1;
impl BusDevice for ACPIPMResource {
fn debug_label(&self) -> String {
@ -135,6 +183,25 @@ impl BusDevice for ACPIPMResource {
let offset = (info.offset - PM1_CONTROL as u64) as usize;
data.copy_from_slice(&self.pm1_control.to_ne_bytes()[offset..offset + data.len()]);
}
// OSPM accesses GPE registers through byte accesses (regardless of their length)
GPE0_STATUS..=GPE0_STATUS_LAST => {
if data.len() > std::mem::size_of::<u8>()
|| info.offset + data.len() as u64 > (GPE0_STATUS_LAST + 1).into()
{
warn!("ACPIPM: bad read size: {}", data.len());
return;
}
data[0] = self.gpe0_status[(info.offset - GPE0_STATUS as u64) as usize];
}
GPE0_ENABLE..=GPE0_ENABLE_LAST => {
if data.len() > std::mem::size_of::<u8>()
|| info.offset + data.len() as u64 > (GPE0_ENABLE_LAST + 1).into()
{
warn!("ACPIPM: bad read size: {}", data.len());
return;
}
data[0] = self.gpe0_enable[(info.offset - GPE0_ENABLE as u64) as usize];
}
_ => {
warn!("ACPIPM: Bad read from {}", info);
}
@ -214,6 +281,26 @@ impl BusDevice for ACPIPMResource {
}
self.pm1_control = val & !BITMASK_PM1CNT_SLEEP_ENABLE;
}
// OSPM accesses GPE registers through byte accesses (regardless of their length)
GPE0_STATUS..=GPE0_STATUS_LAST => {
if data.len() > std::mem::size_of::<u8>()
|| info.offset + data.len() as u64 > (GPE0_STATUS_LAST + 1).into()
{
warn!("ACPIPM: bad write size: {}", data.len());
return;
}
self.gpe0_status[(info.offset - GPE0_STATUS as u64) as usize] &= !data[0];
}
GPE0_ENABLE..=GPE0_ENABLE_LAST => {
if data.len() > std::mem::size_of::<u8>()
|| info.offset + data.len() as u64 > (GPE0_ENABLE_LAST + 1).into()
{
warn!("ACPIPM: bad write size: {}", data.len());
return;
}
self.gpe0_enable[(info.offset - GPE0_ENABLE as u64) as usize] = data[0];
self.gpe_sci();
}
_ => {
warn!("ACPIPM: Bad write to {}", info);
}

View file

@ -109,6 +109,7 @@ impl Default for VmRunMode {
pub trait PmResource {
fn pwrbtn_evt(&mut self) {}
fn gpe_evt(&mut self, _gpe: u32) {}
}
/// The maximum number of devices that can be listed in one `UsbControlCommand`.

View file

@ -115,9 +115,14 @@ const FADT_FIELD_PM1B_EVENT_BLK_ADDR: usize = 60;
const FADT_FIELD_PM1A_CONTROL_BLK_ADDR: usize = 64;
const FADT_FIELD_PM1B_CONTROL_BLK_ADDR: usize = 68;
const FADT_FIELD_PM2_CONTROL_BLK_ADDR: usize = 72;
const FADT_FIELD_GPE0_BLK_ADDR: usize = 80;
const FADT_FIELD_GPE1_BLK_ADDR: usize = 84;
const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
const FADT_FIELD_PM2_CONTROL_BLK_LEN: usize = 90;
const FADT_FIELD_GPE0_BLK_LEN: usize = 92;
const FADT_FIELD_GPE1_BLK_LEN: usize = 93;
const FADT_FIELD_GPE1_BASE: usize = 94;
const FADT_FIELD_FLAGS: usize = 112;
const FADT_FIELD_RESET_REGISTER: usize = 116;
const FADT_FIELD_RESET_VALUE: usize = 128;
@ -129,6 +134,8 @@ const FADT_FIELD_X_PM1B_EVENT_BLK_ADDR: usize = 160;
const FADT_FIELD_X_PM1A_CONTROL_BLK_ADDR: usize = 172;
const FADT_FIELD_X_PM1B_CONTROL_BLK_ADDR: usize = 184;
const FADT_FIELD_X_PM2_CONTROL_BLK_ADDR: usize = 196;
const FADT_FIELD_X_GPE0_BLK_ADDR: usize = 220;
const FADT_FIELD_X_GPE1_BLK_ADDR: usize = 232;
const FADT_FIELD_HYPERVISOR_ID: usize = 268;
// MADT
const MADT_LEN: u32 = 44;
@ -253,6 +260,15 @@ fn write_facp_overrides(
// PM2 Control Block Address (not supported)
facp.write(FADT_FIELD_PM2_CONTROL_BLK_ADDR, 0u32);
// GPE0 Block Address
facp.write(
FADT_FIELD_GPE0_BLK_ADDR,
pm_iobase + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32 + 4,
);
// GPE1 Block Address (not supported)
facp.write(FADT_FIELD_GPE1_BLK_ADDR, 0u32);
// PM1 Event Block Length
facp.write(
FADT_FIELD_PM1A_EVENT_BLK_LEN,
@ -268,6 +284,18 @@ fn write_facp_overrides(
// PM2 Control Block Length (not supported)
facp.write(FADT_FIELD_PM2_CONTROL_BLK_LEN, 0u8);
// GPE0 Block Length
facp.write(
FADT_FIELD_GPE0_BLK_LEN,
devices::acpi::ACPIPM_RESOURCE_GPE0_BLK_LEN,
);
// GPE1 Block Length (not supported)
facp.write(FADT_FIELD_GPE1_BLK_LEN, 0u8);
// GPE1 Base (not supported)
facp.write(FADT_FIELD_GPE1_BASE, 0u8);
// PM1A Extended Event Block Address (not supported)
facp.write(
FADT_FIELD_X_PM1A_EVENT_BLK_ADDR,
@ -307,6 +335,22 @@ fn write_facp_overrides(
..Default::default()
},
);
// GPE0 Extended Address (not supported)
facp.write(
FADT_FIELD_X_GPE0_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
// GPE1 Extended Address (not supported)
facp.write(
FADT_FIELD_X_GPE1_BLK_ADDR,
GenericAddress {
..Default::default()
},
);
}
fn next_offset(offset: GuestAddress, len: u64) -> Option<GuestAddress> {