1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Power Management Device (as found on the PIIX4 chipset - kinda)
//!
//! # What's with all this PIIX4 stuff?
//!
//! The current implementation of [`PowerManagementDevice`] is based off code in
//! Hyper-V, which happens to emulate the _specific_ PM device as found on the
//! PIIX4 chipset.
//!
//! ...well, kinda.
//!
//! This current implementation is only a _partial_ port of the PM device found
//! on the PIIX4 chipset, with a good chunk of the PIIX4 functionality having
//! been lifted into a wrapper device found under `chipset_legacy/piix4_pm.rs`.
//!
//! # So, what's next?
//!
//! Eventually, this device should be swapped out for a minimal "generic" PM /
//! ACPI device, with all remaining PIIX4 specific functionality being lifted
//! into the legacy piix4_pm device (which may or may not end up reusing the
//! generic PM device as part of its implementation).
//!
//! Of course, there's always the tricky issue that the current implementation
//! _works fine_, so when this work is going to happen... well, your guess is as
//! good as mine.

#![warn(missing_docs)]

use chipset_device::interrupt::LineInterruptTarget;
use chipset_device::io::IoError;
use chipset_device::io::IoResult;
use chipset_device::pio::ControlPortIoIntercept;
use chipset_device::pio::PortIoIntercept;
use chipset_device::pio::RegisterPortIoIntercept;
use chipset_device::ChipsetDevice;
use inspect::Inspect;
use inspect::InspectMut;
use open_enum::open_enum;
use vmcore::device_state::ChangeDeviceState;
use vmcore::line_interrupt::LineInterrupt;
use vmcore::vmtime::VmTimeAccess;

open_enum! {
    /// Power management I/O offsets from base port address
    pub enum DynReg: u8 {
        #![allow(missing_docs)] // self explanatory constants
        STATUS             = 0x00, // two-byte value
        RESUME_ENABLE      = 0x02, // two-byte value
        CONTROL            = 0x04, // two-byte value
        TIMER              = 0x08, // four-byte value (read only)
        GEN_PURPOSE_STATUS = 0x0C, // two-byte value
        GEN_PURPOSE_ENABLE = 0x0E, // two-byte value
        PROC_CONTROL       = 0x10, // four-byte value
        PROC_L2            = 0x14, // one-byte value
        PROC_L3            = 0x15, // one-byte value
        GLOBAL_STATUS      = 0x18, // two-byte value
        DEVICE_STATUS      = 0x1C, // four-byte value
        GLOBAL_ENABLE      = 0x20, // two-byte value
        GLOBAL_CONTROL     = 0x28, // four-byte value
        DEVICE_CONTROL     = 0x2C, // four-byte value
        GENERAL_INPUT1     = 0x30, // one-byte value (read only)
        GENERAL_INPUT2     = 0x31, // one-byte value (read only)
        GENERAL_INPUT3     = 0x32, // one-byte value (read only)
        RESET              = 0x33, // one-byte value
        GENERAL_OUTPUT0    = 0x34, // one-byte value
        GENERAL_OUTPUT2    = 0x35, // one-byte value
        GENERAL_OUTPUT3    = 0x36, // one-byte value
        GENERAL_OUTPUT4    = 0x37, // one-byte value
    }
}

const CONTROL_SCI_ENABLE_MASK: u16 = 0x0001; // Events should cause SCI =  not SMI
const CONTROL_SUSPEND_ENABLE_MASK: u16 = 0x2000; // Enable the specified suspend type
const CONTROL_SUSPEND_TYPE_MASK: u16 = 0x1C00; // Suspend type field
const ENABLE_TIMER_OVERFLOW_MASK: u16 = 0x0001; // Timer overflow should interrupt
const GLOBAL_CONTROL_BIOS_RLS_MASK: u32 = 0x00000002; // Generate SCI?
const STATUS_DEVICE_MASK: u16 = 0x0010; // One device event flags is set
const STATUS_GP_MASK: u16 = 0x0080; // One of the GP event flags is set
const STATUS_PM_MASK: u16 = 0x0040; // One of the PM event flags is set
const TIMER_OVERFLOW_MASK: u16 = 0x0001; // The PM timer overflowed

/// Value that initiates a system reset when written to [`DynReg::RESET`].
pub const RESET_VALUE: u8 = 0x01; // Reset the VM

#[derive(Clone, Debug, Inspect)]
struct PmState {
    #[inspect(hex)]
    general_purpose_output: u32,

