1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::sync::Arc;
6
7 use acpi_tables::aml;
8 use acpi_tables::aml::Aml;
9 use base::warn;
10 use sync::Condvar;
11 use sync::Mutex;
12
13 use crate::pci::CrosvmDeviceId;
14 use crate::BusAccessInfo;
15 use crate::BusDevice;
16 use crate::DeviceId;
17 use crate::Suspendable;
18
19 /// PMC Virt MMIO offset
20 const PMC_RESERVED1: u32 = 0;
21 const _PMC_RESERVED2: u32 = 0x4;
22 const _PMC_RESERVED3: u32 = 0x8;
23 const _PMC_RESERVED4: u32 = 0xc;
24
25 pub const VPMC_VIRT_MMIO_SIZE: u64 = 0x10;
26
27 pub struct VirtualPmc {
28 mmio_base: u64,
29 guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>,
30 }
31
32 impl VirtualPmc {
new(mmio_base: u64, guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>) -> Self33 pub fn new(mmio_base: u64, guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>) -> Self {
34 VirtualPmc {
35 mmio_base,
36 guest_suspended_cvar,
37 }
38 }
39 }
40
handle_s2idle_request(guest_suspended_cvar: &Arc<(Mutex<bool>, Condvar)>)41 fn handle_s2idle_request(guest_suspended_cvar: &Arc<(Mutex<bool>, Condvar)>) {
42 // Wake up blocked thread on condvar, which is awaiting non-privileged guest suspension to
43 // finish.
44 let (lock, cvar) = &**guest_suspended_cvar;
45 let mut guest_suspended = lock.lock();
46 *guest_suspended = true;
47
48 cvar.notify_one();
49 }
50
51 impl BusDevice for VirtualPmc {
device_id(&self) -> DeviceId52 fn device_id(&self) -> DeviceId {
53 CrosvmDeviceId::VirtualPmc.into()
54 }
debug_label(&self) -> String55 fn debug_label(&self) -> String {
56 "PmcVirt".to_owned()
57 }
58
read(&mut self, info: BusAccessInfo, data: &mut [u8])59 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
60 if data.len() != std::mem::size_of::<u32>() {
61 warn!(
62 "{}: unsupported read length {}, only support 4bytes read",
63 self.debug_label(),
64 data.len()
65 );
66 return;
67 }
68
69 warn!("{}: unsupported read address {}", self.debug_label(), info);
70 }
71
write(&mut self, info: BusAccessInfo, data: &[u8])72 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
73 if data.len() != std::mem::size_of::<u32>() {
74 warn!(
75 "{}: unsupported write length {}, only support 4bytes write",
76 self.debug_label(),
77 data.len()
78 );
79 return;
80 }
81
82 match info.offset as u32 {
83 PMC_RESERVED1 => {
84 handle_s2idle_request(&self.guest_suspended_cvar);
85 }
86 _ => {
87 warn!("{}: Bad write to address {}", self.debug_label(), info);
88 }
89 };
90 }
91 }
92
93 impl Aml for VirtualPmc {
to_aml_bytes(&self, bytes: &mut Vec<u8>)94 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
95 let vpmc_uuid = "9ea49ba3-434a-49a6-be30-37cc55c4d397";
96 aml::Device::new(
97 "VPMC".into(),
98 vec![
99 &aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0D80")),
100 &aml::Name::new("_HID".into(), &"HYPE0001"),
101 &aml::Name::new("UUID".into(), &aml::Uuid::new(vpmc_uuid)),
102 &aml::OpRegion::new(
103 "VREG".into(),
104 aml::OpRegionSpace::SystemMemory,
105 &self.mmio_base,
106 &(16_u32),
107 ),
108 &aml::Field::new(
109 "VREG".into(),
110 aml::FieldAccessType::DWord,
111 aml::FieldLockRule::Lock,
112 aml::FieldUpdateRule::Preserve,
113 vec![aml::FieldEntry::Named(*b"RSV1", 32)],
114 ),
115 &aml::Method::new(
116 "_DSM".into(),
117 4,
118 true,
119 vec![
120 &aml::If::new(
121 &aml::Equal::new(&aml::Arg(0), &aml::Name::new_field_name("UUID")),
122 vec![
123 &aml::If::new(
124 &aml::Equal::new(&aml::Arg(2), &aml::ZERO),
125 vec![&aml::Return::new(&aml::BufferData::new(vec![3]))],
126 ),
127 &aml::If::new(
128 &aml::Equal::new(&aml::Arg(2), &aml::ONE),
129 vec![&aml::Store::new(
130 &aml::Name::new_field_name("RSV1"),
131 &0x3_usize,
132 )],
133 ),
134 ],
135 ),
136 &aml::Return::new(&aml::BufferData::new(vec![3])),
137 ],
138 ),
139 ],
140 )
141 .to_aml_bytes(bytes);
142 }
143 }
144
145 impl Suspendable for VirtualPmc {}
146