devices/
pmc_virt.rs

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
5use std::sync::Arc;
6
7use acpi_tables::aml;
8use acpi_tables::aml::Aml;
9use base::warn;
10use sync::Condvar;
11use sync::Mutex;
12
13use crate::pci::CrosvmDeviceId;
14use crate::BusAccessInfo;
15use crate::BusDevice;
16use crate::DeviceId;
17use crate::Suspendable;
18
19/// PMC Virt MMIO offset
20const PMC_RESERVED1: u32 = 0;
21const _PMC_RESERVED2: u32 = 0x4;
22const _PMC_RESERVED3: u32 = 0x8;
23const _PMC_RESERVED4: u32 = 0xc;
24
25pub const VPMC_VIRT_MMIO_SIZE: u64 = 0x10;
26
27pub struct VirtualPmc {
28    mmio_base: u64,
29    guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>,
30}
31
32impl VirtualPmc {
33    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
41fn 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
51impl BusDevice for VirtualPmc {
52    fn device_id(&self) -> DeviceId {
53        CrosvmDeviceId::VirtualPmc.into()
54    }
55    fn debug_label(&self) -> String {
56        "PmcVirt".to_owned()
57    }
58
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
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
93impl Aml for VirtualPmc {
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
145impl Suspendable for VirtualPmc {}