    // Power Management Dynamic I/O state
    #[inspect(hex)]
    status: u16,
    #[inspect(hex)]
    resume_enable: u16,
    #[inspect(hex)]
    control: u16,
    #[inspect(hex)]
    general_purpose_status: u16,
    #[inspect(hex)]
    general_purpose_enable: u16,
    #[inspect(hex)]
    processor_control: u32,
    #[inspect(hex)]
    device_status: u32,
    #[inspect(hex)]
    global_status: u16,
    #[inspect(hex)]
    global_enable: u16,
    #[inspect(hex)]
    global_control: u32,
    #[inspect(hex)]
    device_control: u32,
}

impl PmState {
    fn new() -> Self {
        Self {
            general_purpose_output: 0x7FFFBFFF,
            status: 0,
            resume_enable: 0,
            control: 0,
            general_purpose_status: 0,
            general_purpose_enable: 0,
            processor_control: 0,
            device_status: 0,
            global_status: 0,
            global_enable: 0,
            global_control: 0,
            device_control: 0,
        }
    }

    fn read_dynamic(&mut self, vmtime: &VmTimeAccess, offset: u8) -> u32 {
        match DynReg(offset) {
            // 0x00 - two-byte value
            // Indicate that no events have triggered a sticky flag.
            DynReg::STATUS => self.status.into(),
            // 0x02 - two-byte value
            DynReg::RESUME_ENABLE => (self.resume_enable & 0x0521).into(),
            // 0x04 - two-byte value
            DynReg::CONTROL => self.control.into(),
            // 0x08 - four-byte value (read only)
            // If the pmtimer_assist is set then the hypervisor will intercept
            // accesses to this port and return its own reference time.
            // Hypervisor reference time is different from our reference time,
            // but that's ok because nothing else needs to match. This is faster
            // than us doing this work, but not always available.
            DynReg::TIMER => {
                let now = vmtime.now();
                // Convert the 100ns-period VM time to the 3.579545MHz PM timer time.
                (now.as_100ns() as u128 * 3_579_545 / 10_000_000) as u32
            }
            // 0x0C - two-byte value
            DynReg::GEN_PURPOSE_STATUS => self.general_purpose_status.into(),
            // 0x0E - two-byte value
            DynReg::GEN_PURPOSE_ENABLE => self.general_purpose_enable.into(),
            // 0x10 - four-byte value
            DynReg::PROC_CONTROL => self.processor_control,
            // 0x14 - one-byte value
            DynReg::PROC_L2 => 0,
            // 0x15 - one-byte value
            DynReg::PROC_L3 => 0,
            // 0x18 - two-byte value
            DynReg::GLOBAL_STATUS => {
                let mut value = self.global_status;

                // Incorporate the summary status bits. It doesn't appear that
                // the timer overflow status is paid attention to in this case.
                if (self.status & !TIMER_OVERFLOW_MASK) != 0 {
                    value |= STATUS_PM_MASK;
                }

                if self.general_purpose_status != 0 {
                    value |= STATUS_GP_MASK;
                }

                if self.device_status != 0 {
                    value |= STATUS_DEVICE_MASK;
                }

                value.into()
            }
            // 0x1C - four-byte value
            DynReg::DEVICE_STATUS => self.device_status,
            // 0x20 - two-byte value
            DynReg::GLOBAL_ENABLE => self.global_enable.into(),
            // 0x28 - four-byte value
            DynReg::GLOBAL_CONTROL => self.global_control,
            // 0x2C - four-byte value
            DynReg::DEVICE_CONTROL => self.device_control,
            // 0x30 - one-byte value (read only)
            DynReg::GENERAL_INPUT1 => 0,
            // 0x31 - one-byte value (read only)
            DynReg::GENERAL_INPUT2 => 0,
            // 0x32 - one-byte value (read only)
            DynReg::GENERAL_INPUT3 => 0,
            // 0x34 - one-byte value
            DynReg::GENERAL_OUTPUT0 => self.general_purpose_output,
            // 0x35 - one-byte value
            DynReg::GENERAL_OUTPUT2 => self.general_purpose_output >> 8,
            // 0x36 - one-byte value
            DynReg::GENERAL_OUTPUT3 => self.general_purpose_output >> 16,
            // 0x37 - one-byte value
            DynReg::GENERAL_OUTPUT4 => self.general_purpose_output >> 24,
            _ => {
                tracelimit::warn_ratelimited!(?offset, "unhandled register read");
                !0
            }
        }
    }

