• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 anyhow::Context;
10 use base::error;
11 use base::warn;
12 use base::AsRawDescriptor;
13 use base::Event;
14 use base::EventToken;
15 use base::RawDescriptor;
16 use base::Tube;
17 use base::WaitContext;
18 use base::WorkerThread;
19 use power_monitor::BatteryStatus;
20 use power_monitor::CreatePowerMonitorFn;
21 use remain::sorted;
22 use serde::Deserialize;
23 use serde::Serialize;
24 use sync::Mutex;
25 use thiserror::Error;
26 use vm_control::BatControlCommand;
27 use vm_control::BatControlResult;
28 
29 use crate::pci::CrosvmDeviceId;
30 use crate::BusAccessInfo;
31 use crate::BusDevice;
32 use crate::DeviceId;
33 use crate::IrqLevelEvent;
34 use crate::Suspendable;
35 
36 /// Errors for battery devices.
37 #[sorted]
38 #[derive(Error, Debug)]
39 pub enum BatteryError {
40     #[error("Non 32-bit mmio address space")]
41     Non32BitMmioAddress,
42 }
43 
44 type Result<T> = std::result::Result<T, BatteryError>;
45 
46 /// the GoldFish Battery MMIO length.
47 pub const GOLDFISHBAT_MMIO_LEN: u64 = 0x1000;
48 
49 #[derive(Clone, Serialize, Deserialize)]
50 struct GoldfishBatteryState {
51     // interrupt state
52     int_status: u32,
53     int_enable: u32,
54     // AC state
55     ac_online: u32,
56     // Battery state
57     status: u32,
58     health: u32,
59     present: u32,
60     capacity: u32,
61     voltage: u32,
62     current: u32,
63     charge_counter: u32,
64     charge_full: u32,
65 }
66 
67 macro_rules! create_battery_func {
68     // $property: the battery property which is going to be modified.
69     // $int: the interrupt status which is going to be set to notify the guest.
70     ($fn:ident, $property:ident, $int:ident) => {
71         pub(crate) fn $fn(&mut self, value: u32) -> bool {
72             let old = std::mem::replace(&mut self.$property, value);
73             old != self.$property && self.set_int_status($int)
74         }
75     };
76 }
77 
78 impl GoldfishBatteryState {
set_int_status(&mut self, mask: u32) -> bool79     fn set_int_status(&mut self, mask: u32) -> bool {
80         if ((self.int_enable & mask) != 0) && ((self.int_status & mask) == 0) {
81             self.int_status |= mask;
82             return true;
83         }
84         false
85     }
86 
int_status(&self) -> u3287     fn int_status(&self) -> u32 {
88         self.int_status
89     }
90 
91     create_battery_func!(set_ac_online, ac_online, AC_STATUS_CHANGED);
92 
93     create_battery_func!(set_status, status, BATTERY_STATUS_CHANGED);
94 
95     create_battery_func!(set_health, health, BATTERY_STATUS_CHANGED);
96 
97     create_battery_func!(set_present, present, BATTERY_STATUS_CHANGED);
98 
99     create_battery_func!(set_capacity, capacity, BATTERY_STATUS_CHANGED);
100 
101     create_battery_func!(set_voltage, voltage, BATTERY_STATUS_CHANGED);
102 
103     create_battery_func!(set_current, current, BATTERY_STATUS_CHANGED);
104 
105     create_battery_func!(set_charge_counter, charge_counter, BATTERY_STATUS_CHANGED);
106 
107     create_battery_func!(set_charge_full, charge_full, BATTERY_STATUS_CHANGED);
108 }
109 
110 /// GoldFish Battery state
111 pub struct GoldfishBattery {
112     state: Arc<Mutex<GoldfishBatteryState>>,
113     mmio_base: u32,
114     irq_num: u32,
115     irq_evt: IrqLevelEvent,
116     activated: bool,
117     monitor_thread: Option<WorkerThread<()>>,
118     tube: Option<Tube>,
119     create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
120 }
121 
122 #[derive(Serialize, Deserialize)]
123 struct GoldfishBatterySnapshot {
124     state: GoldfishBatteryState,
125     mmio_base: u32,
126     irq_num: u32,
127     activated: bool,
128 }
129 
130 /// Goldfish Battery MMIO offset
131 const BATTERY_INT_STATUS: u32 = 0;
132 const BATTERY_INT_ENABLE: u32 = 0x4;
133 const BATTERY_AC_ONLINE: u32 = 0x8;
134 const BATTERY_STATUS: u32 = 0xC;
135 const BATTERY_HEALTH: u32 = 0x10;
136 const BATTERY_PRESENT: u32 = 0x14;
137 const BATTERY_CAPACITY: u32 = 0x18;
138 const BATTERY_VOLTAGE: u32 = 0x1C;
139 const BATTERY_TEMP: u32 = 0x20;
140 const BATTERY_CHARGE_COUNTER: u32 = 0x24;
141 const BATTERY_VOLTAGE_MAX: u32 = 0x28;
142 const BATTERY_CURRENT_MAX: u32 = 0x2C;
143 const BATTERY_CURRENT_NOW: u32 = 0x30;
144 const BATTERY_CURRENT_AVG: u32 = 0x34;
145 const BATTERY_CHARGE_FULL_UAH: u32 = 0x38;
146 const BATTERY_CYCLE_COUNT: u32 = 0x40;
147 
148 /// Goldfish Battery interrupt bits
149 const BATTERY_STATUS_CHANGED: u32 = 1 << 0;
150 const AC_STATUS_CHANGED: u32 = 1 << 1;
151 const BATTERY_INT_MASK: u32 = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED;
152 
153 /// Goldfish Battery status
154 const BATTERY_STATUS_VAL_UNKNOWN: u32 = 0;
155 const BATTERY_STATUS_VAL_CHARGING: u32 = 1;
156 const BATTERY_STATUS_VAL_DISCHARGING: u32 = 2;
157 const BATTERY_STATUS_VAL_NOT_CHARGING: u32 = 3;
158 
159 /// Goldfish Battery health
160 const BATTERY_HEALTH_VAL_UNKNOWN: u32 = 0;
161 
162 #[derive(EventToken)]
163 pub(crate) enum Token {
164     Commands,
165     Resample,
166     Kill,
167     Monitor,
168 }
169 
command_monitor( tube: Tube, irq_evt: IrqLevelEvent, kill_evt: Event, state: Arc<Mutex<GoldfishBatteryState>>, create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>, )170 fn command_monitor(
171     tube: Tube,
172     irq_evt: IrqLevelEvent,
173     kill_evt: Event,
174     state: Arc<Mutex<GoldfishBatteryState>>,
175     create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
176 ) {
177     let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
178         (&tube, Token::Commands),
179         (irq_evt.get_resample(), Token::Resample),
180         (&kill_evt, Token::Kill),
181     ]) {
182         Ok(pc) => pc,
183         Err(e) => {
184             error!("failed to build WaitContext: {}", e);
185             return;
186         }
187     };
188 
189     let mut power_monitor = match create_power_monitor {
190         Some(f) => match f() {
191             Ok(p) => match wait_ctx.add(p.get_read_notifier(), Token::Monitor) {
192                 Ok(()) => Some(p),
193                 Err(e) => {
194                     error!("failed to add power monitor to poll context: {}", e);
195                     None
196                 }
197             },
198             Err(e) => {
199                 error!("failed to create power monitor: {}", e);
200                 None
201             }
202         },
203         None => None,
204     };
205 
206     'poll: loop {
207         let events = match wait_ctx.wait() {
208             Ok(v) => v,
209             Err(e) => {
210                 error!("error while polling for events: {}", e);
211                 break;
212             }
213         };
214 
215         for event in events.iter().filter(|e| e.is_readable) {
216             match event.token {
217                 Token::Commands => {
218                     let req = match tube.recv() {
219                         Ok(req) => req,
220                         Err(e) => {
221                             error!("failed to receive request: {}", e);
222                             continue;
223                         }
224                     };
225 
226                     let mut bat_state = state.lock();
227                     let inject_irq = match req {
228                         BatControlCommand::SetStatus(status) => bat_state.set_status(status.into()),
229                         BatControlCommand::SetHealth(health) => bat_state.set_health(health.into()),
230                         BatControlCommand::SetPresent(present) => {
231                             let v = present != 0;
232                             bat_state.set_present(v.into())
233                         }
234                         BatControlCommand::SetCapacity(capacity) => {
235                             let v = std::cmp::min(capacity, 100);
236                             bat_state.set_capacity(v)
237                         }
238                         BatControlCommand::SetACOnline(ac_online) => {
239                             let v = ac_online != 0;
240                             bat_state.set_ac_online(v.into())
241                         }
242                     };
243 
244                     if inject_irq {
245                         let _ = irq_evt.trigger();
246                     }
247 
248                     if let Err(e) = tube.send(&BatControlResult::Ok) {
249                         error!("failed to send response: {}", e);
250                     }
251                 }
252 
253                 Token::Monitor => {
254                     // Safe because power_monitor must be populated if Token::Monitor is triggered.
255                     let power_monitor = power_monitor.as_mut().unwrap();
256 
257                     let data = match power_monitor.read_message() {
258                         Ok(Some(d)) => d,
259                         Ok(None) => continue,
260                         Err(e) => {
261                             error!("failed to read new power data: {}", e);
262                             continue;
263                         }
264                     };
265 
266                     let mut bat_state = state.lock();
267 
268                     // Each set_* function called below returns true when interrupt bits
269                     // (*_STATUS_CHANGED) changed. If `inject_irq` is true after we attempt to
270                     // update each field, inject an interrupt.
271                     let mut inject_irq = bat_state.set_ac_online(data.ac_online.into());
272 
273                     match data.battery {
274                         Some(battery_data) => {
275                             inject_irq |= bat_state.set_capacity(battery_data.percent);
276                             let battery_status = match battery_data.status {
277                                 BatteryStatus::Unknown => BATTERY_STATUS_VAL_UNKNOWN,
278                                 BatteryStatus::Charging => BATTERY_STATUS_VAL_CHARGING,
279                                 BatteryStatus::Discharging => BATTERY_STATUS_VAL_DISCHARGING,
280                                 BatteryStatus::NotCharging => BATTERY_STATUS_VAL_NOT_CHARGING,
281                             };
282                             inject_irq |= bat_state.set_status(battery_status);
283                             inject_irq |= bat_state.set_voltage(battery_data.voltage);
284                             inject_irq |= bat_state.set_current(battery_data.current);
285                             inject_irq |= bat_state.set_charge_counter(battery_data.charge_counter);
286                             inject_irq |= bat_state.set_charge_full(battery_data.charge_full);
287                         }
288                         None => {
289                             inject_irq |= bat_state.set_present(0);
290                         }
291                     }
292 
293                     if inject_irq {
294                         let _ = irq_evt.trigger();
295                     }
296                 }
297 
298                 Token::Resample => {
299                     irq_evt.clear_resample();
300                     if state.lock().int_status() != 0 {
301                         let _ = irq_evt.trigger();
302                     }
303                 }
304 
305                 Token::Kill => break 'poll,
306             }
307         }
308     }
309 }
310 
311 impl GoldfishBattery {
312     /// Create GoldfishBattery device model
313     ///
314     /// * `mmio_base` - The 32-bit mmio base address.
315     /// * `irq_num` - The corresponding interrupt number of the irq_evt which will be put into the
316     ///   ACPI DSDT.
317     /// * `irq_evt` - The interrupt event used to notify driver about the battery properties
318     ///   changing.
319     /// * `socket` - Battery control socket
new( mmio_base: u64, irq_num: u32, irq_evt: IrqLevelEvent, tube: Tube, create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>, ) -> Result<Self>320     pub fn new(
321         mmio_base: u64,
322         irq_num: u32,
323         irq_evt: IrqLevelEvent,
324         tube: Tube,
325         create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
326     ) -> Result<Self> {
327         if mmio_base + GOLDFISHBAT_MMIO_LEN - 1 > u32::MAX as u64 {
328             return Err(BatteryError::Non32BitMmioAddress);
329         }
330         let state = Arc::new(Mutex::new(GoldfishBatteryState {
331             capacity: 50,
332             health: BATTERY_HEALTH_VAL_UNKNOWN,
333             present: 1,
334             status: BATTERY_STATUS_VAL_UNKNOWN,
335             ac_online: 1,
336             int_enable: 0,
337             int_status: 0,
338             voltage: 0,
339             current: 0,
340             charge_counter: 0,
341             charge_full: 0,
342         }));
343 
344         Ok(GoldfishBattery {
345             state,
346             mmio_base: mmio_base as u32,
347             irq_num,
348             irq_evt,
349             activated: false,
350             monitor_thread: None,
351             tube: Some(tube),
352             create_power_monitor,
353         })
354     }
355 
356     /// return the descriptors used by this device
keep_rds(&self) -> Vec<RawDescriptor>357     pub fn keep_rds(&self) -> Vec<RawDescriptor> {
358         let mut rds = vec![
359             self.irq_evt.get_trigger().as_raw_descriptor(),
360             self.irq_evt.get_resample().as_raw_descriptor(),
361         ];
362 
363         if let Some(tube) = &self.tube {
364             rds.push(tube.as_raw_descriptor());
365         }
366 
367         rds
368     }
369 
370     /// start a monitor thread to monitor the events from host
start_monitor(&mut self)371     fn start_monitor(&mut self) {
372         if self.activated {
373             return;
374         }
375 
376         if let Some(tube) = self.tube.take() {
377             let irq_evt = self.irq_evt.try_clone().unwrap();
378             let bat_state = self.state.clone();
379             let create_monitor_fn = self.create_power_monitor.take();
380             self.monitor_thread = Some(WorkerThread::start(self.debug_label(), move |kill_evt| {
381                 command_monitor(tube, irq_evt, kill_evt, bat_state, create_monitor_fn)
382             }));
383             self.activated = true;
384         }
385     }
386 }
387 
388 impl Drop for GoldfishBattery {
drop(&mut self)389     fn drop(&mut self) {
390         if let Err(e) = self.sleep() {
391             error!("{}", e);
392         };
393     }
394 }
395 
396 impl BusDevice for GoldfishBattery {
device_id(&self) -> DeviceId397     fn device_id(&self) -> DeviceId {
398         CrosvmDeviceId::GoldfishBattery.into()
399     }
400 
debug_label(&self) -> String401     fn debug_label(&self) -> String {
402         "GoldfishBattery".to_owned()
403     }
404 
read(&mut self, info: BusAccessInfo, data: &mut [u8])405     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
406         if data.len() != std::mem::size_of::<u32>() {
407             warn!(
408                 "{}: unsupported read length {}, only support 4bytes read",
409                 self.debug_label(),
410                 data.len()
411             );
412             return;
413         }
414 
415         let val = match info.offset as u32 {
416             BATTERY_INT_STATUS => {
417                 // read to clear the interrupt status
418                 std::mem::replace(&mut self.state.lock().int_status, 0)
419             }
420             BATTERY_INT_ENABLE => self.state.lock().int_enable,
421             BATTERY_AC_ONLINE => self.state.lock().ac_online,
422             BATTERY_STATUS => self.state.lock().status,
423             BATTERY_HEALTH => self.state.lock().health,
424             BATTERY_PRESENT => self.state.lock().present,
425             BATTERY_CAPACITY => self.state.lock().capacity,
426             BATTERY_VOLTAGE => self.state.lock().voltage,
427             BATTERY_TEMP => 0,
428             BATTERY_CHARGE_COUNTER => self.state.lock().charge_counter,
429             BATTERY_VOLTAGE_MAX => 0,
430             BATTERY_CURRENT_MAX => 0,
431             BATTERY_CURRENT_NOW => self.state.lock().current,
432             BATTERY_CURRENT_AVG => 0,
433             BATTERY_CHARGE_FULL_UAH => self.state.lock().charge_full,
434             BATTERY_CYCLE_COUNT => 0,
435             _ => {
436                 warn!("{}: unsupported read address {}", self.debug_label(), info);
437                 return;
438             }
439         };
440 
441         let val_arr = val.to_ne_bytes();
442         data.copy_from_slice(&val_arr);
443     }
444 
write(&mut self, info: BusAccessInfo, data: &[u8])445     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
446         if data.len() != std::mem::size_of::<u32>() {
447             warn!(
448                 "{}: unsupported write length {}, only support 4bytes write",
449                 self.debug_label(),
450                 data.len()
451             );
452             return;
453         }
454 
455         let mut val_arr = u32::to_ne_bytes(0u32);
456         val_arr.copy_from_slice(data);
457         let val = u32::from_ne_bytes(val_arr);
458 
459         match info.offset as u32 {
460             BATTERY_INT_ENABLE => {
461                 self.state.lock().int_enable = val;
462                 if (val & BATTERY_INT_MASK) != 0 && !self.activated {
463                     self.start_monitor();
464                 }
465             }
466             _ => {
467                 warn!("{}: Bad write to address {}", self.debug_label(), info);
468             }
469         };
470     }
471 }
472 
473 impl Aml for GoldfishBattery {
to_aml_bytes(&self, bytes: &mut Vec<u8>)474     fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
475         aml::Device::new(
476             "GFBY".into(),
477             vec![
478                 &aml::Name::new("_HID".into(), &"GFSH0001"),
479                 &aml::Name::new(
480                     "_CRS".into(),
481                     &aml::ResourceTemplate::new(vec![
482                         &aml::Memory32Fixed::new(true, self.mmio_base, GOLDFISHBAT_MMIO_LEN as u32),
483                         &aml::Interrupt::new(true, false, false, true, self.irq_num),
484                     ]),
485                 ),
486             ],
487         )
488         .to_aml_bytes(bytes);
489     }
490 }
491 
492 impl Suspendable for GoldfishBattery {
sleep(&mut self) -> anyhow::Result<()>493     fn sleep(&mut self) -> anyhow::Result<()> {
494         if let Some(thread) = self.monitor_thread.take() {
495             thread.stop();
496         }
497         Ok(())
498     }
499 
wake(&mut self) -> anyhow::Result<()>500     fn wake(&mut self) -> anyhow::Result<()> {
501         if self.activated {
502             // Set activated to false for start_monitor to start monitoring again.
503             self.activated = false;
504             self.start_monitor();
505         }
506         Ok(())
507     }
508 
snapshot(&mut self) -> anyhow::Result<serde_json::Value>509     fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
510         serde_json::to_value(GoldfishBatterySnapshot {
511             state: self.state.lock().clone(),
512             mmio_base: self.mmio_base,
513             irq_num: self.irq_num,
514             activated: self.activated,
515         })
516         .context("failed to snapshot GoldfishBattery")
517     }
518 
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>519     fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
520         let deser: GoldfishBatterySnapshot =
521             serde_json::from_value(data).context("failed to deserialize GoldfishBattery")?;
522         {
523             let mut locked_state = self.state.lock();
524             *locked_state = deser.state;
525         }
526         self.mmio_base = deser.mmio_base;
527         self.irq_num = deser.irq_num;
528         self.activated = deser.activated;
529         Ok(())
530     }
531 }
532 
533 #[cfg(test)]
534 mod tests {
535     use super::*;
536     use crate::suspendable_tests;
537 
modify_device(battery: &mut GoldfishBattery)538     fn modify_device(battery: &mut GoldfishBattery) {
539         let mut state = battery.state.lock();
540         state.set_capacity(70);
541     }
542 
543     suspendable_tests! {
544         battery, GoldfishBattery::new(
545             0,
546             0,
547             IrqLevelEvent::new().unwrap(),
548             Tube::pair().unwrap().1,
549             None,
550         ).unwrap(),
551         modify_device
552     }
553 }
554