From eb16dd5118dd0c1e9dd831daac4fd857d4579a76 Mon Sep 17 00:00:00 2001 From: Peter Fang Date: Mon, 20 Dec 2021 02:48:54 -0800 Subject: [PATCH] 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 Tested-by: kokoro Commit-Queue: Tomasz Nowicki --- devices/src/acpi.rs | 89 ++++++++++++++++++++++++++++++++++++++++++- vm_control/src/lib.rs | 1 + x86_64/src/acpi.rs | 44 +++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs index 0570910883..e6e1c652fe 100644 --- a/devices/src/acpi.rs +++ b/devices/src/acpi.rs @@ -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 +/// register’s length is defined to be half the length of the GPE0 register block, and is described +/// in the ACPI FADT’s 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: 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: 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::() + || 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::() + || 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::() + || 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::() + || 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); } diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs index 1815c3d526..c08d18b8b6 100644 --- a/vm_control/src/lib.rs +++ b/vm_control/src/lib.rs @@ -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`. diff --git a/x86_64/src/acpi.rs b/x86_64/src/acpi.rs index bef6e011f2..b90e3722bb 100644 --- a/x86_64/src/acpi.rs +++ b/x86_64/src/acpi.rs @@ -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 {