    fn write_dynamic(&mut self, action: &mut PowerActionFn, offset: u8, value: u32, mask: u32) {
        match DynReg(offset) {
            // 0x00 - two-byte value
            DynReg::STATUS => self.status &= !value as u16,
            // 0x02 - two-byte value
            DynReg::RESUME_ENABLE => {
                // 0x0521 represents the bits that are not marked as reserved in the PIIX4 manual.
                self.resume_enable &= !mask as u16;
                self.resume_enable |= value as u16 & 0x0521;
            }
            // 0x04 - two-byte value
            DynReg::CONTROL => {
                let value = value as u16;
                if (value & CONTROL_SUSPEND_ENABLE_MASK) != 0 {
                    // Get the suspend type, which is Bits[12:10] of the control register.
                    // Our platform defines a suspend type of 0 as power off(S5) and a suspend
                    // type of 1 as hibernate(S4); no other types are supported.The BIOS/firmware
                    // ACPI tables must reflect these values to the guest.
                    //
                    // Any other values will be ignored.
                    let suspend_type = (value & CONTROL_SUSPEND_TYPE_MASK) >> 10;
                    match suspend_type {
                        0 => (action)(PowerAction::PowerOff),
                        1 => (action)(PowerAction::Hibernate),
                        _ => {}
                    }
                }

                self.control &= !mask as u16;
                self.control |= value;
            }
            DynReg::TIMER => {
                // Ignore writes.
            }
            // 0x0C - two-byte value
            DynReg::GEN_PURPOSE_STATUS => {
                self.general_purpose_status &= !value as u16;
            }
            // 0x0E - two-byte value
            DynReg::GEN_PURPOSE_ENABLE => {
                self.general_purpose_enable &= !mask as u16;
                self.general_purpose_enable |= value as u16 & 0x0f01;
            }
            // 0x10 - four-byte value
            DynReg::PROC_CONTROL => {
                self.processor_control &= !mask;
                self.processor_control |= value & 0x00023E1E;
            }
            // 0x14 - one-byte value
            DynReg::PROC_L2 => {} // Writes to this address do nothing.
            // 0x15 - one-byte value
            DynReg::PROC_L3 => {} // Writes to this address do nothing.
            // 0x18 - two-byte value
            DynReg::GLOBAL_STATUS => {
                // Writes of 1 clear the corresponding status bits. Some of
                // these bits can only be cleared when other registers are
                // cleared (i.e. they are "summary" status bits for other registers.
                self.global_status &= !(value & 0x0D25) as u16;
            }

            // 0x1C - four-byte value
            DynReg::DEVICE_STATUS => self.device_status = !value,
            // 0x20 - two-byte value
            DynReg::GLOBAL_ENABLE => {
                self.global_enable &= !mask as u16;
                self.global_enable |= (value & 0x8D13) as u16;
            }
            // 0x28 - four-byte value
            DynReg::GLOBAL_CONTROL => {
                // We don't support the BIOS release bit.
                let value = value & !GLOBAL_CONTROL_BIOS_RLS_MASK;
                self.global_control &= !mask;
                self.global_control |= value & 0x0701FFE7;
            }
            // 0x2C - four-byte value
            DynReg::DEVICE_CONTROL => {
                self.device_control &= !mask;
                self.device_control |= value;
            }
            // 0x33 - one-byte value
            DynReg::RESET => {
                if value as u8 == RESET_VALUE {
                    (action)(PowerAction::Reboot);
                }
            }
            // 0x34 - one-byte value
            DynReg::GENERAL_OUTPUT0 => {
                let mask = mask & 0xffff;
                self.general_purpose_output &= !mask;
                self.general_purpose_output |= value & mask;
            }
            // 0x35 - one-byte value
            DynReg::GENERAL_OUTPUT2 => {
                self.general_purpose_output &= !0xFF00;
                self.general_purpose_output |= (value << 8) & 0xFF00;
            }
            // 0x36 - one-byte value
            DynReg::GENERAL_OUTPUT3 => {
                let mask = mask & 0xffff;
                self.general_purpose_output &= !(mask << 16);
                self.general_purpose_output |= (value << 16) & mask;
            }
            // 0x37 - one-byte value
            DynReg::GENERAL_OUTPUT4 => {
                self.general_purpose_output &= !0xFF000000;
                self.general_purpose_output |= (value << 24) & 0x7F000000;
            }
            _ => tracelimit::warn_ratelimited!(?offset, ?value, "unhandled register write"),
        }
    }
}

