ACPI: Add register gpe notify event interface

Device want to get notification when a specific gpe happens, this commit
add register_gpe_notify_dev(gpe_num, dev) interface into PmResource trait,
so that others could register (gpe_num, dev) into acpi, then they
could get notification at specific gpe trigger.

BUG=b:185084350
TEST=Verify TBT pcie hotplug function in ManaTEE, virtual pcie root port
must get gpe notification during this process

Change-Id: I56eb90361a6b96be364d35c4b6a5ec598a50757e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3539566
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Xiong Zhang 2022-03-15 11:34:16 +08:00 committed by Chromeos LUCI
parent c26cf3d62c
commit 1b6e011dab
3 changed files with 72 additions and 4 deletions

View file

@ -6,11 +6,12 @@ use crate::{BusAccessInfo, BusDevice, BusResumeDevice};
use acpi_tables::{aml, aml::Aml};
use base::{error, info, warn, Error as SysError, Event, PollToken, WaitContext};
use base::{AcpiNotifyEvent, NetlinkGenericSocket};
use std::collections::BTreeMap;
use std::sync::Arc;
use std::thread;
use sync::Mutex;
use thiserror::Error;
use vm_control::PmResource;
use vm_control::{GpeNotify, PmResource};
#[cfg(feature = "direct")]
use {std::fs, std::io::Error as IoError, std::path::PathBuf};
@ -38,6 +39,7 @@ struct Pm1Resource {
struct GpeResource {
status: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
enable: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
gpe_notify: BTreeMap<u32, Vec<Arc<Mutex<dyn GpeNotify>>>>,
}
#[cfg(feature = "direct")]
@ -85,6 +87,7 @@ impl ACPIPMResource {
let gpe0 = GpeResource {
status: Default::default(),
enable: Default::default(),
gpe_notify: BTreeMap::new(),
};
#[cfg(feature = "direct")]
@ -258,6 +261,14 @@ fn run_worker(
if let Some((ref trigger, ref resample)) = sci_direct_evt {
let _ = trigger.read();
for (gpe, devs) in &gpe0.lock().gpe_notify {
if DirectGpe::is_gpe_trigger(*gpe).unwrap_or(false) {
for dev in devs {
dev.lock().notify();
}
}
}
if let Err(e) = sci_evt.write(1) {
error!("ACPIPM: failed to trigger sci event: {}", e);
}
@ -387,7 +398,29 @@ impl Pm1Resource {
impl GpeResource {
fn trigger_sci(&self, sci_evt: &Event) {
if (0..self.status.len()).any(|i| self.status[i] & self.enable[i] != 0) {
let mut trigger = false;
for i in 0..self.status.len() {
let gpes = self.status[i] & self.enable[i];
if gpes == 0 {
continue;
}
for j in 0..8 {
if gpes & (1 << j) == 0 {
continue;
}
let gpe_num: u32 = i as u32 * 8 + j;
if let Some(notify_devs) = self.gpe_notify.get(&gpe_num) {
for notify_dev in notify_devs.iter() {
notify_dev.lock().notify();
}
}
}
trigger = true;
}
if trigger {
if let Err(e) = sci_evt.write(1) {
error!("ACPIPM: failed to trigger sci event for gpe: {}", e);
}
@ -478,6 +511,22 @@ impl DirectGpe {
}
}
}
fn is_gpe_trigger(gpe: u32) -> Result<bool, IoError> {
let path = PathBuf::from("/sys/firmware/acpi/interrupts").join(format!("gpe{:02X}", gpe));
let s = fs::read_to_string(&path)?;
let mut enable = false;
let mut status = false;
for itr in s.split_whitespace() {
match itr {
"EN" => enable = true,
"STS" => status = true,
_ => (),
}
}
Ok(enable && status)
}
}
/// the ACPI PM register length.
@ -558,6 +607,16 @@ impl PmResource for ACPIPMResource {
gpe0.status[byte] |= 1 << (gpe % 8);
gpe0.trigger_sci(&self.sci_evt);
}
fn register_gpe_notify_dev(&mut self, gpe: u32, notify_dev: Arc<Mutex<dyn GpeNotify>>) {
let mut gpe0 = self.gpe0.lock();
match gpe0.gpe_notify.get_mut(&gpe) {
Some(v) => v.push(notify_dev),
None => {
gpe0.gpe_notify.insert(gpe, vec![notify_dev]);
}
}
}
}
const PM1_STATUS_LAST: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;

View file

@ -678,6 +678,7 @@ fn create_pcie_root_port(
devices: &mut Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
hp_vec: &mut Vec<Arc<Mutex<dyn HotPlugBus>>>,
hp_endpoints_ranges: &mut Vec<RangeInclusive<u32>>,
_gpe_notify_devs: &mut Vec<(u32, Arc<Mutex<dyn GpeNotify>>)>,
) -> Result<()> {
if host_pcie_rp.is_empty() {
// user doesn't specify host pcie root port which link to this virtual pcie rp,
@ -1265,10 +1266,10 @@ where
)?;
let mut hp_endpoints_ranges: Vec<RangeInclusive<u32>> = Vec::new();
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
let mut hotplug_buses: Vec<Arc<Mutex<dyn HotPlugBus>>> = Vec::new();
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
let mut gpe_notify_devs: Vec<(u32, Arc<Mutex<dyn GpeNotify>>)> = Vec::new();
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
#[cfg(feature = "direct")]
@ -1284,6 +1285,7 @@ where
&mut devices,
&mut hotplug_buses,
&mut hp_endpoints_ranges,
&mut gpe_notify_devs,
)?;
}
@ -1359,6 +1361,12 @@ where
for hotplug_bus in hotplug_buses.iter() {
linux.hotplug_bus.push(hotplug_bus.clone());
}
if let Some(pm) = &linux.pm {
while let Some((gpe, notify_dev)) = gpe_notify_devs.pop() {
pm.lock().register_gpe_notify_dev(gpe, notify_dev);
}
}
}
#[cfg(feature = "direct")]

View file

@ -116,6 +116,7 @@ pub trait GpeNotify: Send {
pub trait PmResource {
fn pwrbtn_evt(&mut self) {}
fn gpe_evt(&mut self, _gpe: u32) {}
fn register_gpe_notify_dev(&mut self, _gpe: u32, _notify_dev: Arc<Mutex<dyn GpeNotify>>) {}
}
/// The maximum number of devices that can be listed in one `UsbControlCommand`.