1#![warn(missing_docs)]
30
31pub mod resolver;
32
33use chipset_device::ChipsetDevice;
34use chipset_device::interrupt::LineInterruptTarget;
35use chipset_device::io::IoError;
36use chipset_device::io::IoResult;
37use chipset_device::pio::ControlPortIoIntercept;
38use chipset_device::pio::PortIoIntercept;
39use chipset_device::pio::RegisterPortIoIntercept;
40use chipset_device::poll_device::PollDevice;
41use chipset_resources::acpi_power_button::PowerButtonRpc;
42use futures::StreamExt;
43use inspect::Inspect;
44use inspect::InspectMut;
45use open_enum::open_enum;
46use vmcore::device_state::ChangeDeviceState;
47use vmcore::line_interrupt::LineInterrupt;
48use vmcore::vmtime::VmTimeAccess;
49
50open_enum! {
51 pub enum DynReg: u8 {
53 #![expect(missing_docs)] STATUS = acpi_spec::hyperv::PM_STATUS as u8, RESUME_ENABLE = 0x02, CONTROL = acpi_spec::hyperv::PM_CONTROL as u8, TIMER = acpi_spec::hyperv::PM_TIMER as u8, GEN_PURPOSE_STATUS = acpi_spec::hyperv::PM_GPE0_STATUS as u8, GEN_PURPOSE_ENABLE = 0x0E, PROC_CONTROL = 0x10, PROC_L2 = 0x14, PROC_L3 = 0x15, GLOBAL_STATUS = 0x18, DEVICE_STATUS = 0x1C, GLOBAL_ENABLE = 0x20, GLOBAL_CONTROL = 0x28, DEVICE_CONTROL = 0x2C, GENERAL_INPUT1 = 0x30, GENERAL_INPUT2 = 0x31, GENERAL_INPUT3 = 0x32, RESET = acpi_spec::hyperv::PM_RESET as u8, GENERAL_OUTPUT0 = 0x34, GENERAL_OUTPUT2 = 0x35, GENERAL_OUTPUT3 = 0x36, GENERAL_OUTPUT4 = 0x37, }
77}
78
79const CONTROL_SCI_ENABLE_MASK: u16 = 0x0001; const CONTROL_SUSPEND_ENABLE_MASK: u16 = 0x2000; const CONTROL_SUSPEND_TYPE_MASK: u16 = 0x1C00; const ENABLE_TIMER_OVERFLOW_MASK: u16 = 0x0001; const GLOBAL_CONTROL_BIOS_RLS_MASK: u32 = 0x00000002; const STATUS_DEVICE_MASK: u16 = 0x0010; const STATUS_GP_MASK: u16 = 0x0080; const STATUS_PM_MASK: u16 = 0x0040; const TIMER_OVERFLOW_MASK: u16 = 0x0001; const POWER_BUTTON_MASK: u16 = 1 << 8;
89
90pub const RESET_VALUE: u8 = acpi_spec::hyperv::RESET_VALUE;
92
93#[derive(Clone, Debug, Inspect)]
94struct PmState {
95 #[inspect(hex)]
96 general_purpose_output: u32,
97
98 #[inspect(hex)]
100 status: u16,
101 #[inspect(hex)]
102 resume_enable: u16,
103 #[inspect(hex)]
104 control: u16,
105 #[inspect(hex)]
106 general_purpose_status: u16,
107 #[inspect(hex)]
108 general_purpose_enable: u16,
109 #[inspect(hex)]
110 processor_control: u32,
111 #[inspect(hex)]
112 device_status: u32,
113 #[inspect(hex)]
114 global_status: u16,
115 #[inspect(hex)]
116 global_enable: u16,
117 #[inspect(hex)]
118 global_control: u32,
119 #[inspect(hex)]
120 device_control: u32,
121}
122
123impl PmState {
124 fn new() -> Self {
125 Self {
126 general_purpose_output: 0x7FFFBFFF,
127 status: 0,
128 resume_enable: 0,
129 control: 0,
130 general_purpose_status: 0,
131 general_purpose_enable: 0,
132 processor_control: 0,
133 device_status: 0,
134 global_status: 0,
135 global_enable: 0,
136 global_control: 0,
137 device_control: 0,
138 }
139 }
140
141 fn read_dynamic(&mut self, vmtime: &VmTimeAccess, offset: u8) -> u32 {
142 match DynReg(offset) {
143 DynReg::STATUS => self.status.into(),
146 DynReg::RESUME_ENABLE => (self.resume_enable & 0x0521).into(),
148 DynReg::CONTROL => self.control.into(),
150 DynReg::TIMER => {
157 let now = vmtime.now();
158 (now.as_100ns() as u128 * 3_579_545 / 10_000_000) as u32
160 }
161 DynReg::GEN_PURPOSE_STATUS => self.general_purpose_status.into(),
163 DynReg::GEN_PURPOSE_ENABLE => self.general_purpose_enable.into(),
165 DynReg::PROC_CONTROL => self.processor_control,
167 DynReg::PROC_L2 => 0,
169 DynReg::PROC_L3 => 0,
171 DynReg::GLOBAL_STATUS => {
173 let mut value = self.global_status;
174
175 if (self.status & !TIMER_OVERFLOW_MASK) != 0 {
178 value |= STATUS_PM_MASK;
179 }
180
181 if self.general_purpose_status != 0 {
182 value |= STATUS_GP_MASK;
183 }
184
185 if self.device_status != 0 {
186 value |= STATUS_DEVICE_MASK;
187 }
188
189 value.into()
190 }
191 DynReg::DEVICE_STATUS => self.device_status,
193 DynReg::GLOBAL_ENABLE => self.global_enable.into(),
195 DynReg::GLOBAL_CONTROL => self.global_control,
197 DynReg::DEVICE_CONTROL => self.device_control,
199 DynReg::GENERAL_INPUT1 => 0,
201 DynReg::GENERAL_INPUT2 => 0,
203 DynReg::GENERAL_INPUT3 => 0,
205 DynReg::GENERAL_OUTPUT0 => self.general_purpose_output,
207 DynReg::GENERAL_OUTPUT2 => self.general_purpose_output >> 8,
209 DynReg::GENERAL_OUTPUT3 => self.general_purpose_output >> 16,
211 DynReg::GENERAL_OUTPUT4 => self.general_purpose_output >> 24,
213 _ => {
214 tracelimit::warn_ratelimited!(?offset, "unhandled register read");
215 !0
216 }
217 }
218 }
219
220 fn write_dynamic(&mut self, action: &mut PowerActionFn, offset: u8, value: u32, mask: u32) {
221 match DynReg(offset) {
222 DynReg::STATUS => self.status &= !value as u16,
224 DynReg::RESUME_ENABLE => {
226 self.resume_enable &= !mask as u16;
228 self.resume_enable |= value as u16 & 0x0521;
229 }
230 DynReg::CONTROL => {
232 let value = value as u16;
233 if (value & CONTROL_SUSPEND_ENABLE_MASK) != 0 {
234 let suspend_type = (value & CONTROL_SUSPEND_TYPE_MASK) >> 10;
241 match suspend_type {
242 0 => (action)(PowerAction::PowerOff),
243 1 => (action)(PowerAction::Hibernate),
244 _ => {}
245 }
246 }
247
248 self.control &= !mask as u16;
249 self.control |= value;
250 }
251 DynReg::TIMER => {
252 }
254 DynReg::GEN_PURPOSE_STATUS => {
256 self.general_purpose_status &= !value as u16;
257 }
258 DynReg::GEN_PURPOSE_ENABLE => {
260 self.general_purpose_enable &= !mask as u16;
261 self.general_purpose_enable |= value as u16 & 0x0f01;
262 }
263 DynReg::PROC_CONTROL => {
265 self.processor_control &= !mask;
266 self.processor_control |= value & 0x00023E1E;
267 }
268 DynReg::PROC_L2 => {} DynReg::PROC_L3 => {} DynReg::GLOBAL_STATUS => {
274 self.global_status &= !(value & 0x0D25) as u16;
278 }
279
280 DynReg::DEVICE_STATUS => self.device_status = !value,
282 DynReg::GLOBAL_ENABLE => {
284 self.global_enable &= !mask as u16;
285 self.global_enable |= (value & 0x8D13) as u16;
286 }
287 DynReg::GLOBAL_CONTROL => {
289 let value = value & !GLOBAL_CONTROL_BIOS_RLS_MASK;
291 self.global_control &= !mask;
292 self.global_control |= value & 0x0701FFE7;
293 }
294 DynReg::DEVICE_CONTROL => {
296 self.device_control &= !mask;
297 self.device_control |= value;
298 }
299 DynReg::RESET => {
301 if value as u8 == RESET_VALUE {
302 (action)(PowerAction::Reboot);
303 }
304 }
305 DynReg::GENERAL_OUTPUT0 => {
307 let mask = mask & 0xffff;
308 self.general_purpose_output &= !mask;
309 self.general_purpose_output |= value & mask;
310 }
311 DynReg::GENERAL_OUTPUT2 => {
313 self.general_purpose_output &= !0xFF00;
314 self.general_purpose_output |= (value << 8) & 0xFF00;
315 }
316 DynReg::GENERAL_OUTPUT3 => {
318 let mask = mask & 0xffff;
319 self.general_purpose_output &= !(mask << 16);
320 self.general_purpose_output |= (value << 16) & mask;
321 }
322 DynReg::GENERAL_OUTPUT4 => {
324 self.general_purpose_output &= !0xFF000000;
325 self.general_purpose_output |= (value << 24) & 0x7F000000;
326 }
327 _ => tracelimit::warn_ratelimited!(?offset, ?value, "unhandled register write"),
328 }
329 }
330}
331
332#[expect(missing_docs)] #[derive(Debug, Copy, Clone)]
335pub enum PowerAction {
336 PowerOff,
337 Hibernate,
338 Reboot,
339}
340
341pub type PowerActionFn = Box<dyn FnMut(PowerAction) + Send + Sync>;
343
344#[derive(Inspect)]
345struct PowerManagementDeviceRt {
346 pio_dynamic: Box<dyn ControlPortIoIntercept>,
348 acpi_interrupt: LineInterrupt,
350 vmtime: VmTimeAccess,
352 #[inspect(skip)]
354 action: PowerActionFn,
355 #[inspect(skip)]
357 pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
358 #[inspect(skip)]
359 power_button_recv: mesh::Receiver<PowerButtonRpc>,
360}
361
362#[derive(Debug, Clone, Copy)]
371pub struct EnableAcpiMode {
372 pub default_pio_dynamic: u16,
374}
375
376pub use chipset_resources::pm::PmTimerAssist;
378
379#[derive(InspectMut)]
383pub struct PowerManagementDevice {
384 #[inspect(skip)]
386 enable_acpi_mode: Option<EnableAcpiMode>,
387
388 #[inspect(flatten)]
390 rt: PowerManagementDeviceRt,
391
392 #[inspect(flatten)]
394 state: PmState,
395}
396
397impl PowerManagementDevice {
398 pub fn new(
410 action: PowerActionFn,
411 acpi_interrupt: LineInterrupt,
412 register_pio: &mut dyn RegisterPortIoIntercept,
413 vmtime: VmTimeAccess,
414 enable_acpi_mode: Option<EnableAcpiMode>,
415 pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
416 power_button_recv: Option<mesh::Receiver<PowerButtonRpc>>,
417 ) -> Self {
418 let pio_dynamic = register_pio.new_io_region("dynamic", 0x37);
419
420 let mut this = PowerManagementDevice {
421 enable_acpi_mode,
422 rt: PowerManagementDeviceRt {
423 pio_dynamic,
424 action,
425 acpi_interrupt,
426 vmtime,
427 pm_timer_assist,
428 power_button_recv: power_button_recv.unwrap_or_default(),
429 },
430 state: PmState::new(),
431 };
432
433 if let Some(pm_timer_assist) = &this.rt.pm_timer_assist {
435 pm_timer_assist.set(None)
436 }
437
438 if let Some(acpi_mode) = enable_acpi_mode {
439 this.enable_acpi_mode(acpi_mode.default_pio_dynamic)
440 }
441
442 this
443 }
444
445 fn enable_acpi_mode(&mut self, default_pio_dynamic: u16) {
446 tracing::debug!("ACPI mode enabled");
447 self.update_dynamic_pio_mappings(Some(default_pio_dynamic));
448 self.state.control = CONTROL_SCI_ENABLE_MASK;
449 }
450
451 pub fn check_interrupt_assertion(&self) {
464 let level = self.state.control & CONTROL_SCI_ENABLE_MASK != 0
466 && ((self.state.resume_enable & self.state.status != 0)
467 || (self.state.general_purpose_status & self.state.general_purpose_enable != 0));
468
469 self.rt.acpi_interrupt.set_level(level)
470 }
471
472 #[inline(always)]
474 pub fn update_dynamic_pio_mappings(&mut self, pio_dynamic_addr: Option<u16>) {
475 match pio_dynamic_addr {
476 Some(addr) => {
477 self.rt.pio_dynamic.map(addr);
478 if let Some(assist) = &self.rt.pm_timer_assist {
479 assist.set(Some(addr + DynReg::TIMER.0 as u16))
480 }
481 }
482 None => {
483 self.rt.pio_dynamic.unmap();
484 if let Some(assist) = &self.rt.pm_timer_assist {
485 assist.set(None);
486 }
487 }
488 }
489 }
490
491 #[inline(always)]
495 pub fn pcat_facp_acpi_enable(&mut self, enable: bool) {
496 if enable {
497 self.state.control |= CONTROL_SCI_ENABLE_MASK;
498 self.state.resume_enable |= ENABLE_TIMER_OVERFLOW_MASK;
499 } else {
500 self.state.control &= !CONTROL_SCI_ENABLE_MASK;
501 self.state.resume_enable &= !ENABLE_TIMER_OVERFLOW_MASK;
502 }
503 self.check_interrupt_assertion();
504 }
505
506 pub fn power_action(&mut self) -> &mut PowerActionFn {
510 &mut self.rt.action
511 }
512}
513
514impl ChangeDeviceState for PowerManagementDevice {
515 fn start(&mut self) {}
516
517 async fn stop(&mut self) {}
518
519 async fn reset(&mut self) {
520 self.update_dynamic_pio_mappings(None);
521 self.rt.acpi_interrupt.set_level(false);
522 self.state = PmState::new();
523 if let Some(acpi_mode) = self.enable_acpi_mode {
524 self.enable_acpi_mode(acpi_mode.default_pio_dynamic)
525 }
526 }
527}
528
529impl ChipsetDevice for PowerManagementDevice {
530 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
531 Some(self)
532 }
533
534 fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
535 Some(self)
536 }
537
538 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
539 Some(self)
540 }
541}
542
543impl PollDevice for PowerManagementDevice {
544 fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
545 while let std::task::Poll::Ready(Some(PowerButtonRpc::Press(rpc))) =
546 self.rt.power_button_recv.poll_next_unpin(cx)
547 {
548 self.state.status |= POWER_BUTTON_MASK;
549 self.check_interrupt_assertion();
550 rpc.complete(());
551 }
552 }
553}
554
555fn aligned_offset(offset: u8) -> Option<u8> {
556 const TABLE: &[(DynReg, u8)] = &[
557 (DynReg::STATUS, 2), (DynReg::RESUME_ENABLE, 2), (DynReg::CONTROL, 2), (DynReg::TIMER, 4), (DynReg::GEN_PURPOSE_STATUS, 2), (DynReg::GEN_PURPOSE_ENABLE, 2), (DynReg::PROC_CONTROL, 4), (DynReg::PROC_L2, 1), (DynReg::PROC_L3, 1), (DynReg::GLOBAL_STATUS, 2), (DynReg::DEVICE_STATUS, 4), (DynReg::GLOBAL_ENABLE, 2), (DynReg::GLOBAL_CONTROL, 4), (DynReg::DEVICE_CONTROL, 4), (DynReg::GENERAL_INPUT1, 1), (DynReg::GENERAL_INPUT2, 1), (DynReg::GENERAL_INPUT3, 1), (DynReg::GENERAL_OUTPUT0, 1), (DynReg::GENERAL_OUTPUT2, 1), (DynReg::GENERAL_OUTPUT3, 1), (DynReg::GENERAL_OUTPUT4, 1), (DynReg::RESET, 1), ];
580
581 for (start, len) in TABLE.iter().copied() {
582 if offset >= start.0 && offset < start.0 + len {
583 return Some(start.0);
584 }
585 }
586 None
587}
588
589impl PortIoIntercept for PowerManagementDevice {
590 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
591 if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
592 let offset = offset as u8;
593 let value = if let Some(aligned_offset) = aligned_offset(offset) {
594 let value: u64 = self
595 .state
596 .read_dynamic(&self.rt.vmtime, aligned_offset)
597 .into();
598 value >> ((offset - aligned_offset) * 8)
599 } else {
600 tracelimit::warn_ratelimited!(offset, "unknown read from relative offset");
601 0
602 };
603
604 data.copy_from_slice(&value.to_ne_bytes()[..data.len()]);
605 return IoResult::Ok;
606 }
607
608 IoResult::Err(IoError::InvalidRegister)
609 }
610
611 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
612 if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
613 let offset = offset as u8;
614 let mut value = [0; 8];
615 value[..data.len()].copy_from_slice(data);
616 let value = u32::from_ne_bytes(value[..4].try_into().unwrap());
617 if let Some(aligned_offset) = aligned_offset(offset) {
618 let mask = !0u64 >> ((8 - data.len()) * 8) << ((offset - aligned_offset) * 8);
619 let value = value << ((offset - aligned_offset) * 8);
620 self.state
621 .write_dynamic(&mut self.rt.action, aligned_offset, value, mask as u32)
622 } else {
623 tracelimit::warn_ratelimited!(offset, value, "unknown write to relative offset");
624 }
625
626 self.check_interrupt_assertion();
627 return IoResult::Ok;
628 }
629
630 IoResult::Err(IoError::InvalidRegister)
631 }
632}
633
634impl LineInterruptTarget for PowerManagementDevice {
639 fn set_irq(&mut self, vector: u32, high: bool) {
640 self.state.general_purpose_status |= (high as u16) << vector;
642 self.check_interrupt_assertion();
643 }
644
645 fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
646 &[0..=15]
647 }
648}
649
650mod saved_state {
651 use super::*;
652 use vmcore::save_restore::RestoreError;
653 use vmcore::save_restore::SaveError;
654 use vmcore::save_restore::SaveRestore;
655
656 mod state {
657 use mesh::payload::Protobuf;
658 use vmcore::save_restore::SavedStateRoot;
659
660 #[derive(Protobuf, SavedStateRoot)]
661 #[mesh(package = "chipset.pm")]
662 pub struct SavedState {
663 #[mesh(1)]
664 pub general_purpose_output: u32,
665 #[mesh(2)]
666 pub status: u16,
667 #[mesh(3)]
668 pub resume_enable: u16,
669 #[mesh(4)]
670 pub control: u16,
671 #[mesh(5)]
672 pub general_purpose_status: u16,
673 #[mesh(6)]
674 pub general_purpose_enable: u16,
675 #[mesh(7)]
676 pub processor_control: u32,
677 #[mesh(8)]
678 pub device_status: u32,
679 #[mesh(9)]
680 pub global_status: u16,
681 #[mesh(10)]
682 pub global_enable: u16,
683 #[mesh(11)]
684 pub global_control: u32,
685 #[mesh(12)]
686 pub device_control: u32,
687 }
688 }
689
690 impl SaveRestore for PowerManagementDevice {
691 type SavedState = state::SavedState;
692
693 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
694 let PmState {
695 general_purpose_output,
696 status,
697 resume_enable,
698 control,
699 general_purpose_status,
700 general_purpose_enable,
701 processor_control,
702 device_status,
703 global_status,
704 global_enable,
705 global_control,
706 device_control,
707 } = self.state;
708
709 let saved_state = state::SavedState {
710 general_purpose_output,
711 status,
712 resume_enable,
713 control,
714 general_purpose_status,
715 general_purpose_enable,
716 processor_control,
717 device_status,
718 global_status,
719 global_enable,
720 global_control,
721 device_control,
722 };
723
724 Ok(saved_state)
725 }
726
727 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
728 let state::SavedState {
729 general_purpose_output,
730 status,
731 resume_enable,
732 control,
733 general_purpose_status,
734 general_purpose_enable,
735 processor_control,
736 device_status,
737 global_status,
738 global_enable,
739 global_control,
740 device_control,
741 } = state;
742
743 self.state = PmState {
744 general_purpose_output,
745 status,
746 resume_enable,
747 control,
748 general_purpose_status,
749 general_purpose_enable,
750 processor_control,
751 device_status,
752 global_status,
753 global_enable,
754 global_control,
755 device_control,
756 };
757
758 self.check_interrupt_assertion();
759
760 Ok(())
761 }
762 }
763
764 #[cfg(test)]
765 mod tests {
766 use super::*;
767 use chipset_device::pio::ExternallyManagedPortIoIntercepts;
768 use mesh::rpc::RpcSend;
769 use pal_async::DefaultDriver;
770 use pal_async::async_test;
771 use vmcore::line_interrupt::test_helpers::TestLineInterruptTarget;
772 use vmcore::vmtime::VmTime;
773 use vmcore::vmtime::VmTimeKeeper;
774
775 const BASE: u16 = 0x1000;
776
777 struct PioRegion(Option<u16>);
778
779 impl ControlPortIoIntercept for PioRegion {
780 fn region_name(&self) -> &str {
781 "pm"
782 }
783 fn map(&mut self, addr: u16) {
784 self.0 = Some(addr);
785 }
786 fn unmap(&mut self) {
787 self.0 = None;
788 }
789 fn addr(&self) -> Option<u16> {
790 self.0
791 }
792 fn len(&self) -> u16 {
793 0x37
794 }
795 fn offset_of(&self, addr: u16) -> Option<u16> {
796 addr.checked_sub(self.0?)
797 .filter(|offset| *offset < self.len())
798 }
799 }
800
801 async fn new_pm(
802 driver: &DefaultDriver,
803 line: LineInterrupt,
804 power_button_recv: Option<mesh::Receiver<PowerButtonRpc>>,
805 ) -> (VmTimeKeeper, PowerManagementDevice) {
806 let time = VmTimeKeeper::new(driver, VmTime::from_100ns(0));
807 let access = time.builder().build(driver).await.unwrap();
808 let mut pm = PowerManagementDevice::new(
809 Box::new(|_| panic!("unexpected power action")),
810 line,
811 &mut ExternallyManagedPortIoIntercepts,
812 access.access("pm"),
813 Some(EnableAcpiMode {
814 default_pio_dynamic: BASE,
815 }),
816 None,
817 power_button_recv,
818 );
819 pm.rt.pio_dynamic = Box::new(PioRegion(Some(BASE)));
820 (time, pm)
821 }
822
823 fn write(pm: &mut PowerManagementDevice, reg: DynReg, value: u16) {
824 pm.io_write(BASE + u16::from(reg.0), &value.to_le_bytes())
825 .unwrap();
826 }
827
828 fn gpe_status(pm: &mut PowerManagementDevice) -> u16 {
829 let mut data = [0; 2];
830 pm.io_read(BASE + u16::from(DynReg::GEN_PURPOSE_STATUS.0), &mut data)
831 .unwrap();
832 u16::from_le_bytes(data)
833 }
834
835 #[async_test]
836 async fn power_button_latches_until_cleared_and_obeys_sci_enable(driver: DefaultDriver) {
837 let target = TestLineInterruptTarget::new_arc();
838 let (send, recv) = mesh::channel();
839 let (_time, mut pm) = new_pm(
840 &driver,
841 LineInterrupt::new_with_target("pm", target.clone(), 0),
842 Some(recv),
843 )
844 .await;
845
846 write(&mut pm, DynReg::RESUME_ENABLE, ENABLE_TIMER_OVERFLOW_MASK);
847 let mut response = std::pin::pin!(send.call(PowerButtonRpc::Press, ()));
848 assert!(futures::poll!(&mut response).is_pending());
849 futures::future::poll_fn(|cx| {
850 pm.poll_device(cx);
851 std::task::Poll::Ready(())
852 })
853 .await;
854 response.await.unwrap();
855 let mut status = [0; 2];
856 pm.io_read(BASE + u16::from(DynReg::STATUS.0), &mut status)
857 .unwrap();
858 assert_eq!(u16::from_le_bytes(status), POWER_BUTTON_MASK);
859 assert!(
860 !target.is_high(0),
861 "timer enable must not enable the power button"
862 );
863
864 write(&mut pm, DynReg::RESUME_ENABLE, POWER_BUTTON_MASK);
865 assert!(target.is_high(0));
866 write(&mut pm, DynReg::CONTROL, 0);
867 assert!(!target.is_high(0));
868 write(&mut pm, DynReg::CONTROL, CONTROL_SCI_ENABLE_MASK);
869 assert!(target.is_high(0));
870
871 let saved = pm.save().unwrap();
872 pm.reset().await;
873 assert!(!target.is_high(0));
874 assert_eq!(pm.state.status, 0);
875 pm.restore(saved).unwrap();
876 assert!(target.is_high(0));
877 pm.pcat_facp_acpi_enable(false);
878 assert!(!target.is_high(0));
879 pm.pcat_facp_acpi_enable(true);
880 assert!(target.is_high(0));
881 write(&mut pm, DynReg::STATUS, POWER_BUTTON_MASK);
882 assert!(!target.is_high(0));
883 assert_eq!(pm.state.status & POWER_BUTTON_MASK, 0);
884 }
885
886 #[async_test]
887 async fn sci_requires_a_matching_enabled_gpe(driver: DefaultDriver) {
888 let target = TestLineInterruptTarget::new_arc();
889 let (_time, mut pm) = new_pm(
890 &driver,
891 LineInterrupt::new_with_target("pm", target.clone(), 0),
892 None,
893 )
894 .await;
895 write(&mut pm, DynReg::GEN_PURPOSE_ENABLE, 1);
896 pm.set_irq(8, true);
897 pm.set_irq(8, false);
898 assert_eq!(gpe_status(&mut pm), 1 << 8);
899 assert!(!target.is_high(0), "GPE0 enable must not enable GPE8");
900 write(&mut pm, DynReg::GEN_PURPOSE_ENABLE, 1 << 8);
901 assert!(target.is_high(0));
902 write(&mut pm, DynReg::GEN_PURPOSE_STATUS, 1 << 8);
903 assert!(!target.is_high(0));
904 }
905
906 #[async_test]
907 async fn sci_requires_a_matching_enabled_pm_event(driver: DefaultDriver) {
908 let target = TestLineInterruptTarget::new_arc();
909 let (_time, mut pm) = new_pm(
910 &driver,
911 LineInterrupt::new_with_target("pm", target.clone(), 0),
912 None,
913 )
914 .await;
915 pm.state.status = 1 << 5;
916 write(&mut pm, DynReg::RESUME_ENABLE, ENABLE_TIMER_OVERFLOW_MASK);
917 assert!(
918 !target.is_high(0),
919 "timer enable must not enable the global event"
920 );
921 write(&mut pm, DynReg::RESUME_ENABLE, 1 << 5);
922 assert!(target.is_high(0));
923 write(&mut pm, DynReg::STATUS, 1 << 5);
924 assert!(!target.is_high(0));
925 }
926 }
927}