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 handle(&self, abi: &HypercallAbi) -> anyhow::Result<()> {
34        let call = DeviceManagerHypercall::new(abi)
35            .with_context(|| format!("Invalid HvcDevicePowerManager call: {abi:?}"))?;
36        match call {
37            DeviceManagerHypercall::PowerOff(d) => self.manager.lock().power_off(d),
38            DeviceManagerHypercall::PowerOn(d) => self.manager.lock().power_on(d),
39        }
40        .with_context(|| format!("HvcDevicePowerManager::handle({call:?})"))
41    }
42}
43
44impl BusDevice for HvcDevicePowerManager {
45    fn device_id(&self) -> DeviceId {
46        PlatformDeviceId::HvcDevicePowerManager.into()
47    }
48
49    fn debug_label(&self) -> String {
50        "HvcDevicePowerManager".to_owned()
51    }
52
53    fn handle_hypercall(&self, abi: &mut HypercallAbi) -> anyhow::Result<()> {
54        match abi.hypercall_id() as u32 {
55            Self::HVC_FUNCTION_ID => {
56                let ok_or_err = self.handle(abi);
57                let r0 = if ok_or_err.is_ok() {
58                    0 // SMCCC_SUCCESS
59                } else {
60                    -3i64 as _ // SMCCC_INVALID_PARAMETER
61                };
62
63                abi.set_results(&[r0, 0, 0, 0]);
64
65                ok_or_err
66            }
67            fid => bail!("HvcDevicePowerManager: Call {fid:#x} is not implemented"),
68        }
69    }
70
71    fn read(&mut self, _info: BusAccessInfo, _data: &mut [u8]) {
72        unimplemented!("HvcDevicePowerManager: read not supported");
73    }
74
75    fn write(&mut self, _info: BusAccessInfo, _data: &[u8]) {
76        unimplemented!("HvcDevicePowerManager: write not supported");
77    }
78}
79
80impl BusDeviceSync for HvcDevicePowerManager {
81    fn read(&self, _info: BusAccessInfo, _data: &mut [u8]) {
82        unimplemented!("HvcDevicePowerManager: read not supported");
83    }
84
85    fn write(&self, _info: BusAccessInfo, _data: &[u8]) {
86        unimplemented!("HvcDevicePowerManager: write not supported");
87    }
88}
89
90impl Suspendable for HvcDevicePowerManager {}
91
92#[derive(Debug)]
93enum DeviceManagerHypercall {
94    PowerOff(usize),
95    PowerOn(usize),
96}
97
98impl DeviceManagerHypercall {
99    fn new(abi: &HypercallAbi) -> Option<Self> {
100        let func = *abi.get_argument(0).unwrap();
101        let arg0 = *abi.get_argument(1).unwrap();
102
103        match func {
104            0 => Some(Self::PowerOff(arg0)),
105            1 => Some(Self::PowerOn(arg0)),
106            _ => None,
107        }
108    }
109}