// 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, Condvar)>, } impl VirtualPmc { pub fn new(mmio_base: u64, guest_suspended_cvar: Arc<(Mutex, Condvar)>) -> Self { VirtualPmc { mmio_base, guest_suspended_cvar, } } } fn handle_s2idle_request(guest_suspended_cvar: &Arc<(Mutex, 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::() { 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::() { 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) { 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 {}