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.update_dynamic_pio_mappings(Some(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)]
463 pub fn update_dynamic_pio_mappings(&mut self, pio_dynamic_addr: Option<u16>) {
464 match pio_dynamic_addr {
465 Some(addr) => {
466 self.rt.pio_dynamic.map(addr);
467 if let Some(assist) = &self.rt.pm_timer_assist {
468 assist.set(Some(addr + DynReg::TIMER.0 as u16))
469 }
470 }
471 None => {
472 self.rt.pio_dynamic.unmap();
473 if let Some(assist) = &self.rt.pm_timer_assist {
474 assist.set(None);
475 }
476 }
477 }
478 }
479
480 #[inline(always)]
484 pub fn pcat_facp_acpi_enable(&mut self, enable: bool) {
485 if enable {
486 self.state.control |= CONTROL_SCI_ENABLE_MASK;
487 self.state.resume_enable |= ENABLE_TIMER_OVERFLOW_MASK;
488 } else {
489 self.state.control &= !CONTROL_SCI_ENABLE_MASK;
490 self.state.resume_enable &= !ENABLE_TIMER_OVERFLOW_MASK;
491 }
492 }
493
494 pub fn power_action(&mut self) -> &mut PowerActionFn {
498 &mut self.rt.action
499 }
500}
501
502impl ChangeDeviceState for PowerManagementDevice {
503 fn start(&mut self) {}
504
505 async fn stop(&mut self) {}
506
507 async fn reset(&mut self) {
508 self.update_dynamic_pio_mappings(None);
509 self.rt.acpi_interrupt.set_level(false);
510 self.state = PmState::new();
511 if let Some(acpi_mode) = self.enable_acpi_mode {
512 self.enable_acpi_mode(acpi_mode.default_pio_dynamic)
513 }
514 }
515}
516
517impl ChipsetDevice for PowerManagementDevice {
518 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
519 Some(self)
520 }
521
522 fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
523 Some(self)
524 }
525}
526
527fn aligned_offset(offset: u8) -> Option<u8> {
528 const TABLE: &[(DynReg, u8)] = &[
529 (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), ];
552
553 for (start, len) in TABLE.iter().copied() {
554 if offset >= start.0 && offset < start.0 + len {
555 return Some(start.0);
556 }
557 }
558 None
559}
560
561impl PortIoIntercept for PowerManagementDevice {
562 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
563 if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
564 let offset = offset as u8;
565 let value = if let Some(aligned_offset) = aligned_offset(offset) {
566 let value: u64 = self
567 .state
568 .read_dynamic(&self.rt.vmtime, aligned_offset)
569 .into();
570 value >> ((offset - aligned_offset) * 8)
571 } else {
572 tracelimit::warn_ratelimited!(offset, "unknown read from relative offset");
573 0
574 };
575
576 data.copy_from_slice(&value.to_ne_bytes()[..data.len()]);
577 return IoResult::Ok;
578 }
579
580 IoResult::Err(IoError::InvalidRegister)
581 }
582
583 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
584 if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
585 let offset = offset as u8;
586 let mut value = [0; 8];
587 value[..data.len()].copy_from_slice(data);
588 let value = u32::from_ne_bytes(value[..4].try_into().unwrap());
589 if let Some(aligned_offset) = aligned_offset(offset) {
590 let mask = !0u64 >> ((8 - data.len()) * 8) << ((offset - aligned_offset) * 8);
591 let value = value << ((offset - aligned_offset) * 8);
592 self.state
593 .write_dynamic(&mut self.rt.action, aligned_offset, value, mask as u32)
594 } else {
595 tracelimit::warn_ratelimited!(offset, value, "unknown write to relative offset");
596 }
597
598 self.check_interrupt_assertion();
599 return IoResult::Ok;
600 }
601
602 IoResult::Err(IoError::InvalidRegister)
603 }
604}
605
606impl LineInterruptTarget for PowerManagementDevice {
611 fn set_irq(&mut self, vector: u32, high: bool) {
612 self.state.general_purpose_status |= (high as u16) << vector;
614 self.check_interrupt_assertion();
615 }
616
617 fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
618 &[0..=15]
619 }
620}
621
622mod saved_state {
623 use super::*;
624 use vmcore::save_restore::RestoreError;
625 use vmcore::save_restore::SaveError;
626 use vmcore::save_restore::SaveRestore;
627
628 mod state {
629 use mesh::payload::Protobuf;
630 use vmcore::save_restore::SavedStateRoot;
631
632 #[derive(Protobuf, SavedStateRoot)]
633 #[mesh(package = "chipset.pm")]
634 pub struct SavedState {
635 #[mesh(1)]
636 pub general_purpose_output: u32,
637 #[mesh(2)]
638 pub status: u16,
639 #[mesh(3)]
640 pub resume_enable: u16,
641 #[mesh(4)]
642 pub control: u16,
643 #[mesh(5)]
644 pub general_purpose_status: u16,
645 #[mesh(6)]
646 pub general_purpose_enable: u16,
647 #[mesh(7)]
648 pub processor_control: u32,
649 #[mesh(8)]
650 pub device_status: u32,
651 #[mesh(9)]
652 pub global_status: u16,
653 #[mesh(10)]
654 pub global_enable: u16,
655 #[mesh(11)]
656 pub global_control: u32,
657 #[mesh(12)]
658 pub device_control: u32,
659 }
660 }
661
662 impl SaveRestore for PowerManagementDevice {
663 type SavedState = state::SavedState;
664
665 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
666 let PmState {
667 general_purpose_output,
668 status,
669 resume_enable,
670 control,
671 general_purpose_status,
672 general_purpose_enable,
673 processor_control,
674 device_status,
675 global_status,
676 global_enable,
677 global_control,
678 device_control,
679 } = self.state;
680
681 let saved_state = state::SavedState {
682 general_purpose_output,
683 status,
684 resume_enable,
685 control,
686 general_purpose_status,
687 general_purpose_enable,
688 processor_control,
689 device_status,
690 global_status,
691 global_enable,
692 global_control,
693 device_control,
694 };
695
696 Ok(saved_state)
697 }
698
699 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
700 let state::SavedState {
701 general_purpose_output,
702 status,
703 resume_enable,
704 control,
705 general_purpose_status,
706 general_purpose_enable,
707 processor_control,
708 device_status,
709 global_status,
710 global_enable,
711 global_control,
712 device_control,
713 } = state;
714
715 self.state = PmState {
716 general_purpose_output,
717 status,
718 resume_enable,
719 control,
720 general_purpose_status,
721 general_purpose_enable,
722 processor_control,
723 device_status,
724 global_status,
725 global_enable,
726 global_control,
727 device_control,
728 };
729
730 self.check_interrupt_assertion();
731
732 Ok(())
733 }
734 }
735}