devices/power/
hvc.rs

1// Copyright 2026 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 anyhow::bail;
6use anyhow::Context;
7use hypervisor::HypercallAbi;
8use sync::Mutex;
9use vm_control::DeviceId;
10use vm_control::PlatformDeviceId;
11
12use crate::BusAccessInfo;
13use crate::BusDevice;
14use crate::BusDeviceSync;
15use crate::DevicePowerManager;
16use crate::Suspendable;
17
18/// Back-end for pKVM-enabled power management, intended for physical assigned devices.
19pub struct HvcDevicePowerManager {
20    manager: Mutex<DevicePowerManager>,
21}
22
23impl HvcDevicePowerManager {
24    pub const HVC_FUNCTION_ID: u32 = 0xc600003c;
25
26    /// Takes ownership of the abstract `DevicePowerManager`, as a `HvcDevicePowerManager`.
27    pub fn new(manager: DevicePowerManager) -> Self {
28        Self {
29            manager: Mutex::new(manager),
30        }
31    }
32
33    fn set_power_state(&self, domain_id: usize, power_level: usize) -> anyhow::Result<()> {
34        match power_level {
35            0 => self.manager.lock().power_off(domain_id)?,
36            _ => self.manager.lock().power_on(domain_id)?,
37        };
38        Ok(())
39    }
40}
41
42impl BusDevice for HvcDevicePowerManager {
43    fn device_id(&self) -> DeviceId {
44        PlatformDeviceId::HvcDevicePowerManager.into()
45    }
46
47    fn debug_label(&self) -> String {
48        "HvcDevicePowerManager".to_owned()
49    }
50
51    fn handle_hypercall(&self, abi: &mut HypercallAbi) -> anyhow::Result<()> {
52        match abi.hypercall_id() as u32 {
53            Self::HVC_FUNCTION_ID => {
54                let domain_id = *abi.get_argument(0).unwrap();
55                let power_level = *abi.get_argument(1).unwrap();
56
57                let ok_or_err = self
58                    .set_power_state(domain_id, power_level)
59                    .with_context(|| {
60                        format!(
61                            "HvcDevicePowerManager::handle_hypercall({domain_id}, {power_level})"
62                        )
63                    });
64
65                let r0 = if ok_or_err.is_ok() {
66                    0 // SMCCC_SUCCESS
67                } else {
68                    -3i64 as _ // SMCCC_INVALID_PARAMETER
69                };
70
71                abi.set_results(&[r0, 0, 0, 0]);
72
73                ok_or_err
74            }
75            fid => bail!("HvcDevicePowerManager: Call {fid:#x} is not implemented"),
76        }
77    }
78
79    fn read(&mut self, _info: BusAccessInfo, _data: &mut [u8]) {
80        unimplemented!("HvcDevicePowerManager: read not supported");
81    }
82
83    fn write(&mut self, _info: BusAccessInfo, _data: &[u8]) {
84        unimplemented!("HvcDevicePowerManager: write not supported");
85    }
86}
87
88impl BusDeviceSync for HvcDevicePowerManager {
89    fn read(&self, _info: BusAccessInfo, _data: &mut [u8]) {
90        unimplemented!("HvcDevicePowerManager: read not supported");
91    }
92
93    fn write(&self, _info: BusAccessInfo, _data: &[u8]) {
94        unimplemented!("HvcDevicePowerManager: write not supported");
95    }
96}
97
98impl Suspendable for HvcDevicePowerManager {}