xref: /aosp_15_r20/external/crosvm/devices/src/pmc_virt.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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