Skip to main content

chipset/pm/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Power Management Device (as found on the PIIX4 chipset - kinda)
5//!
6//! # What's with all this PIIX4 stuff?
7//!
8//! The current implementation of [`PowerManagementDevice`] is based off code in
9//! Hyper-V, which happens to emulate the _specific_ PM device as found on the
10//! PIIX4 chipset.
11//!
12//! ...well, kinda.
13//!
14//! This current implementation is only a _partial_ port of the PM device found
15//! on the PIIX4 chipset, with a good chunk of the PIIX4 functionality having
16//! been lifted into a wrapper device found under `chipset_legacy/piix4_pm.rs`.
17//!
18//! # So, what's next?
19//!
20//! Eventually, this device should be swapped out for a minimal "generic" PM /
21//! ACPI device, with all remaining PIIX4 specific functionality being lifted
22//! into the legacy piix4_pm device (which may or may not end up reusing the
23//! generic PM device as part of its implementation).
24//!
25//! Of course, there's always the tricky issue that the current implementation
26//! _works fine_, so when this work is going to happen... well, your guess is as
27//! good as mine.
28
29#![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    /// Power management I/O offsets from base port address
52    pub enum DynReg: u8 {
53        #![expect(missing_docs)] // self explanatory constants
54        STATUS             = acpi_spec::hyperv::PM_STATUS as u8, // two-byte value
55        RESUME_ENABLE      = 0x02, // two-byte value
56        CONTROL            = acpi_spec::hyperv::PM_CONTROL as u8, // two-byte value
57        TIMER              = acpi_spec::hyperv::PM_TIMER as u8, // four-byte value (read only)
58        GEN_PURPOSE_STATUS = acpi_spec::hyperv::PM_GPE0_STATUS as u8, // two-byte value
59        GEN_PURPOSE_ENABLE = 0x0E, // two-byte value
60        PROC_CONTROL       = 0x10, // four-byte value
61        PROC_L2            = 0x14, // one-byte value
62        PROC_L3            = 0x15, // one-byte value
63        GLOBAL_STATUS      = 0x18, // two-byte value
64        DEVICE_STATUS      = 0x1C, // four-byte value
65        GLOBAL_ENABLE      = 0x20, // two-byte value
66        GLOBAL_CONTROL     = 0x28, // four-byte value
67        DEVICE_CONTROL     = 0x2C, // four-byte value
68        GENERAL_INPUT1     = 0x30, // one-byte value (read only)
69        GENERAL_INPUT2     = 0x31, // one-byte value (read only)
70        GENERAL_INPUT3     = 0x32, // one-byte value (read only)
71        RESET              = acpi_spec::hyperv::PM_RESET as u8, // one-byte value
72        GENERAL_OUTPUT0    = 0x34, // one-byte value
73        GENERAL_OUTPUT2    = 0x35, // one-byte value
74        GENERAL_OUTPUT3    = 0x36, // one-byte value
75        GENERAL_OUTPUT4    = 0x37, // one-byte value
76    }
77}
78
79const CONTROL_SCI_ENABLE_MASK: u16 = 0x0001; // Events should cause SCI =  not SMI
80const CONTROL_SUSPEND_ENABLE_MASK: u16 = 0x2000; // Enable the specified suspend type
81const CONTROL_SUSPEND_TYPE_MASK: u16 = 0x1C00; // Suspend type field
82const ENABLE_TIMER_OVERFLOW_MASK: u16 = 0x0001; // Timer overflow should interrupt
83const GLOBAL_CONTROL_BIOS_RLS_MASK: u32 = 0x00000002; // Generate SCI?
84const STATUS_DEVICE_MASK: u16 = 0x0010; // One device event flags is set
85const STATUS_GP_MASK: u16 = 0x0080; // One of the GP event flags is set
86const STATUS_PM_MASK: u16 = 0x0040; // One of the PM event flags is set
87const TIMER_OVERFLOW_MASK: u16 = 0x0001; // The PM timer overflowed
88const POWER_BUTTON_MASK: u16 = 1 << 8;
89
90/// Value that initiates a system reset when written to [`DynReg::RESET`].
91pub 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    // Power Management Dynamic I/O state
99    #[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            // 0x00 - two-byte value
144            // Indicate that no events have triggered a sticky flag.
145            DynReg::STATUS => self.status.into(),
146            // 0x02 - two-byte value
147            DynReg::RESUME_ENABLE => (self.resume_enable & 0x0521).into(),
148            // 0x04 - two-byte value
149            DynReg::CONTROL => self.control.into(),
150            // 0x08 - four-byte value (read only)
151            // If the pmtimer_assist is set then the hypervisor will intercept
152            // accesses to this port and return its own reference time.
153            // Hypervisor reference time is different from our reference time,
154            // but that's ok because nothing else needs to match. This is faster
155            // than us doing this work, but not always available.
156            DynReg::TIMER => {
157                let now = vmtime.now();
158                // Convert the 100ns-period VM time to the 3.579545MHz PM timer time.
159                (now.as_100ns() as u128 * 3_579_545 / 10_000_000) as u32
160            }
161            // 0x0C - two-byte value
162            DynReg::GEN_PURPOSE_STATUS => self.general_purpose_status.into(),
163            // 0x0E - two-byte value
164            DynReg::GEN_PURPOSE_ENABLE => self.general_purpose_enable.into(),
165            // 0x10 - four-byte value
166            DynReg::PROC_CONTROL => self.processor_control,
167            // 0x14 - one-byte value
168            DynReg::PROC_L2 => 0,
169            // 0x15 - one-byte value
170            DynReg::PROC_L3 => 0,
171            // 0x18 - two-byte value
172            DynReg::GLOBAL_STATUS => {
173                let mut value = self.global_status;
174
175                // Incorporate the summary status bits. It doesn't appear that
176                // the timer overflow status is paid attention to in this case.
177                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            // 0x1C - four-byte value
192            DynReg::DEVICE_STATUS => self.device_status,
193            // 0x20 - two-byte value
194            DynReg::GLOBAL_ENABLE => self.global_enable.into(),
195            // 0x28 - four-byte value
196            DynReg::GLOBAL_CONTROL => self.global_control,
197            // 0x2C - four-byte value
198            DynReg::DEVICE_CONTROL => self.device_control,
199            // 0x30 - one-byte value (read only)
200            DynReg::GENERAL_INPUT1 => 0,
201            // 0x31 - one-byte value (read only)
202            DynReg::GENERAL_INPUT2 => 0,
203            // 0x32 - one-byte value (read only)
204            DynReg::GENERAL_INPUT3 => 0,
205            // 0x34 - one-byte value
206            DynReg::GENERAL_OUTPUT0 => self.general_purpose_output,
207            // 0x35 - one-byte value
208            DynReg::GENERAL_OUTPUT2 => self.general_purpose_output >> 8,
209            // 0x36 - one-byte value
210            DynReg::GENERAL_OUTPUT3 => self.general_purpose_output >> 16,
211            // 0x37 - one-byte value
212            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            // 0x00 - two-byte value
223            DynReg::STATUS => self.status &= !value as u16,
224            // 0x02 - two-byte value
225            DynReg::RESUME_ENABLE => {
226                // 0x0521 represents the bits that are not marked as reserved in the PIIX4 manual.
227                self.resume_enable &= !mask as u16;
228                self.resume_enable |= value as u16 & 0x0521;
229            }
230            // 0x04 - two-byte value
231            DynReg::CONTROL => {
232                let value = value as u16;
233                if (value & CONTROL_SUSPEND_ENABLE_MASK) != 0 {
234                    // Get the suspend type, which is Bits[12:10] of the control register.
235                    // Our platform defines a suspend type of 0 as power off(S5) and a suspend
236                    // type of 1 as hibernate(S4); no other types are supported.The BIOS/firmware
237                    // ACPI tables must reflect these values to the guest.
238                    //
239                    // Any other values will be ignored.
240                    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                // Ignore writes.
253            }
254            // 0x0C - two-byte value
255            DynReg::GEN_PURPOSE_STATUS => {
256                self.general_purpose_status &= !value as u16;
257            }
258            // 0x0E - two-byte value
259            DynReg::GEN_PURPOSE_ENABLE => {
260                self.general_purpose_enable &= !mask as u16;
261                self.general_purpose_enable |= value as u16 & 0x0f01;
262            }
263            // 0x10 - four-byte value
264            DynReg::PROC_CONTROL => {
265                self.processor_control &= !mask;
266                self.processor_control |= value & 0x00023E1E;
267            }
268            // 0x14 - one-byte value
269            DynReg::PROC_L2 => {} // Writes to this address do nothing.
270            // 0x15 - one-byte value
271            DynReg::PROC_L3 => {} // Writes to this address do nothing.
272            // 0x18 - two-byte value
273            DynReg::GLOBAL_STATUS => {
274                // Writes of 1 clear the corresponding status bits. Some of
275                // these bits can only be cleared when other registers are
276                // cleared (i.e. they are "summary" status bits for other registers.
277                self.global_status &= !(value & 0x0D25) as u16;
278            }
279
280            // 0x1C - four-byte value
281            DynReg::DEVICE_STATUS => self.device_status = !value,
282            // 0x20 - two-byte value
283            DynReg::GLOBAL_ENABLE => {
284                self.global_enable &= !mask as u16;
285                self.global_enable |= (value & 0x8D13) as u16;
286            }
287            // 0x28 - four-byte value
288            DynReg::GLOBAL_CONTROL => {
289                // We don't support the BIOS release bit.
290                let value = value & !GLOBAL_CONTROL_BIOS_RLS_MASK;
291                self.global_control &= !mask;
292                self.global_control |= value & 0x0701FFE7;
293            }
294            // 0x2C - four-byte value
295            DynReg::DEVICE_CONTROL => {
296                self.device_control &= !mask;
297                self.device_control |= value;
298            }
299            // 0x33 - one-byte value
300            DynReg::RESET => {
301                if value as u8 == RESET_VALUE {
302                    (action)(PowerAction::Reboot);
303                }
304            }
305            // 0x34 - one-byte value
306            DynReg::GENERAL_OUTPUT0 => {
307                let mask = mask & 0xffff;
308                self.general_purpose_output &= !mask;
309                self.general_purpose_output |= value & mask;
310            }
311            // 0x35 - one-byte value
312            DynReg::GENERAL_OUTPUT2 => {
313                self.general_purpose_output &= !0xFF00;
314                self.general_purpose_output |= (value << 8) & 0xFF00;
315            }
316            // 0x36 - one-byte value
317            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            // 0x37 - one-byte value
323            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/// Power action being requested
333#[expect(missing_docs)] // self explanatory variants
334#[derive(Debug, Copy, Clone)]
335pub enum PowerAction {
336    PowerOff,
337    Hibernate,
338    Reboot,
339}
340
341/// Callback invoked whenever a power action is requested
342pub type PowerActionFn = Box<dyn FnMut(PowerAction) + Send + Sync>;
343
344#[derive(Inspect)]
345struct PowerManagementDeviceRt {
346    /// 0x37-byte IO port corresponding to the dynamic register range
347    pio_dynamic: Box<dyn ControlPortIoIntercept>,
348    /// ACPI interrupt line
349    acpi_interrupt: LineInterrupt,
350    /// VM time access for the PM timer.
351    vmtime: VmTimeAccess,
352    /// Callback invoked whenever a power action is requested
353    #[inspect(skip)]
354    action: PowerActionFn,
355    /// Enable / Disable hypervisor PM timer assist (when available)
356    #[inspect(skip)]
357    pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
358    #[inspect(skip)]
359    power_button_recv: mesh::Receiver<PowerButtonRpc>,
360}
361
362/// This is used when running the UEFI BIOS. When passed via
363/// `PowerManagementDevice::new`, the device will pre-populate various register
364/// values + automatically map the dynamic memory regions at the specified port
365/// io address.
366///
367/// NOTE: at some point, this device should be refactored into a "generic" power
368/// management device, which will do away with all the PIIX4 cruft that
369/// necessitates this extra "enable ACPI mode" call.
370#[derive(Debug, Clone, Copy)]
371pub struct EnableAcpiMode {
372    /// Default base address for the dynamic register region.
373    pub default_pio_dynamic: u16,
374}
375
376// Re-export the PmTimerAssist trait from chipset_resources.
377pub use chipset_resources::pm::PmTimerAssist;
378
379/// A power management + ACPI device.
380///
381/// See the module level docs for more details.
382#[derive(InspectMut)]
383pub struct PowerManagementDevice {
384    // Static configuration
385    #[inspect(skip)]
386    enable_acpi_mode: Option<EnableAcpiMode>,
387
388    // Runtime glue
389    #[inspect(flatten)]
390    rt: PowerManagementDeviceRt,
391
392    // Volatile state
393    #[inspect(flatten)]
394    state: PmState,
395}
396
397impl PowerManagementDevice {
398    /// Create a new [`PowerManagementDevice`].
399    ///
400    /// Most arguments to this constructor are self describing, though there are
401    /// some that merit additional explanation:
402    ///
403    /// - `action`: a callback invoked whenever the PM initiates a power event
404    /// - `pio_control` and `pio_status`: define where in the port IO space the
405    ///   control/status registers get mapped to.
406    /// - `enable_acpi_mode`: see the docs for [`EnableAcpiMode`]
407    /// - `power_button_recv`: optional fixed power-button presses advertised by
408    ///   the PCAT FADT.
409    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        // ensure timer assist is disabled
434        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    /// (used by the PIIX4 wrapper device)
452    // DEVNOTE: also used internally, but the PIIX4 wrapper device also uses it
453    ///
454    /// Evaluates whether the power management (ACPI) interrupt should be
455    /// asserted or de-asserted
456    ///
457    /// If the state is out of sync with what it should be, this function will
458    /// either assert or de-assert the interrupt. The logic for whether an ACPI
459    /// interrupt should be sent is covered in various parts of Chapter 4 of any
460    /// version of the ACPI spec.
461    ///
462    /// reSearch query: `CheckInterruptAssertion`
463    pub fn check_interrupt_assertion(&self) {
464        // Check if any power events should cause an interrupt to be asserted.
465        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    /// Remap dynamic registers based on config in the PCI config space
473    #[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    /// (used by the PIIX4 wrapper device)
492    ///
493    /// See calling code in piix4_pm.rs for details on what this does.
494    #[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    /// (used by the PIIX4 wrapper device)
507    ///
508    /// Get a mutable reference to the provided [`PowerActionFn`]
509    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),             // 0x00 - two-byte value
558        (DynReg::RESUME_ENABLE, 2),      // 0x02 - two-byte value
559        (DynReg::CONTROL, 2),            // 0x04 - two-byte value
560        (DynReg::TIMER, 4),              // 0x08 - four-byte value
561        (DynReg::GEN_PURPOSE_STATUS, 2), // 0x0C - two-byte value
562        (DynReg::GEN_PURPOSE_ENABLE, 2), // 0x0E - two-byte value
563        (DynReg::PROC_CONTROL, 4),       // 0x10 - four-byte value
564        (DynReg::PROC_L2, 1),            // 0x14 - one-byte value
565        (DynReg::PROC_L3, 1),            // 0x15 - one-byte value
566        (DynReg::GLOBAL_STATUS, 2),      // 0x18 - two-byte value
567        (DynReg::DEVICE_STATUS, 4),      // 0x1C - four-byte value
568        (DynReg::GLOBAL_ENABLE, 2),      // 0x20 - two-byte value
569        (DynReg::GLOBAL_CONTROL, 4),     // 0x28 - four-byte value
570        (DynReg::DEVICE_CONTROL, 4),     // 0x2C - four-byte value
571        (DynReg::GENERAL_INPUT1, 1),     // 0x30 - one-byte value (read only)
572        (DynReg::GENERAL_INPUT2, 1),     // 0x31 - one-byte value (read only)
573        (DynReg::GENERAL_INPUT3, 1),     // 0x32 - one-byte value (read only)
574        (DynReg::GENERAL_OUTPUT0, 1),    // 0x34 - one-byte value
575        (DynReg::GENERAL_OUTPUT2, 1),    // 0x35 - one-byte value
576        (DynReg::GENERAL_OUTPUT3, 1),    // 0x36 - one-byte value
577        (DynReg::GENERAL_OUTPUT4, 1),    // 0x37 - one-byte value
578        (DynReg::RESET, 1),              // 0x38 - one-byte value
579    ];
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
634/// Target for lines corresponding to bits in General Purpose Event Block 0.
635///
636/// For a full general description of this register, see the ACPI Spec. See
637/// section 4.7.1 in the ACPI 2.0 spec.
638impl LineInterruptTarget for PowerManagementDevice {
639    fn set_irq(&mut self, vector: u32, high: bool) {
640        // Latch the bit; it can only be cleared by the guest.
641        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}