/// Power action being requested
#[allow(missing_docs)] // self explanatory variants
#[derive(Debug, Copy, Clone)]
pub enum PowerAction {
    PowerOff,
    Hibernate,
    Reboot,
}

/// Callback invoked whenever a power action is requested
pub type PowerActionFn = Box<dyn FnMut(PowerAction) + Send + Sync>;

#[derive(Inspect)]
struct PowerManagementDeviceRt {
    /// 0x37-byte IO port corresponding to the dynamic register range
    pio_dynamic: Box<dyn ControlPortIoIntercept>,
    /// ACPI interrupt line
    acpi_interrupt: LineInterrupt,
    /// VM time access for the PM timer.
    vmtime: VmTimeAccess,
    /// Callback invoked whenever a power action is requested
    #[inspect(skip)]
    action: PowerActionFn,
    /// Enable / Disable hypervisor PM timer assist (when available)
    #[inspect(skip)]
    pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
}

/// This is used when running the UEFI BIOS. When passed via
/// `PowerManagementDevice::new`, the device will pre-populate various register
/// values + automatically map the dynamic memory regions at the specified port
/// io address.
///
/// NOTE: at some point, this device should be refactored into a "generic" power
/// management device, which will do away with all the PIIX4 cruft that
/// necessitates this extra "enable ACPI mode" call.
#[derive(Debug, Clone, Copy)]
pub struct EnableAcpiMode {
    /// Default base address for the dynamic register region.
    pub default_pio_dynamic: u16,
}

/// Interface to enable/disable hypervisor PM timer assist.
pub trait PmTimerAssist: Send + Sync {
    /// Sets the port of the PM timer assist.
    fn set(&self, port: Option<u16>);
}

/// A power management + ACPI device.
///
/// See the module level docs for more details.
#[derive(InspectMut)]
pub struct PowerManagementDevice {
    // Static configuration
    #[inspect(skip)]
    enable_acpi_mode: Option<EnableAcpiMode>,

    // Runtime glue
    #[inspect(flatten)]
    rt: PowerManagementDeviceRt,

    // Volatile state
    #[inspect(flatten)]
    state: PmState,
}

impl PowerManagementDevice {
    /// Create a new [`PowerManagementDevice`].
    ///
    /// Most arguments to this constructor are self describing, though there are
    /// some that merit additional explanation:
    ///
    /// - `action`: a callback invoked whenever the PM initiates a power event
    /// - `pio_control` and `pio_status`: define where in the port IO space the
    ///   control/status registers get mapped to.
    /// - `enable_acpi_mode`: see the docs for [`EnableAcpiMode`]
    pub fn new(
        action: PowerActionFn,
        acpi_interrupt: LineInterrupt,
        register_pio: &mut dyn RegisterPortIoIntercept,
        vmtime: VmTimeAccess,
        enable_acpi_mode: Option<EnableAcpiMode>,
        pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
    ) -> Self {
        let pio_dynamic = register_pio.new_io_region("dynamic", 0x37);

        let mut this = PowerManagementDevice {
            enable_acpi_mode,
            rt: PowerManagementDeviceRt {
                pio_dynamic,
                action,
                acpi_interrupt,
                vmtime,
                pm_timer_assist,
            },
            state: PmState::new(),
        };

        // ensure timer assist is disabled
        if let Some(pm_timer_assist) = &this.rt.pm_timer_assist {
            pm_timer_assist.set(None)
        }

        if let Some(acpi_mode) = enable_acpi_mode {
            this.enable_acpi_mode(acpi_mode.default_pio_dynamic)
        }

        this
    }

    fn enable_acpi_mode(&mut self, default_pio_dynamic: u16) {
        tracing::debug!("ACPI mode enabled");
        self.rt.pio_dynamic.map(default_pio_dynamic);
        self.state.control = CONTROL_SCI_ENABLE_MASK;
    }

    /// (used by the PIIX4 wrapper device)
    // DEVNOTE: also used internally, but the PIIX4 wrapper device also uses it
    ///
    /// Evaluates whether the power management (ACPI) interrupt should be
    /// asserted or de-asserted
    ///
    /// If the state is out of sync with what it should be, this function will
    /// either assert or de-assert the interrupt. The logic for whether an ACPI
    /// interrupt should be sent is covered in various parts of Chapter 4 of any
    /// version of the ACPI spec.
    ///
    /// reSearch query: `CheckInterruptAssertion`
    pub fn check_interrupt_assertion(&self) {
        // Check if any power events should cause an interrupt to be asserted.
        let level = (self.state.resume_enable > 0 && self.state.status > 0)
            || (self.state.general_purpose_status > 0 && self.state.general_purpose_enable > 0);

        self.rt.acpi_interrupt.set_level(level)
    }

