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 inspect::Inspect;
41use inspect::InspectMut;
42use open_enum::open_enum;
43use vmcore::device_state::ChangeDeviceState;
44use vmcore::line_interrupt::LineInterrupt;
45use vmcore::vmtime::VmTimeAccess;
46
47open_enum! {
48 pub enum DynReg: u8 {
50 #![expect(missing_docs)] STATUS = 0x00, RESUME_ENABLE = 0x02, CONTROL = 0x04, TIMER = 0x08, GEN_PURPOSE_STATUS = 0x0C, 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 = 0x33, GENERAL_OUTPUT0 = 0x34, GENERAL_OUTPUT2 = 0x35, GENERAL_OUTPUT3 = 0x36, GENERAL_OUTPUT4 = 0x37, }
74}
75
76const 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; pub const RESET_VALUE: u8 = 0x01; #[derive(Clone, Debug, Inspect)]
90struct PmState {
91 #[inspect(hex)]
92 general_purpose_output: u32,
93
94 #[inspect(hex)]
96 status: u16,
97 #[inspect(hex)]
98 resume_enable: u16,
99 #[inspect(hex)]
100 control: u16,
101 #[inspect(hex)]
102 general_purpose_status: u16,
103 #[inspect(hex)]
104 general_purpose_enable: u16,
105 #[inspect(hex)]
106 processor_control: u32,
107 #[inspect(hex)]
108 device_status: u32,
109 #[inspect(hex)]
110 global_status: u16,
111 #[inspect(hex)]
112 global_enable: u16,
113 #[inspect(hex)]
114 global_control: u32,
115 #[inspect(hex)]
116 device_control: u32,
117}
118
119impl PmState {
120 fn new() -> Self {
121 Self {
122 general_purpose_output: 0x7FFFBFFF,
123 status: 0,
124 resume_enable: 0,
125 control: 0,
126 general_purpose_status: 0,
127 general_purpose_enable: 0,
128 processor_control: 0,
129 device_status: 0,
130 global_status: 0,
131 global_enable: 0,
132 global_control: 0,
133 device_control: 0,
134 }
135 }
136
137 fn read_dynamic(&mut self, vmtime: &VmTimeAccess, offset: u8) -> u32 {
138 match DynReg(offset) {
139 DynReg::STATUS => self.status.into(),
142 DynReg::RESUME_ENABLE => (self.resume_enable & 0x0521).into(),
144 DynReg::CONTROL => self.control.into(),
146 DynReg::TIMER => {
153 let now = vmtime.now();
154 (now.as_100ns() as u128 * 3_579_545 / 10_000_000) as u32
156 }
157 DynReg::GEN_PURPOSE_STATUS => self.general_purpose_status.into(),
159 DynReg::GEN_PURPOSE_ENABLE => self.general_purpose_enable.into(),
161 DynReg::PROC_CONTROL => self.processor_control,
163 DynReg::PROC_L2 => 0,
165 DynReg::PROC_L3 => 0,
167 DynReg::GLOBAL_STATUS => {
169 let mut value = self.global_status;
170
171 if (self.status & !TIMER_OVERFLOW_MASK) != 0 {
174 value |= STATUS_PM_MASK;
175 }
176
177 if self.general_purpose_status != 0 {
178 value |= STATUS_GP_MASK;
179 }
180
181 if self.device_status != 0 {
182 value |= STATUS_DEVICE_MASK;
183 }
184
185 value.into()
186 }
187 DynReg::DEVICE_STATUS => self.device_status,
189 DynReg::GLOBAL_ENABLE => self.global_enable.into(),
191 DynReg::GLOBAL_CONTROL => self.global_control,
193 DynReg::DEVICE_CONTROL => self.device_control,
195 DynReg::GENERAL_INPUT1 => 0,
197 DynReg::GENERAL_INPUT2 => 0,
199 DynReg::GENERAL_INPUT3 => 0,
201 DynReg::GENERAL_OUTPUT0 => self.general_purpose_output,
203 DynReg::GENERAL_OUTPUT2 => self.general_purpose_output >> 8,
205 DynReg::GENERAL_OUTPUT3 => self.general_purpose_output >> 16,
207 DynReg::GENERAL_OUTPUT4 => self.general_purpose_output >> 24,
209 _ => {
210 tracelimit::warn_ratelimited!(?offset, "unhandled register read");
211 !0
212 }
213 }
214 }
215
216 fn write_dynamic(&mut self, action: &mut PowerActionFn, offset: u8, value: u32, mask: u32) {
217 match DynReg(offset) {
218 DynReg::STATUS => self.status &= !value as u16,
220 DynReg::RESUME_ENABLE => {
222 self.resume_enable &= !mask as u16;
224 self.resume_enable |= value as u16 & 0x0521;
225 }
226 DynReg::CONTROL => {
228 let value = value as u16;
229 if (value & CONTROL_SUSPEND_ENABLE_MASK) != 0 {
230 let suspend_type = (value & CONTROL_SUSPEND_TYPE_MASK) >> 10;
237 match suspend_type {
238 0 => (action)(PowerAction::PowerOff),
239 1 => (action)(PowerAction::Hibernate),
240 _ => {}
241 }
242 }
243
244 self.control &= !mask as u16;
245 self.control |= value;
246 }
247 DynReg::TIMER => {
248 }
250 DynReg::GEN_PURPOSE_STATUS => {
252 self.general_purpose_status &= !value as u16;
253 }
254 DynReg::GEN_PURPOSE_ENABLE => {
256 self.general_purpose_enable &= !mask as u16;
257 self.general_purpose_enable |= value as u16 & 0x0f01;
258 }
259 DynReg::PROC_CONTROL => {
261 self.processor_control &= !mask;
262 self.processor_control |= value & 0x00023E1E;
263 }
264 DynReg::PROC_L2 => {} DynReg::PROC_L3 => {} DynReg::GLOBAL_STATUS => {
270 self.global_status &= !(value & 0x0D25) as u16;
274 }
275
276 DynReg::DEVICE_STATUS => self.device_status = !value,
278 DynReg::GLOBAL_ENABLE => {
280 self.global_enable &= !mask as u16;
281 self.global_enable |= (value & 0x8D13) as u16;
282 }
283 DynReg::GLOBAL_CONTROL => {
285 let value = value & !GLOBAL_CONTROL_BIOS_RLS_MASK;
287 self.global_control &= !mask;
288 self.global_control |= value & 0x0701FFE7;
289 }
290 DynReg::DEVICE_CONTROL => {
292 self.device_control &= !mask;
293 self.device_control |= value;
294 }
295 DynReg::RESET => {
297 if value as u8 == RESET_VALUE {
298 (action)(PowerAction::Reboot);
299 }
300 }
301 DynReg::GENERAL_OUTPUT0 => {
303 let mask = mask & 0xffff;
304 self.general_purpose_output &= !mask;
305 self.general_purpose_output |= value & mask;
306 }
307 DynReg::GENERAL_OUTPUT2 => {
309 self.general_purpose_output &= !0xFF00;
310 self.general_purpose_output |= (value << 8) & 0xFF00;
311 }
312 DynReg::GENERAL_OUTPUT3 => {
314 let mask = mask & 0xffff;
315 self.general_purpose_output &= !(mask << 16);
316 self.general_purpose_output |= (value << 16) & mask;
317 }
318 DynReg::GENERAL_OUTPUT4 => {
320 self.general_purpose_output &= !0xFF000000;
321 self.general_purpose_output |= (value << 24) & 0x7F000000;
322 }
323 _ => tracelimit::warn_ratelimited!(?offset, ?value, "unhandled register write"),
324 }
325 }
326}
327
328#[expect(missing_docs)] #[derive(Debug, Copy, Clone)]
331pub enum PowerAction {
332 PowerOff,
333 Hibernate,
334 Reboot,
335}
336
337pub type PowerActionFn = Box<dyn FnMut(PowerAction) + Send + Sync>;
339
340#[derive(Inspect)]
341struct PowerManagementDeviceRt {
342 pio_dynamic: Box<dyn ControlPortIoIntercept>,
344 acpi_interrupt: LineInterrupt,
346 vmtime: VmTimeAccess,
348 #[inspect(skip)]
350 action: PowerActionFn,
351 #[inspect(skip)]
353 pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
354}
355
356#[derive(Debug, Clone, Copy)]
365pub struct EnableAcpiMode {
366 pub default_pio_dynamic: u16,
368}
369
370pub use chipset_resources::pm::PmTimerAssist;
372
373#[derive(InspectMut)]
377pub struct PowerManagementDevice {
378 #[inspect(skip)]
380 enable_acpi_mode: Option<EnableAcpiMode>,
381
382 #[inspect(flatten)]
384 rt: PowerManagementDeviceRt,
385
386 #[inspect(flatten)]
388 state: PmState,
389}
390
391impl PowerManagementDevice {
392 pub fn new(
402 action: PowerActionFn,
403 acpi_interrupt: LineInterrupt,
404 register_pio: &mut dyn RegisterPortIoIntercept,
405 vmtime: VmTimeAccess,
406 enable_acpi_mode: Option<EnableAcpiMode>,
407 pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
408 ) -> Self {
409 let pio_dynamic = register_pio.new_io_region("dynamic", 0x37);
410
411 let mut this = PowerManagementDevice {
412 enable_acpi_mode,
413 rt: PowerManagementDeviceRt {
414 pio_dynamic,
415 action,
416 acpi_interrupt,
417 vmtime,
418 pm_timer_assist,
419 },
420 state: PmState::new(),
421 };
422
423 if let Some(pm_timer_assist) = &this.rt.pm_timer_assist {
425 pm_timer_assist.set(None)
426 }
427
428 if let Some(acpi_mode) = enable_acpi_mode {
429 this.enable_acpi_mode(acpi_mode.default_pio_dynamic)
430 }
431
432 this
433 }
434
435 fn enable_acpi_mode(&mut self, default_pio_dynamic: u16) {
436 tracing::debug!("ACPI mode enabled");
437 self.rt.pio_dynamic.map(default_pio_dynamic);
438 self.state.control = CONTROL_SCI_ENABLE_MASK;
439 }
440
441 pub fn check_interrupt_assertion(&self) {
454 let level = (self.state.resume_enable > 0 && self.state.status > 0)
456 || (self.state.general_purpose_status > 0 && self.state.general_purpose_enable > 0);
457
458 self.rt.acpi_interrupt.set_level(level)
459 }
460
461 #[inline(always)]
465 pub fn update_dynamic_pio_mappings(&mut self, pio_dynamic_addr: Option<u16>) {
466 match pio_dynamic_addr {
467 Some(addr) => {
468 self.rt.pio_dynamic.map(addr);
469 if let Some(assist) = &self.rt.pm_timer_assist {
470 assist.set(Some(addr + DynReg::TIMER.0 as u16))
471 }
472 }
473 None => {
474 self.rt.pio_dynamic.unmap();
475 if let Some(assist) = &self.rt.pm_timer_assist {
476 assist.set(None);
477 }
478 }
479 }
480 }
481
482 #[inline(always)]
486 pub fn pcat_facp_acpi_enable(&mut self, enable: bool) {
487 if enable {
488 self.state.control |= CONTROL_SCI_ENABLE_MASK;
489 self.state.resume_enable |= ENABLE_TIMER_OVERFLOW_MASK;
490 } else {
491 self.state.control &= !CONTROL_SCI_ENABLE_MASK;
492 self.state.resume_enable &= !ENABLE_TIMER_OVERFLOW_MASK;
493 }
494 }
495
496 pub fn power_action(&mut self) -> &mut PowerActionFn {
500 &mut self.rt.action
501 }
502}
503
504impl ChangeDeviceState for PowerManagementDevice {
505 fn start(&mut self) {}
506
507 async fn stop(&mut self) {}
508
509 async fn reset(&mut self) {
510 self.rt.pio_dynamic.unmap();
511 self.rt.acpi_interrupt.set_level(false);
512 self.state = PmState::new();
513 if let Some(acpi_mode) = self.enable_acpi_mode {
514 self.enable_acpi_mode(acpi_mode.default_pio_dynamic)
515 }
516 }
517}
518
519impl ChipsetDevice for PowerManagementDevice {
520 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
521 Some(self)
522 }
523
524 fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
525 Some(self)
526 }
527}
528
529fn aligned_offset(offset: u8) -> Option<u8> {
530 const TABLE: &[(DynReg, u8)] = &[
531 (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), ];
554
555 for (start, len) in TABLE.iter().copied() {
556 if offset >= start.0 && offset < start.0 + len {
557 return Some(start.0);
558 }
559 }
560 None
561}
562
563impl PortIoIntercept for PowerManagementDevice {
564 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
565 if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
566 let offset = offset as u8;
567 let value = if let Some(aligned_offset) = aligned_offset(offset) {
568 let value: u64 = self
569 .state
570 .read_dynamic(&self.rt.vmtime, aligned_offset)
571 .into();
572 value >> ((offset - aligned_offset) * 8)
573 } else {
574 tracelimit::warn_ratelimited!(offset, "unknown read from relative offset");
575 0
576 };
577
578 data.copy_from_slice(&value.to_ne_bytes()[..data.len()]);
579 return IoResult::Ok;
580 }
581
582 IoResult::Err(IoError::InvalidRegister)
583 }
584
585 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
586 if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
587 let offset = offset as u8;
588 let mut value = [0; 8];
589 value[..data.len()].copy_from_slice(data);
590 let value = u32::from_ne_bytes(value[..4].try_into().unwrap());
591 if let Some(aligned_offset) = aligned_offset(offset) {
592 let mask = !0u64 >> ((8 - data.len()) * 8) << ((offset - aligned_offset) * 8);
593 let value = value << ((offset - aligned_offset) * 8);
594 self.state
595 .write_dynamic(&mut self.rt.action, aligned_offset, value, mask as u32)
596 } else {
597 tracelimit::warn_ratelimited!(offset, value, "unknown write to relative offset");
598 }
599
600 self.check_interrupt_assertion();
601 return IoResult::Ok;
602 }
603
604 IoResult::Err(IoError::InvalidRegister)
605 }
606}
607
608impl LineInterruptTarget for PowerManagementDevice {
613 fn set_irq(&mut self, vector: u32, high: bool) {
614 self.state.general_purpose_status |= (high as u16) << vector;
616 self.check_interrupt_assertion();
617 }
618
619 fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
620 &[0..=15]
621 }
622}
623
624mod saved_state {
625 use super::*;
626 use vmcore::save_restore::RestoreError;
627 use vmcore::save_restore::SaveError;
628 use vmcore::save_restore::SaveRestore;
629
630 mod state {
631 use mesh::payload::Protobuf;
632 use vmcore::save_restore::SavedStateRoot;
633
634 #[derive(Protobuf, SavedStateRoot)]
635 #[mesh(package = "chipset.pm")]
636 pub struct SavedState {
637 #[mesh(1)]
638 pub general_purpose_output: u32,
639 #[mesh(2)]
640 pub status: u16,
641 #[mesh(3)]
642 pub resume_enable: u16,
643 #[mesh(4)]
644 pub control: u16,
645 #[mesh(5)]
646 pub general_purpose_status: u16,
647 #[mesh(6)]
648 pub general_purpose_enable: u16,
649 #[mesh(7)]
650 pub processor_control: u32,
651 #[mesh(8)]
652 pub device_status: u32,
653 #[mesh(9)]
654 pub global_status: u16,
655 #[mesh(10)]
656 pub global_enable: u16,
657 #[mesh(11)]
658 pub global_control: u32,
659 #[mesh(12)]
660 pub device_control: u32,
661 }
662 }
663
664 impl SaveRestore for PowerManagementDevice {
665 type SavedState = state::SavedState;
666
667 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
668 let PmState {
669 general_purpose_output,
670 status,
671 resume_enable,
672 control,
673 general_purpose_status,
674 general_purpose_enable,
675 processor_control,
676 device_status,
677 global_status,
678 global_enable,
679 global_control,
680 device_control,
681 } = self.state;
682
683 let saved_state = state::SavedState {
684 general_purpose_output,
685 status,
686 resume_enable,
687 control,
688 general_purpose_status,
689 general_purpose_enable,
690 processor_control,
691 device_status,
692 global_status,
693 global_enable,
694 global_control,
695 device_control,
696 };
697
698 Ok(saved_state)
699 }
700
701 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
702 let state::SavedState {
703 general_purpose_output,
704 status,
705 resume_enable,
706 control,
707 general_purpose_status,
708 general_purpose_enable,
709 processor_control,
710 device_status,
711 global_status,
712 global_enable,
713 global_control,
714 device_control,
715 } = state;
716
717 self.state = PmState {
718 general_purpose_output,
719 status,
720 resume_enable,
721 control,
722 general_purpose_status,
723 general_purpose_enable,
724 processor_control,
725 device_status,
726 global_status,
727 global_enable,
728 global_control,
729 device_control,
730 };
731
732 self.check_interrupt_assertion();
733
734 Ok(())
735 }
736 }
737}