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 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    /// Power management I/O offsets from base port address
49    pub enum DynReg: u8 {
50        #![expect(missing_docs)] // self explanatory constants
51        STATUS             = 0x00, // two-byte value
52        RESUME_ENABLE      = 0x02, // two-byte value
53        CONTROL            = 0x04, // two-byte value
54        TIMER              = 0x08, // four-byte value (read only)
55        GEN_PURPOSE_STATUS = 0x0C, // two-byte value
56        GEN_PURPOSE_ENABLE = 0x0E, // two-byte value
57        PROC_CONTROL       = 0x10, // four-byte value
58        PROC_L2            = 0x14, // one-byte value
59        PROC_L3            = 0x15, // one-byte value
60        GLOBAL_STATUS      = 0x18, // two-byte value
61        DEVICE_STATUS      = 0x1C, // four-byte value
62        GLOBAL_ENABLE      = 0x20, // two-byte value
63        GLOBAL_CONTROL     = 0x28, // four-byte value
64        DEVICE_CONTROL     = 0x2C, // four-byte value
65        GENERAL_INPUT1     = 0x30, // one-byte value (read only)
66        GENERAL_INPUT2     = 0x31, // one-byte value (read only)
67        GENERAL_INPUT3     = 0x32, // one-byte value (read only)
68        RESET              = 0x33, // one-byte value
69        GENERAL_OUTPUT0    = 0x34, // one-byte value
70        GENERAL_OUTPUT2    = 0x35, // one-byte value
71        GENERAL_OUTPUT3    = 0x36, // one-byte value
72        GENERAL_OUTPUT4    = 0x37, // one-byte value
73    }
74}
75
76const CONTROL_SCI_ENABLE_MASK: u16 = 0x0001; // Events should cause SCI =  not SMI
77const CONTROL_SUSPEND_ENABLE_MASK: u16 = 0x2000; // Enable the specified suspend type
78const CONTROL_SUSPEND_TYPE_MASK: u16 = 0x1C00; // Suspend type field
79const ENABLE_TIMER_OVERFLOW_MASK: u16 = 0x0001; // Timer overflow should interrupt
80const GLOBAL_CONTROL_BIOS_RLS_MASK: u32 = 0x00000002; // Generate SCI?
81const STATUS_DEVICE_MASK: u16 = 0x0010; // One device event flags is set
82const STATUS_GP_MASK: u16 = 0x0080; // One of the GP event flags is set
83const STATUS_PM_MASK: u16 = 0x0040; // One of the PM event flags is set
84const TIMER_OVERFLOW_MASK: u16 = 0x0001; // The PM timer overflowed
85
86/// Value that initiates a system reset when written to [`DynReg::RESET`].
87pub const RESET_VALUE: u8 = 0x01; // Reset the VM
88
89#[derive(Clone, Debug, Inspect)]
90struct PmState {
91    #[inspect(hex)]
92    general_purpose_output: u32,
93
94    // Power Management Dynamic I/O state
95    #[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            // 0x00 - two-byte value
140            // Indicate that no events have triggered a sticky flag.
141            DynReg::STATUS => self.status.into(),
142            // 0x02 - two-byte value
143            DynReg::RESUME_ENABLE => (self.resume_enable & 0x0521).into(),
144            // 0x04 - two-byte value
145            DynReg::CONTROL => self.control.into(),
146            // 0x08 - four-byte value (read only)
147            // If the pmtimer_assist is set then the hypervisor will intercept
148            // accesses to this port and return its own reference time.
149            // Hypervisor reference time is different from our reference time,
150            // but that's ok because nothing else needs to match. This is faster
151            // than us doing this work, but not always available.
152            DynReg::TIMER => {
153                let now = vmtime.now();
154                // Convert the 100ns-period VM time to the 3.579545MHz PM timer time.
155                (now.as_100ns() as u128 * 3_579_545 / 10_000_000) as u32
156            }
157            // 0x0C - two-byte value
158            DynReg::GEN_PURPOSE_STATUS => self.general_purpose_status.into(),
159            // 0x0E - two-byte value
160            DynReg::GEN_PURPOSE_ENABLE => self.general_purpose_enable.into(),
161            // 0x10 - four-byte value
162            DynReg::PROC_CONTROL => self.processor_control,
163            // 0x14 - one-byte value
164            DynReg::PROC_L2 => 0,
165            // 0x15 - one-byte value
166            DynReg::PROC_L3 => 0,
167            // 0x18 - two-byte value
168            DynReg::GLOBAL_STATUS => {
169                let mut value = self.global_status;
170
171                // Incorporate the summary status bits. It doesn't appear that
172                // the timer overflow status is paid attention to in this case.
173                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            // 0x1C - four-byte value
188            DynReg::DEVICE_STATUS => self.device_status,
189            // 0x20 - two-byte value
190            DynReg::GLOBAL_ENABLE => self.global_enable.into(),
191            // 0x28 - four-byte value
192            DynReg::GLOBAL_CONTROL => self.global_control,
193            // 0x2C - four-byte value
194            DynReg::DEVICE_CONTROL => self.device_control,
195            // 0x30 - one-byte value (read only)
196            DynReg::GENERAL_INPUT1 => 0,
197            // 0x31 - one-byte value (read only)
198            DynReg::GENERAL_INPUT2 => 0,
199            // 0x32 - one-byte value (read only)
200            DynReg::GENERAL_INPUT3 => 0,
201            // 0x34 - one-byte value
202            DynReg::GENERAL_OUTPUT0 => self.general_purpose_output,
203            // 0x35 - one-byte value
204            DynReg::GENERAL_OUTPUT2 => self.general_purpose_output >> 8,
205            // 0x36 - one-byte value
206            DynReg::GENERAL_OUTPUT3 => self.general_purpose_output >> 16,
207            // 0x37 - one-byte value
208            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            // 0x00 - two-byte value
219            DynReg::STATUS => self.status &= !value as u16,
220            // 0x02 - two-byte value
221            DynReg::RESUME_ENABLE => {
222                // 0x0521 represents the bits that are not marked as reserved in the PIIX4 manual.
223                self.resume_enable &= !mask as u16;
224                self.resume_enable |= value as u16 & 0x0521;
225            }
226            // 0x04 - two-byte value
227            DynReg::CONTROL => {
228                let value = value as u16;
229                if (value & CONTROL_SUSPEND_ENABLE_MASK) != 0 {
230                    // Get the suspend type, which is Bits[12:10] of the control register.
231                    // Our platform defines a suspend type of 0 as power off(S5) and a suspend
232                    // type of 1 as hibernate(S4); no other types are supported.The BIOS/firmware
233                    // ACPI tables must reflect these values to the guest.
234                    //
235                    // Any other values will be ignored.
236                    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                // Ignore writes.
249            }
250            // 0x0C - two-byte value
251            DynReg::GEN_PURPOSE_STATUS => {
252                self.general_purpose_status &= !value as u16;
253            }
254            // 0x0E - two-byte value
255            DynReg::GEN_PURPOSE_ENABLE => {
256                self.general_purpose_enable &= !mask as u16;
257                self.general_purpose_enable |= value as u16 & 0x0f01;
258            }
259            // 0x10 - four-byte value
260            DynReg::PROC_CONTROL => {
261                self.processor_control &= !mask;
262                self.processor_control |= value & 0x00023E1E;
263            }
264            // 0x14 - one-byte value
265            DynReg::PROC_L2 => {} // Writes to this address do nothing.
266            // 0x15 - one-byte value
267            DynReg::PROC_L3 => {} // Writes to this address do nothing.
268            // 0x18 - two-byte value
269            DynReg::GLOBAL_STATUS => {
270                // Writes of 1 clear the corresponding status bits. Some of
271                // these bits can only be cleared when other registers are
272                // cleared (i.e. they are "summary" status bits for other registers.
273                self.global_status &= !(value & 0x0D25) as u16;
274            }
275
276            // 0x1C - four-byte value
277            DynReg::DEVICE_STATUS => self.device_status = !value,
278            // 0x20 - two-byte value
279            DynReg::GLOBAL_ENABLE => {
280                self.global_enable &= !mask as u16;
281                self.global_enable |= (value & 0x8D13) as u16;
282            }
283            // 0x28 - four-byte value
284            DynReg::GLOBAL_CONTROL => {
285                // We don't support the BIOS release bit.
286                let value = value & !GLOBAL_CONTROL_BIOS_RLS_MASK;
287                self.global_control &= !mask;
288                self.global_control |= value & 0x0701FFE7;
289            }
290            // 0x2C - four-byte value
291            DynReg::DEVICE_CONTROL => {
292                self.device_control &= !mask;
293                self.device_control |= value;
294            }
295            // 0x33 - one-byte value
296            DynReg::RESET => {
297                if value as u8 == RESET_VALUE {
298                    (action)(PowerAction::Reboot);
299                }
300            }
301            // 0x34 - one-byte value
302            DynReg::GENERAL_OUTPUT0 => {
303                let mask = mask & 0xffff;
304                self.general_purpose_output &= !mask;
305                self.general_purpose_output |= value & mask;
306            }
307            // 0x35 - one-byte value
308            DynReg::GENERAL_OUTPUT2 => {
309                self.general_purpose_output &= !0xFF00;
310                self.general_purpose_output |= (value << 8) & 0xFF00;
311            }
312            // 0x36 - one-byte value
313            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            // 0x37 - one-byte value
319            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/// Power action being requested
329#[expect(missing_docs)] // self explanatory variants
330#[derive(Debug, Copy, Clone)]
331pub enum PowerAction {
332    PowerOff,
333    Hibernate,
334    Reboot,
335}
336
337/// Callback invoked whenever a power action is requested
338pub type PowerActionFn = Box<dyn FnMut(PowerAction) + Send + Sync>;
339
340#[derive(Inspect)]
341struct PowerManagementDeviceRt {
342    /// 0x37-byte IO port corresponding to the dynamic register range
343    pio_dynamic: Box<dyn ControlPortIoIntercept>,
344    /// ACPI interrupt line
345    acpi_interrupt: LineInterrupt,
346    /// VM time access for the PM timer.
347    vmtime: VmTimeAccess,
348    /// Callback invoked whenever a power action is requested
349    #[inspect(skip)]
350    action: PowerActionFn,
351    /// Enable / Disable hypervisor PM timer assist (when available)
352    #[inspect(skip)]
353    pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
354}
355
356/// This is used when running the UEFI BIOS. When passed via
357/// `PowerManagementDevice::new`, the device will pre-populate various register
358/// values + automatically map the dynamic memory regions at the specified port
359/// io address.
360///
361/// NOTE: at some point, this device should be refactored into a "generic" power
362/// management device, which will do away with all the PIIX4 cruft that
363/// necessitates this extra "enable ACPI mode" call.
364#[derive(Debug, Clone, Copy)]
365pub struct EnableAcpiMode {
366    /// Default base address for the dynamic register region.
367    pub default_pio_dynamic: u16,
368}
369
370// Re-export the PmTimerAssist trait from chipset_resources.
371pub use chipset_resources::pm::PmTimerAssist;
372
373/// A power management + ACPI device.
374///
375/// See the module level docs for more details.
376#[derive(InspectMut)]
377pub struct PowerManagementDevice {
378    // Static configuration
379    #[inspect(skip)]
380    enable_acpi_mode: Option<EnableAcpiMode>,
381
382    // Runtime glue
383    #[inspect(flatten)]
384    rt: PowerManagementDeviceRt,
385
386    // Volatile state
387    #[inspect(flatten)]
388    state: PmState,
389}
390
391impl PowerManagementDevice {
392    /// Create a new [`PowerManagementDevice`].
393    ///
394    /// Most arguments to this constructor are self describing, though there are
395    /// some that merit additional explanation:
396    ///
397    /// - `action`: a callback invoked whenever the PM initiates a power event
398    /// - `pio_control` and `pio_status`: define where in the port IO space the
399    ///   control/status registers get mapped to.
400    /// - `enable_acpi_mode`: see the docs for [`EnableAcpiMode`]
401    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        // ensure timer assist is disabled
424        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    /// (used by the PIIX4 wrapper device)
442    // DEVNOTE: also used internally, but the PIIX4 wrapper device also uses it
443    ///
444    /// Evaluates whether the power management (ACPI) interrupt should be
445    /// asserted or de-asserted
446    ///
447    /// If the state is out of sync with what it should be, this function will
448    /// either assert or de-assert the interrupt. The logic for whether an ACPI
449    /// interrupt should be sent is covered in various parts of Chapter 4 of any
450    /// version of the ACPI spec.
451    ///
452    /// reSearch query: `CheckInterruptAssertion`
453    pub fn check_interrupt_assertion(&self) {
454        // Check if any power events should cause an interrupt to be asserted.
455        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    /// (used by the PIIX4 wrapper device)
462    ///
463    /// Remap dynamic registers based on config in the PCI config space
464    #[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    /// (used by the PIIX4 wrapper device)
483    ///
484    /// See calling code in piix4_pm.rs for details on what this does.
485    #[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    /// (used by the PIIX4 wrapper device)
497    ///
498    /// Get a mutable reference to the provided [`PowerActionFn`]
499    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),             // 0x00 - two-byte value
532        (DynReg::RESUME_ENABLE, 2),      // 0x02 - two-byte value
533        (DynReg::CONTROL, 2),            // 0x04 - two-byte value
534        (DynReg::TIMER, 4),              // 0x08 - four-byte value
535        (DynReg::GEN_PURPOSE_STATUS, 2), // 0x0C - two-byte value
536        (DynReg::GEN_PURPOSE_ENABLE, 2), // 0x0E - two-byte value
537        (DynReg::PROC_CONTROL, 4),       // 0x10 - four-byte value
538        (DynReg::PROC_L2, 1),            // 0x14 - one-byte value
539        (DynReg::PROC_L3, 1),            // 0x15 - one-byte value
540        (DynReg::GLOBAL_STATUS, 2),      // 0x18 - two-byte value
541        (DynReg::DEVICE_STATUS, 4),      // 0x1C - four-byte value
542        (DynReg::GLOBAL_ENABLE, 2),      // 0x20 - two-byte value
543        (DynReg::GLOBAL_CONTROL, 4),     // 0x28 - four-byte value
544        (DynReg::DEVICE_CONTROL, 4),     // 0x2C - four-byte value
545        (DynReg::GENERAL_INPUT1, 1),     // 0x30 - one-byte value (read only)
546        (DynReg::GENERAL_INPUT2, 1),     // 0x31 - one-byte value (read only)
547        (DynReg::GENERAL_INPUT3, 1),     // 0x32 - one-byte value (read only)
548        (DynReg::GENERAL_OUTPUT0, 1),    // 0x34 - one-byte value
549        (DynReg::GENERAL_OUTPUT2, 1),    // 0x35 - one-byte value
550        (DynReg::GENERAL_OUTPUT3, 1),    // 0x36 - one-byte value
551        (DynReg::GENERAL_OUTPUT4, 1),    // 0x37 - one-byte value
552        (DynReg::RESET, 1),              // 0x38 - one-byte value
553    ];
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
608/// Target for lines corresponding to bits in General Purpose Event Block 0.
609///
610/// For a full general description of this register, see the ACPI Spec. See
611/// section 4.7.1 in the ACPI 2.0 spec.
612impl LineInterruptTarget for PowerManagementDevice {
613    fn set_irq(&mut self, vector: u32, high: bool) {
614        // Latch the bit; it can only be cleared by the guest.
615        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}