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