1pub mod resolver;
33
34use chipset_device::ChipsetDevice;
35use chipset_device::io::IoError;
36use chipset_device::io::IoResult;
37use chipset_device::mmio::MmioIntercept;
38use chipset_device::poll_device::PollDevice;
39use chipset_resources::battery::HostBatteryUpdate;
40use futures::StreamExt;
41use inspect::Inspect;
42use inspect::InspectMut;
43use open_enum::open_enum;
44use std::ops::RangeInclusive;
45use vmcore::device_state::ChangeDeviceState;
46use vmcore::line_interrupt::LineInterrupt;
47
48pub const BATTERY_MMIO_REGION_BASE_ADDRESS_X64: u64 = 0xfed3f000;
50pub const BATTERY_MMIO_REGION_BASE_ADDRESS_ARM: u64 = 0xEFFEA000;
51pub const BATTERY_DEVICE_MMIO_REGION_SIZE: u64 = 0x20;
52pub const BATTERY_DEVICE_MMIO_REGION_MASK: u64 = BATTERY_DEVICE_MMIO_REGION_SIZE - 1;
53
54pub const BATTERY_STATUS_GPE0_LINE: u32 = 9;
57pub const BATTERY_STATUS_IRQ_NO: u32 = 4;
58
59pub const ACPI_DEVICE_NOTIFY_BST_CHANGED: u32 = 0x1;
68pub const ACPI_DEVICE_NOTIFY_BIX_CHANGED: u32 = 0x2;
69
70pub const ACPI_DEVICE_NOTIFY_VALID_BITS: u32 = 0x3;
72
73pub const VIRTUAL_BATTERY_CAPACITY: u32 = 5000;
75
76open_enum! {
78 #[derive(Inspect)]
79 #[inspect(debug)]
80 pub enum RegisterOffset: u64 {
81 STA_BATTERY_STATUS = 0x0,
82 BST_BATTERY_STATE = 0x4,
83 BST_BATTERY_PRESENT_RATE = 0x8,
84 BST_BATTERY_REMAINING_CAPACITY = 0xc,
85 PSR_AC_POWER_STATUS = 0x10,
86 BATTERY_ACPI_NOTIFY_STATUS = 0x14,
87 BATTERY_ACPI_NOTIFY_CLEAR = 0x18,
88 }
89}
90
91pub struct BatteryRuntimeDeps {
93 pub battery_status_recv: mesh::Receiver<HostBatteryUpdate>,
94 pub notify_interrupt: LineInterrupt,
95}
96
97#[derive(InspectMut)]
99pub struct BatteryDevice {
100 #[inspect(skip)]
102 rt: BatteryRuntimeDeps,
103
104 #[inspect(skip)]
106 mmio_region: (&'static str, RangeInclusive<u64>),
107 base_addr: u64,
108
109 notify_bits: u32,
111 state: HostBatteryUpdate,
112}
113
114impl BatteryDevice {
115 pub fn new(platform: BatteryRuntimeDeps, base_addr: u64) -> Self {
117 BatteryDevice {
118 rt: platform,
119 mmio_region: (
120 "battery",
121 base_addr..=base_addr + (BATTERY_DEVICE_MMIO_REGION_SIZE - 1),
122 ),
123 base_addr,
124 state: HostBatteryUpdate::default(),
125 notify_bits: 0,
126 }
127 }
128
129 fn read_register(&self, offset: RegisterOffset) -> u32 {
130 match offset {
131 RegisterOffset::STA_BATTERY_STATUS => {
132 if self.state.battery_present {
133 0x1F
134 } else {
135 0xF
136 }
137 }
138 RegisterOffset::BST_BATTERY_STATE => {
139 if !self.state.battery_present {
140 0
141 } else if self.state.charging {
142 0x2
143 } else if self.state.discharging {
144 0x1
145 } else {
146 tracelimit::warn_ratelimited!(
148 "BST_BATTERY_STATE encountered a weird state, defaulting to 0"
149 );
150 0
151 }
152 }
153 RegisterOffset::BST_BATTERY_PRESENT_RATE => {
154 if self.state.battery_present && self.state.max_capacity != 0 {
155 (self.state.rate.saturating_mul(VIRTUAL_BATTERY_CAPACITY))
157 / self.state.max_capacity
158 } else {
159 0xFFFFFFFF
161 }
162 }
163 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY => {
164 if self.state.battery_present && self.state.max_capacity != 0 {
165 (self
167 .state
168 .remaining_capacity
169 .saturating_mul(VIRTUAL_BATTERY_CAPACITY))
170 / self.state.max_capacity
171 } else {
172 0xFFFFFFFF
174 }
175 }
176 RegisterOffset::PSR_AC_POWER_STATUS => {
177 if self.state.ac_online {
178 0x1
179 } else {
180 0x0
181 }
182 }
183 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS => self.notify_bits,
184 _ => 0,
185 }
186 }
187
188 fn write_register(&mut self, offset: RegisterOffset, value: u32) {
189 if offset != RegisterOffset::BATTERY_ACPI_NOTIFY_CLEAR {
190 tracelimit::warn_ratelimited!("Invalid write to battery device at offset {:?}", offset);
192 return;
193 }
194
195 self.notify_bits &= !value;
197 self.notify_bits &= ACPI_DEVICE_NOTIFY_VALID_BITS;
198
199 self.check_interrupt_assertion();
201 }
202
203 fn check_interrupt_assertion(&self) {
206 self.rt.notify_interrupt.set_level(self.notify_bits != 0)
207 }
208}
209
210impl ChangeDeviceState for BatteryDevice {
211 fn start(&mut self) {}
212
213 async fn stop(&mut self) {}
214
215 async fn reset(&mut self) {
216 let Self {
217 rt,
218 mmio_region: _,
219 base_addr: _,
220 notify_bits,
221 state,
222 } = self;
223 *state = HostBatteryUpdate::default();
224 rt.notify_interrupt.set_level(false);
225 *notify_bits = 0;
226 }
227}
228
229impl ChipsetDevice for BatteryDevice {
230 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
231 Some(self)
232 }
233
234 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
235 Some(self)
236 }
237}
238
239impl MmioIntercept for BatteryDevice {
240 fn mmio_read(&mut self, address: u64, data: &mut [u8]) -> IoResult {
241 assert_eq!(address & !BATTERY_DEVICE_MMIO_REGION_MASK, self.base_addr);
242 if data.len() == size_of::<u32>() {
243 let value =
244 self.read_register(RegisterOffset(address & BATTERY_DEVICE_MMIO_REGION_MASK));
245 data.copy_from_slice(&value.to_ne_bytes());
246 IoResult::Ok
247 } else {
248 IoResult::Err(IoError::InvalidAccessSize)
249 }
250 }
251
252 fn mmio_write(&mut self, address: u64, data: &[u8]) -> IoResult {
253 assert_eq!(address & !BATTERY_DEVICE_MMIO_REGION_MASK, self.base_addr);
254 if let Ok(x) = data.try_into().map(u32::from_ne_bytes) {
255 self.write_register(RegisterOffset(address & BATTERY_DEVICE_MMIO_REGION_MASK), x);
256 IoResult::Ok
257 } else {
258 IoResult::Err(IoError::InvalidAccessSize)
259 }
260 }
261
262 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
263 std::slice::from_ref(&self.mmio_region)
264 }
265}
266
267impl PollDevice for BatteryDevice {
268 fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
269 while let std::task::Poll::Ready(Some(update)) =
270 self.rt.battery_status_recv.poll_next_unpin(cx)
271 {
272 self.state = update;
273 if self.state.battery_present && self.state.max_capacity == 0 {
274 tracelimit::warn_ratelimited!(
277 "BATTERY: Invalid state: max_capacity is 0 but battery is present. Marking battery as not present."
278 );
279 self.state.battery_present = false;
280 }
281 self.notify_bits |= ACPI_DEVICE_NOTIFY_BIX_CHANGED;
283 self.check_interrupt_assertion();
284 }
285 }
286}
287
288mod save_restore {
289 use super::*;
290 use vmcore::save_restore::RestoreError;
291 use vmcore::save_restore::SaveError;
292 use vmcore::save_restore::SaveRestore;
293
294 mod state {
295 use mesh::payload::Protobuf;
296 use vmcore::save_restore::SavedStateRoot;
297
298 #[derive(Protobuf, SavedStateRoot)]
299 #[mesh(package = "firmware.battery")]
300 pub struct SavedState {
301 #[mesh(1)]
302 pub notify_bits: u32,
303 #[mesh(2)]
304 pub battery_state: BatterySavedState,
305 }
306
307 #[derive(Protobuf, SavedStateRoot)]
308 #[mesh(package = "firmware.battery")]
309 pub struct BatterySavedState {
310 #[mesh(1)]
311 pub battery_present: bool,
312 #[mesh(2)]
313 pub charging: bool,
314 #[mesh(3)]
315 pub discharging: bool,
316 #[mesh(4)]
317 pub rate: u32,
318 #[mesh(5)]
319 pub remaining_capacity: u32,
320 #[mesh(6)]
321 pub max_capacity: u32,
322 #[mesh(7)]
323 pub ac_online: bool,
324 }
325 }
326
327 impl SaveRestore for BatteryDevice {
328 type SavedState = state::SavedState;
329
330 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
331 let Self {
332 rt: _,
333 mmio_region: _,
334 base_addr: _,
335 notify_bits,
336 state:
337 HostBatteryUpdate {
338 battery_present,
339 charging,
340 discharging,
341 rate,
342 remaining_capacity,
343 max_capacity,
344 ac_online,
345 },
346 } = *self;
347
348 let saved_state = state::SavedState {
349 notify_bits,
350 battery_state: state::BatterySavedState {
351 battery_present,
352 charging,
353 discharging,
354 rate,
355 remaining_capacity,
356 max_capacity,
357 ac_online,
358 },
359 };
360
361 Ok(saved_state)
362 }
363
364 fn restore(&mut self, saved_state: Self::SavedState) -> Result<(), RestoreError> {
365 let state::SavedState {
366 notify_bits,
367 battery_state:
368 state::BatterySavedState {
369 battery_present,
370 charging,
371 discharging,
372 rate,
373 remaining_capacity,
374 max_capacity,
375 ac_online,
376 },
377 } = saved_state;
378
379 self.notify_bits = notify_bits;
380 self.state = HostBatteryUpdate {
381 battery_present,
382 charging,
383 discharging,
384 rate,
385 remaining_capacity,
386 max_capacity,
387 ac_online,
388 };
389
390 self.check_interrupt_assertion();
391
392 Ok(())
393 }
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use futures::task::Context;
401 use vmcore::line_interrupt::LineInterrupt;
402
403 fn create_test_platform() -> (BatteryDevice, mesh::Sender<HostBatteryUpdate>) {
404 let (tx, rx) = mesh::channel::<HostBatteryUpdate>();
405 let battery = BatteryDevice::new(
406 BatteryRuntimeDeps {
407 battery_status_recv: {
408 tx.send(HostBatteryUpdate::default());
409 rx
410 },
411 notify_interrupt: LineInterrupt::detached(),
412 },
413 BATTERY_MMIO_REGION_BASE_ADDRESS_X64,
414 );
415 (battery, tx)
416 }
417
418 fn mmio_read_helper(battery: &mut BatteryDevice, offset: u64) -> [u8; 4] {
419 let mut bytes = [0; 4];
420 battery
421 .mmio_read(battery.base_addr + offset, &mut bytes)
422 .unwrap();
423 bytes
424 }
425
426 fn check_mmio_read(battery: &mut BatteryDevice, offset: u64, expected_value: u32) {
427 assert_eq!(
428 u32::from_ne_bytes(mmio_read_helper(battery, offset)),
429 expected_value
430 );
431 assert_eq!(
432 battery.read_register(RegisterOffset(offset)),
433 expected_value
434 );
435 }
436
437 fn send_update(
438 battery: &mut BatteryDevice,
439 update: HostBatteryUpdate,
440 sender: &mesh::Sender<HostBatteryUpdate>,
441 ) {
442 sender.send(update);
443 battery.poll_device(&mut Context::from_waker(std::task::Waker::noop()));
444 }
445
446 #[test]
448 fn test_basic_battery_mmio() {
449 let (mut battery, tx) = create_test_platform();
451 let mut state = HostBatteryUpdate::default();
452
453 check_mmio_read(&mut battery, RegisterOffset::STA_BATTERY_STATUS.0, 0xF);
455 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_STATE.0, 0);
456 check_mmio_read(
457 &mut battery,
458 RegisterOffset::BST_BATTERY_PRESENT_RATE.0,
459 0xFFFFFFFF,
460 );
461 check_mmio_read(
462 &mut battery,
463 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY.0,
464 0xFFFFFFFF,
465 );
466 check_mmio_read(&mut battery, RegisterOffset::PSR_AC_POWER_STATUS.0, 0);
467 check_mmio_read(
468 &mut battery,
469 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS.0,
470 0,
471 );
472
473 state.battery_present = true;
475 state.remaining_capacity = 100;
476 state.max_capacity = 100;
477 state.ac_online = true;
478 send_update(&mut battery, state, &tx);
479
480 check_mmio_read(&mut battery, RegisterOffset::STA_BATTERY_STATUS.0, 0x1F);
481 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_STATE.0, 0);
482 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_PRESENT_RATE.0, 0);
483 check_mmio_read(
484 &mut battery,
485 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY.0,
486 (100 * VIRTUAL_BATTERY_CAPACITY) / 100,
487 );
488 check_mmio_read(&mut battery, RegisterOffset::PSR_AC_POWER_STATUS.0, 0x1);
489 check_mmio_read(
490 &mut battery,
491 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS.0,
492 2,
493 );
494
495 state.charging = true;
497 state.remaining_capacity = 50;
498 state.rate = 40;
499 send_update(&mut battery, state, &tx);
500
501 check_mmio_read(&mut battery, RegisterOffset::STA_BATTERY_STATUS.0, 0x1F);
502 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_STATE.0, 0x2);
503 check_mmio_read(
504 &mut battery,
505 RegisterOffset::BST_BATTERY_PRESENT_RATE.0,
506 (40 * VIRTUAL_BATTERY_CAPACITY) / 100,
507 );
508 check_mmio_read(
509 &mut battery,
510 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY.0,
511 (50 * VIRTUAL_BATTERY_CAPACITY) / 100,
512 );
513 check_mmio_read(&mut battery, RegisterOffset::PSR_AC_POWER_STATUS.0, 0x1);
514 check_mmio_read(
515 &mut battery,
516 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS.0,
517 2,
518 );
519
520 state.ac_online = false;
522 state.charging = false;
523 state.discharging = true;
524 state.rate = 45;
525 send_update(&mut battery, state, &tx);
526 check_mmio_read(&mut battery, RegisterOffset::STA_BATTERY_STATUS.0, 0x1F);
527 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_STATE.0, 0x1);
528 check_mmio_read(
529 &mut battery,
530 RegisterOffset::BST_BATTERY_PRESENT_RATE.0,
531 (45 * VIRTUAL_BATTERY_CAPACITY) / 100,
532 );
533 check_mmio_read(
534 &mut battery,
535 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY.0,
536 (50 * VIRTUAL_BATTERY_CAPACITY) / 100,
537 );
538 check_mmio_read(&mut battery, RegisterOffset::PSR_AC_POWER_STATUS.0, 0x0);
539 check_mmio_read(
540 &mut battery,
541 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS.0,
542 2,
543 );
544
545 let data: u32 = 0x2;
547 let _ = battery.mmio_write(
548 battery.base_addr + RegisterOffset::BATTERY_ACPI_NOTIFY_CLEAR.0,
549 &data.to_ne_bytes(),
550 );
551 assert_eq!(battery.notify_bits, 0);
552 }
553
554 #[test]
556 fn test_battery_not_present() {
557 let (mut battery, tx) = create_test_platform();
559
560 let state = HostBatteryUpdate {
562 ac_online: true,
563 ..HostBatteryUpdate::default()
564 };
565 send_update(&mut battery, state, &tx);
566 check_mmio_read(&mut battery, RegisterOffset::STA_BATTERY_STATUS.0, 0xF);
567 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_STATE.0, 0);
568 check_mmio_read(
569 &mut battery,
570 RegisterOffset::BST_BATTERY_PRESENT_RATE.0,
571 0xFFFFFFFF,
572 );
573 check_mmio_read(
574 &mut battery,
575 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY.0,
576 0xFFFFFFFF,
577 );
578 check_mmio_read(&mut battery, RegisterOffset::PSR_AC_POWER_STATUS.0, 0x1);
579 check_mmio_read(
580 &mut battery,
581 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS.0,
582 0x2,
583 );
584 }
585
586 #[test]
588 fn test_bad_register_read_writes() {
589 let (mut battery, _) = create_test_platform();
591
592 let mut data = vec![0; size_of::<u32>() + 1];
594 match battery.mmio_read(battery.base_addr, &mut data) {
595 IoResult::Err(e) => assert!(matches!(e, IoError::InvalidAccessSize)),
596 _ => panic!("Expected error, but got Ok"),
597 }
598
599 let data = vec![0; size_of::<u32>() + 1];
601 match battery.mmio_write(battery.base_addr, &data) {
602 IoResult::Err(e) => assert!(matches!(e, IoError::InvalidAccessSize)),
603 _ => panic!("Expected error, but got Ok"),
604 }
605
606 let data = vec![0; size_of::<u32>() - 1];
608 match battery.mmio_write(battery.base_addr, &data) {
609 IoResult::Err(e) => assert!(matches!(e, IoError::InvalidAccessSize)),
610 _ => panic!("Expected error, but got Ok"),
611 }
612 }
613
614 #[test]
616 fn test_bad_battery_capacity() {
617 let (mut battery, tx) = create_test_platform();
619
620 let state = HostBatteryUpdate {
622 battery_present: true,
623 remaining_capacity: 50,
624 max_capacity: 0,
625 ac_online: true,
626 ..HostBatteryUpdate::default()
627 };
628 send_update(&mut battery, state, &tx);
629 check_mmio_read(&mut battery, RegisterOffset::STA_BATTERY_STATUS.0, 0xF);
630 check_mmio_read(&mut battery, RegisterOffset::BST_BATTERY_STATE.0, 0x0);
631 check_mmio_read(
632 &mut battery,
633 RegisterOffset::BST_BATTERY_PRESENT_RATE.0,
634 0xFFFFFFFF,
635 );
636 check_mmio_read(
637 &mut battery,
638 RegisterOffset::BST_BATTERY_REMAINING_CAPACITY.0,
639 0xFFFFFFFF,
640 );
641 check_mmio_read(&mut battery, RegisterOffset::PSR_AC_POWER_STATUS.0, 0x1);
642 check_mmio_read(
643 &mut battery,
644 RegisterOffset::BATTERY_ACPI_NOTIFY_STATUS.0,
645 0x2,
646 );
647 }
648}