    /// (used by the PIIX4 wrapper device)
    ///
    /// Remap dynamic registers based on config in the PCI config space
    #[inline(always)]
    pub fn update_dynamic_pio_mappings(&mut self, pio_dynamic_addr: Option<u16>) {
        match pio_dynamic_addr {
            Some(addr) => {
                self.rt.pio_dynamic.map(addr);
                if let Some(assist) = &self.rt.pm_timer_assist {
                    assist.set(Some(addr + DynReg::TIMER.0 as u16))
                }
            }
            None => {
                self.rt.pio_dynamic.unmap();
                if let Some(assist) = &self.rt.pm_timer_assist {
                    assist.set(None);
                }
            }
        }
    }

    /// (used by the PIIX4 wrapper device)
    ///
    /// See calling code in piix4_pm.rs for details on what this does.
    #[inline(always)]
    pub fn pcat_facp_acpi_enable(&mut self, enable: bool) {
        if enable {
            self.state.control |= CONTROL_SCI_ENABLE_MASK;
            self.state.resume_enable |= ENABLE_TIMER_OVERFLOW_MASK;
        } else {
            self.state.control &= !CONTROL_SCI_ENABLE_MASK;
            self.state.resume_enable &= !ENABLE_TIMER_OVERFLOW_MASK;
        }
    }

    /// (used by the PIIX4 wrapper device)
    ///
    /// Get a mutable reference to the provided [`PowerActionFn`]
    pub fn power_action(&mut self) -> &mut PowerActionFn {
        &mut self.rt.action
    }
}

impl ChangeDeviceState for PowerManagementDevice {
    fn start(&mut self) {}

    async fn stop(&mut self) {}

    async fn reset(&mut self) {
        self.rt.pio_dynamic.unmap();
        self.rt.acpi_interrupt.set_level(false);
        self.state = PmState::new();
        if let Some(acpi_mode) = self.enable_acpi_mode {
            self.enable_acpi_mode(acpi_mode.default_pio_dynamic)
        }
    }
}

impl ChipsetDevice for PowerManagementDevice {
    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
        Some(self)
    }

    fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
        Some(self)
    }
}

fn aligned_offset(offset: u8) -> Option<u8> {
    const TABLE: &[(DynReg, u8)] = &[
        (DynReg::STATUS, 2),             // 0x00 - two-byte value
        (DynReg::RESUME_ENABLE, 2),      // 0x02 - two-byte value
        (DynReg::CONTROL, 2),            // 0x04 - two-byte value
        (DynReg::TIMER, 4),              // 0x08 - four-byte value
        (DynReg::GEN_PURPOSE_STATUS, 2), // 0x0C - two-byte value
        (DynReg::GEN_PURPOSE_ENABLE, 2), // 0x0E - two-byte value
        (DynReg::PROC_CONTROL, 4),       // 0x10 - four-byte value
        (DynReg::PROC_L2, 1),            // 0x14 - one-byte value
        (DynReg::PROC_L3, 1),            // 0x15 - one-byte value
        (DynReg::GLOBAL_STATUS, 2),      // 0x18 - two-byte value
        (DynReg::DEVICE_STATUS, 4),      // 0x1C - four-byte value
        (DynReg::GLOBAL_ENABLE, 2),      // 0x20 - two-byte value
        (DynReg::GLOBAL_CONTROL, 4),     // 0x28 - four-byte value
        (DynReg::DEVICE_CONTROL, 4),     // 0x2C - four-byte value
        (DynReg::GENERAL_INPUT1, 1),     // 0x30 - one-byte value (read only)
        (DynReg::GENERAL_INPUT2, 1),     // 0x31 - one-byte value (read only)
        (DynReg::GENERAL_INPUT3, 1),     // 0x32 - one-byte value (read only)
        (DynReg::GENERAL_OUTPUT0, 1),    // 0x34 - one-byte value
        (DynReg::GENERAL_OUTPUT2, 1),    // 0x35 - one-byte value
        (DynReg::GENERAL_OUTPUT3, 1),    // 0x36 - one-byte value
        (DynReg::GENERAL_OUTPUT4, 1),    // 0x37 - one-byte value
        (DynReg::RESET, 1),              // 0x38 - one-byte value
    ];

    for (start, len) in TABLE.iter().copied() {
        if offset >= start.0 && offset < start.0 + len {
            return Some(start.0);
        }
    }
    None
}

