1pub(crate) mod hvc;
8
9use std::collections::BTreeMap;
10use std::sync::Arc;
11
12use anyhow::bail;
13use anyhow::Context;
14use base::warn;
15use sync::Mutex;
16
17use crate::BusDevice;
18
19pub struct DevicePowerManager {
21 domains: Mutex<BTreeMap<usize, PowerDomain>>,
22}
23
24impl Default for DevicePowerManager {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl DevicePowerManager {
31 pub fn new() -> Self {
32 Self {
33 domains: Mutex::new(BTreeMap::new()),
34 }
35 }
36
37 pub fn attach(
41 &mut self,
42 device: Arc<Mutex<dyn BusDevice>>,
43 domain_id: usize,
44 ) -> anyhow::Result<()> {
45 let domains = self.domains.get_mut();
46 let domain = domains
47 .entry(domain_id)
48 .or_insert_with(|| PowerDomain::new(device.lock().initial_power_state()));
49 domain.attach(device)
50 }
51
52 pub fn power_on(&self, domain_id: usize) -> anyhow::Result<()> {
54 self.domains
55 .lock()
56 .get_mut(&domain_id)
57 .with_context(|| format!("Unknown PD {domain_id:#x}"))?
58 .power_on()
59 }
60
61 pub fn power_off(&self, domain_id: usize) -> anyhow::Result<()> {
63 self.domains
64 .lock()
65 .get_mut(&domain_id)
66 .with_context(|| format!("Unknown PD {domain_id:#x}"))?
67 .power_off()
68 }
69}
70
71struct PowerDomain {
72 devices: Vec<Arc<Mutex<dyn BusDevice>>>,
73 is_on: bool,
74}
75
76impl PowerDomain {
77 fn new(is_on: bool) -> Self {
78 Self {
79 devices: Vec::new(),
80 is_on,
81 }
82 }
83
84 fn attach(&mut self, device: Arc<Mutex<dyn BusDevice>>) -> anyhow::Result<()> {
85 let is_on = device.lock().initial_power_state();
86 if self.is_on != is_on {
87 bail!(
88 "Can't attach device to PD when states don't match: device is {}, PD is {}",
89 on_off_str(is_on),
90 on_off_str(self.is_on)
91 )
92 }
93 self.devices.push(device);
94 Ok(())
95 }
96
97 fn power_on(&mut self) -> anyhow::Result<()> {
98 self.power_on_off(true)
99 }
100
101 fn power_off(&mut self) -> anyhow::Result<()> {
102 self.power_on_off(false)
103 }
104
105 fn power_on_off(&mut self, on: bool) -> anyhow::Result<()> {
106 if self.is_on == on {
107 warn!("Ignoring attempt to update PD: already {}", on_off_str(on));
108 } else {
109 Self::switch_devices(&self.devices, on)?;
110 self.is_on = on;
111 }
112
113 Ok(())
114 }
115
116 fn switch_devices(devices: &[Arc<Mutex<dyn BusDevice>>], on: bool) -> anyhow::Result<()> {
117 for (i, device) in devices.iter().enumerate() {
118 let result = if on {
119 device.lock().power_on()
120 } else {
121 device.lock().power_off()
122 };
123 if let Err(e) = result {
124 warn!(
125 "Failed to switch {} device '{}': {e}",
126 on_off_str(on),
127 device.lock().debug_label()
128 );
129 Self::switch_devices(&devices[..i], !on).expect("PM failure during clean-up");
130 return Err(e);
131 }
132 }
133
134 Ok(())
135 }
136}
137
138fn on_off_str(on: bool) -> &'static str {
139 if on {
140 "ON"
141 } else {
142 "OFF"
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use vm_control::DeviceId;
149 use vm_control::PlatformDeviceId;
150
151 use super::*;
152 use crate::BusDevice;
153 use crate::Suspendable;
154
155 pub struct MockPoweredDevice {
156 initial_power_state: bool,
157 is_on: bool,
158 fail_power_on: bool,
159 fail_power_off: bool,
160 }
161
162 impl MockPoweredDevice {
163 fn new(initial_power_state: bool) -> Self {
164 Self {
165 initial_power_state,
166 is_on: initial_power_state,
167 fail_power_on: false,
168 fail_power_off: false,
169 }
170 }
171
172 fn with_failure(
173 initial_power_state: bool,
174 fail_power_on: bool,
175 fail_power_off: bool,
176 ) -> Self {
177 Self {
178 initial_power_state,
179 is_on: initial_power_state,
180 fail_power_on,
181 fail_power_off,
182 }
183 }
184
185 fn is_on(&self) -> bool {
186 self.is_on
187 }
188 }
189
190 impl BusDevice for MockPoweredDevice {
191 fn device_id(&self) -> DeviceId {
192 PlatformDeviceId::Mock.into()
193 }
194
195 fn debug_label(&self) -> String {
196 "mock device".to_owned()
197 }
198
199 fn supports_power_management(&self) -> anyhow::Result<bool> {
200 Ok(true)
201 }
202
203 fn initial_power_state(&self) -> bool {
204 self.initial_power_state
205 }
206
207 fn power_on(&mut self) -> anyhow::Result<()> {
208 if self.fail_power_on {
209 bail!("mock fail power on");
210 }
211 self.is_on = true;
212 Ok(())
213 }
214
215 fn power_off(&mut self) -> anyhow::Result<()> {
216 if self.fail_power_off {
217 bail!("mock fail power off");
218 }
219 self.is_on = false;
220 Ok(())
221 }
222 }
223
224 impl Suspendable for MockPoweredDevice {}
225
226 #[test]
227 fn domain_attaches_devices() {
228 let mock_starts_on1 = MockPoweredDevice::new(true);
229 let mock_starts_on2 = MockPoweredDevice::new(true);
230 let mock_starts_off1 = MockPoweredDevice::new(false);
231 let mock_starts_off2 = MockPoweredDevice::new(false);
232
233 let dev_starts_on1 = Arc::new(Mutex::new(mock_starts_on1));
234 let dev_starts_on2 = Arc::new(Mutex::new(mock_starts_on2));
235 let dev_starts_off1 = Arc::new(Mutex::new(mock_starts_off1));
236 let dev_starts_off2 = Arc::new(Mutex::new(mock_starts_off2));
237
238 let mut pd_starts_on = PowerDomain::new(true);
239 let mut pd_starts_off = PowerDomain::new(false);
240
241 assert!(pd_starts_on.attach(dev_starts_off1.clone()).is_err());
243 assert!(pd_starts_off.attach(dev_starts_on1.clone()).is_err());
244
245 assert!(pd_starts_on.attach(dev_starts_on1.clone()).is_ok());
247 assert!(pd_starts_off.attach(dev_starts_off1.clone()).is_ok());
248
249 assert!(pd_starts_on.attach(dev_starts_off2.clone()).is_err());
251 assert!(pd_starts_off.attach(dev_starts_on2.clone()).is_err());
252
253 assert!(pd_starts_on.attach(dev_starts_on2.clone()).is_ok());
255 assert!(pd_starts_off.attach(dev_starts_off2.clone()).is_ok());
256 }
257
258 #[test]
259 fn manager_powers_on() {
260 let mock1 = Arc::new(Mutex::new(MockPoweredDevice::new(false)));
261 let mock2 = Arc::new(Mutex::new(MockPoweredDevice::new(false)));
262
263 let mut power_manager = DevicePowerManager::new();
264 power_manager.attach(mock1.clone(), 0).unwrap();
265 power_manager.attach(mock2.clone(), 0).unwrap();
266
267 power_manager.power_on(0).unwrap();
268
269 assert!(mock1.lock().is_on());
270 assert!(mock2.lock().is_on());
271 }
272
273 #[test]
274 fn manager_powers_off() {
275 let mock1 = Arc::new(Mutex::new(MockPoweredDevice::new(true)));
276 let mock2 = Arc::new(Mutex::new(MockPoweredDevice::new(true)));
277
278 let mut power_manager = DevicePowerManager::new();
279 power_manager.attach(mock1.clone(), 0).unwrap();
280 power_manager.attach(mock2.clone(), 0).unwrap();
281
282 power_manager.power_off(0).unwrap();
283
284 assert!(!mock1.lock().is_on());
285 assert!(!mock2.lock().is_on());
286 }
287
288 #[test]
289 fn manager_cleans_up_failure() {
290 let mock1 = Arc::new(Mutex::new(MockPoweredDevice::new(false)));
292 let mock2 = Arc::new(Mutex::new(MockPoweredDevice::with_failure(
293 false, true, false,
294 )));
295
296 let mut power_manager = DevicePowerManager::new();
297 power_manager.attach(mock1.clone(), 0).unwrap();
298 power_manager.attach(mock2.clone(), 0).unwrap();
299
300 assert!(power_manager.power_on(0).is_err());
302
303 assert!(!mock2.lock().is_on());
305 assert!(!mock1.lock().is_on());
307 }
308
309 #[test]
310 fn manager_isolates_domains() {
311 let mock0_1 = Arc::new(Mutex::new(MockPoweredDevice::new(false)));
312 let mock0_2 = Arc::new(Mutex::new(MockPoweredDevice::new(false)));
313 let mock1_1 = Arc::new(Mutex::new(MockPoweredDevice::new(false)));
314
315 let mut power_manager = DevicePowerManager::new();
316 power_manager.attach(mock0_1.clone(), 0).unwrap();
317 power_manager.attach(mock0_2.clone(), 0).unwrap();
318 power_manager.attach(mock1_1.clone(), 1).unwrap();
319
320 power_manager.power_on(0).unwrap();
322 assert!(mock0_1.lock().is_on());
323 assert!(mock0_2.lock().is_on());
324 assert!(!mock1_1.lock().is_on());
325
326 power_manager.power_on(1).unwrap();
328 assert!(mock0_1.lock().is_on());
329 assert!(mock0_2.lock().is_on());
330 assert!(mock1_1.lock().is_on());
331
332 power_manager.power_off(0).unwrap();
334 assert!(!mock0_1.lock().is_on());
335 assert!(!mock0_2.lock().is_on());
336 assert!(mock1_1.lock().is_on());
337 }
338
339 #[test]
340 fn manager_rejects_unknown_domain() {
341 let power_manager = DevicePowerManager::new();
342 assert!(power_manager.power_on(42).is_err());
343 assert!(power_manager.power_off(42).is_err());
344 }
345}