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(())
}
}
}