blob: 38c6f1bf8a7ec13bc0d5e1c507fef03823aa06bd [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::sync::Arc;
use acpi_tables::aml;
use acpi_tables::aml::Aml;
use base::warn;
use sync::Condvar;
use sync::Mutex;
use crate::pci::CrosvmDeviceId;
use crate::BusAccessInfo;
use crate::BusDevice;
use crate::DeviceId;
use crate::Suspendable;
/// PMC Virt MMIO offset
const PMC_RESERVED1: u32 = 0;
const _PMC_RESERVED2: u32 = 0x4;
const _PMC_RESERVED3: u32 = 0x8;
const _PMC_RESERVED4: u32 = 0xc;
pub const VPMC_VIRT_MMIO_SIZE: u64 = 0x10;
pub struct VirtualPmc {
mmio_base: u64,
guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>,
}
impl VirtualPmc {
pub fn new(mmio_base: u64, guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>) -> Self {
VirtualPmc {
mmio_base,
guest_suspended_cvar,
}
}
}
fn handle_s2idle_request(guest_suspended_cvar: &Arc<(Mutex<bool>, Condvar)>) {
// Wake up blocked thread on condvar, which is awaiting non-privileged guest suspension to
// finish.
let (lock, cvar) = &**guest_suspended_cvar;
let mut guest_suspended = lock.lock();
*guest_suspended = true;
cvar.notify_one();
}
impl BusDevice for VirtualPmc {
fn device_id(&self) -> DeviceId {
CrosvmDeviceId::VirtualPmc.into()
}
fn debug_label(&self) -> String {
"PmcVirt".to_owned()
}
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
if data.len() != std::mem::size_of::<u32>() {
warn!(
"{}: unsupported read length {}, only support 4bytes read",
self.debug_label(),
data.len()
);
return;
}
warn!("{}: unsupported read address {}", self.debug_label(), info);
}
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
if data.len() != std::mem::size_of::<u32>() {
warn!(
"{}: unsupported write length {}, only support 4bytes write",
self.debug_label(),
data.len()
);
return;
}
match info.offset as u32 {
PMC_RESERVED1 => {
handle_s2idle_request(&self.guest_suspended_cvar);
}
_ => {
warn!("{}: Bad write to address {}", self.debug_label(), info);
}
};
}
}
impl Aml for VirtualPmc {
fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
let vpmc_uuid = "9ea49ba3-434a-49a6-be30-37cc55c4d397";
aml::Device::new(
"VPMC".into(),
vec![
&aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0D80")),
&aml::Name::new("_HID".into(), &"HYPE0001"),
&aml::Name::new("UUID".into(), &aml::Uuid::new(vpmc_uuid)),
&aml::OpRegion::new(
"VREG".into(),
aml::OpRegionSpace::SystemMemory,
&self.mmio_base,
&(16_u32),
),
&aml::Field::new(
"VREG".into(),
aml::FieldAccessType::DWord,
aml::FieldLockRule::Lock,
aml::FieldUpdateRule::Preserve,
vec![aml::FieldEntry::Named(*b"RSV1", 32)],
),
&aml::Method::new(
"_DSM".into(),
4,
true,
vec![
&aml::If::new(
&aml::Equal::new(&aml::Arg(0), &aml::Name::new_field_name("UUID")),
vec![
&aml::If::new(
&aml::Equal::new(&aml::Arg(2), &aml::ZERO),
vec![&aml::Return::new(&aml::BufferData::new(vec![3]))],
),
&aml::If::new(
&aml::Equal::new(&aml::Arg(2), &aml::ONE),
vec![&aml::Store::new(
&aml::Name::new_field_name("RSV1"),
&0x3_usize,
)],
),
],
),
&aml::Return::new(&aml::BufferData::new(vec![3])),
],
),
],
)
.to_aml_bytes(bytes);
}
}
impl Suspendable for VirtualPmc {}