pcie: Implement added GpeNotify() trait

When specific GPE event happens, some devices want to get notification.
For example, virtual pcie root port wants to get notification when
acpi hotplug GPE occurs. So virtual pcie root port implements this trait.

When virtual pcie root port gets this hotplug GPE notification, it will
inject a PME into CrOS, CrOS PME handler will wakeup virtual pcie root
port and TBT DMA engine, and forbit them to enter into suspend again
during hotplug process.

Once hotplug process is finished, virtual pcie root port and TBT DMA
engine could be allowed to suspend again.

BUG=b:185084350
TEST=Verify TBT pcie hotplug function in ManaTEE

Change-Id: I6c984e4f81713ad34383f1fb4ea2a5776ac2fccf
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3539565
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 2021-12-24 14:50:16 +08:00 committed by Chromeos LUCI
parent b0d2e4da00
commit 5631839e90
2 changed files with 36 additions and 14 deletions

View file

@ -17,6 +17,7 @@ use anyhow::{anyhow, Result};
use base::warn;
use data_model::DataInit;
use resources::{Alloc, SystemAllocator};
use vm_control::GpeNotify;
// reserve 8MB memory window
const PCIE_RP_BR_MEM_SIZE: u64 = 0x80_0000;
@ -255,25 +256,29 @@ impl PcieRootPort {
}
}
fn inject_pme(&mut self) {
if (self.root_status & PCIE_ROOTSTA_PME_STATUS) != 0 {
self.root_status |= PCIE_ROOTSTA_PME_PENDING;
self.pme_pending_request_id = self.pci_address;
} else {
let request_id = self.pci_address.unwrap();
let req_id = ((request_id.bus as u32) << 8)
| ((request_id.dev as u32) << 3)
| (request_id.func as u32);
self.root_status &= !PCIE_ROOTSTA_PME_REQ_ID_MASK;
self.root_status |= req_id;
self.pme_pending_request_id = None;
self.root_status |= PCIE_ROOTSTA_PME_STATUS;
self.trigger_pme_interrupt();
}
}
// when RP is D3, HP interrupt is disabled by pcie driver, so inject a PME to wakeup
// RP first, then inject HP interrupt.
fn trigger_hp_or_pme_interrupt(&mut self) {
if self.pmc_config.should_trigger_pme() {
self.hp_interrupt_pending = true;
if (self.root_status & PCIE_ROOTSTA_PME_STATUS) != 0 {
self.root_status |= PCIE_ROOTSTA_PME_PENDING;
self.pme_pending_request_id = self.pci_address;
} else {
let request_id = self.pci_address.unwrap();
let req_id = ((request_id.bus as u32) << 8)
| ((request_id.dev as u32) << 3)
| (request_id.func as u32);
self.root_status &= !PCIE_ROOTSTA_PME_REQ_ID_MASK;
self.root_status |= req_id;
self.pme_pending_request_id = None;
self.root_status |= PCIE_ROOTSTA_PME_STATUS;
self.trigger_pme_interrupt();
}
self.inject_pme();
} else {
self.trigger_hp_interrupt();
}
@ -487,3 +492,15 @@ impl HotPlugBus for PcieRootPort {
None
}
}
impl GpeNotify for PcieRootPort {
fn notify(&mut self) {
if self.slot_control.is_none() {
return;
}
if self.pmc_config.should_trigger_pme() {
self.inject_pme();
}
}
}

View file

@ -107,6 +107,11 @@ impl Default for VmRunMode {
}
}
// Trait for devices that get notification on specific GPE trigger
pub trait GpeNotify: Send {
fn notify(&mut self) {}
}
pub trait PmResource {
fn pwrbtn_evt(&mut self) {}
fn gpe_evt(&mut self, _gpe: u32) {}