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