impl PortIoIntercept for PowerManagementDevice {
    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
        if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
            let offset = offset as u8;
            let value = if let Some(aligned_offset) = aligned_offset(offset) {
                let value: u64 = self
                    .state
                    .read_dynamic(&self.rt.vmtime, aligned_offset)
                    .into();
                value >> ((offset - aligned_offset) * 8)
            } else {
                tracelimit::warn_ratelimited!(offset, "unknown read from relative offset");
                0
            };

            data.copy_from_slice(&value.to_ne_bytes()[..data.len()]);
            return IoResult::Ok;
        }

        IoResult::Err(IoError::InvalidRegister)
    }

    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
        if let Some(offset) = self.rt.pio_dynamic.offset_of(io_port) {
            let offset = offset as u8;
            let mut value = [0; 8];
            value[..data.len()].copy_from_slice(data);
            let value = u32::from_ne_bytes(value[..4].try_into().unwrap());
            if let Some(aligned_offset) = aligned_offset(offset) {
                let mask = !0u64 >> ((8 - data.len()) * 8) << ((offset - aligned_offset) * 8);
                let value = value << ((offset - aligned_offset) * 8);
                self.state
                    .write_dynamic(&mut self.rt.action, aligned_offset, value, mask as u32)
            } else {
                tracelimit::warn_ratelimited!(offset, value, "unknown write to relative offset");
            }

            self.check_interrupt_assertion();
            return IoResult::Ok;
        }

        IoResult::Err(IoError::InvalidRegister)
    }
}

/// Target for lines corresponding to bits in General Purpose Event Block 0.
///
/// For a full general description of this register, see the ACPI Spec. See
/// section 4.7.1 in the ACPI 2.0 spec.
impl LineInterruptTarget for PowerManagementDevice {
    fn set_irq(&mut self, vector: u32, high: bool) {
        // Latch the bit; it can only be cleared by the guest.
        self.state.general_purpose_status |= (high as u16) << vector;
        self.check_interrupt_assertion();
    }

    fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
        &[0..=15]
    }
}

mod saved_state {
    use super::*;
    use vmcore::save_restore::RestoreError;
    use vmcore::save_restore::SaveError;
    use vmcore::save_restore::SaveRestore;

    mod state {
        use mesh::payload::Protobuf;
        use vmcore::save_restore::SavedStateRoot;

        #[derive(Protobuf, SavedStateRoot)]
        #[mesh(package = "chipset.pm")]
        pub struct SavedState {
            #[mesh(1)]
            pub general_purpose_output: u32,
            #[mesh(2)]
            pub status: u16,
            #[mesh(3)]
            pub resume_enable: u16,
            #[mesh(4)]
            pub control: u16,
            #[mesh(5)]
            pub general_purpose_status: u16,
            #[mesh(6)]
            pub general_purpose_enable: u16,
            #[mesh(7)]
            pub processor_control: u32,
            #[mesh(8)]
            pub device_status: u32,
            #[mesh(9)]
            pub global_status: u16,
            #[mesh(10)]
            pub global_enable: u16,
            #[mesh(11)]
            pub global_control: u32,
            #[mesh(12)]
            pub device_control: u32,
        }
    }

    impl SaveRestore for PowerManagementDevice {
        type SavedState = state::SavedState;

        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
            let PmState {
                general_purpose_output,
                status,
                resume_enable,
                control,
                general_purpose_status,
                general_purpose_enable,
                processor_control,
                device_status,
                global_status,
                global_enable,
                global_control,
                device_control,
            } = self.state;

            let saved_state = state::SavedState {
                general_purpose_output,
                status,
                resume_enable,
                control,
                general_purpose_status,
                general_purpose_enable,
                processor_control,
                device_status,
                global_status,
                global_enable,
                global_control,
                device_control,
            };

            Ok(saved_state)
        }

        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
            let state::SavedState {
                general_purpose_output,
                status,
                resume_enable,
                control,
                general_purpose_status,
                general_purpose_enable,
                processor_control,
                device_status,
                global_status,
                global_enable,
                global_control,
                device_control,
            } = state;

            self.state = PmState {
                general_purpose_output,
                status,
                resume_enable,
                control,
                general_purpose_status,
                general_purpose_enable,
                processor_control,
                device_status,
                global_status,
                global_enable,
                global_control,
                device_control,
            };

            self.check_interrupt_assertion();

            Ok(())
        }
    }
}