Skip to content

opentmk: acpi shutdown to stop the testvm#3255

Draft
mayank-microsoft wants to merge 2 commits intomicrosoft:mainfrom
mayank-microsoft:mayank/acpi-shutdown
Draft

opentmk: acpi shutdown to stop the testvm#3255
mayank-microsoft wants to merge 2 commits intomicrosoft:mainfrom
mayank-microsoft:mayank/acpi-shutdown

Conversation

@mayank-microsoft
Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings April 11, 2026 17:29
@github-actions github-actions bot added the unsafe Related to unsafe code label Apr 11, 2026
@github-actions
Copy link
Copy Markdown

⚠️ Unsafe Code Detected

This PR modifies files containing unsafe Rust code. Extra scrutiny is required during review.

For more on why we check whole files, instead of just diffs, check out the Rustonomicon

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an ACPI-based shutdown path to OpenTMK’s UEFI runtime so test VMs can power off cleanly after completion/panic, and introduces a local Flowey pipeline to build/package OpenTMK into a bootable VHD for convenience.

Changes:

  • Implement ACPI S5 shutdown for OpenTMK UEFI (including FADT parsing + runtime shutdown writes).
  • Refactor ACPI-related errors into a typed AcpiError carried by TmkError::AcpiError.
  • Add local-only Flowey plumbing (build-opentmk) to build OpenTMK and package it into a VHD.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
opentmk/src/uefi/rt.rs Attempt ACPI shutdown on panic instead of spinning forever.
opentmk/src/uefi/mod.rs Attempt ACPI shutdown after test completion.
opentmk/src/uefi/acpi_wrap.rs Extend ACPI context to locate FADT and derive a shutdown mechanism.
opentmk/src/tmkdefs.rs Introduce AcpiError and wrap it in TmkError::AcpiError(AcpiError).
opentmk/src/devices/shutdown.rs New module implementing legacy + HW-reduced ACPI S5 shutdown writes.
opentmk/src/devices/mod.rs Export the new shutdown device module.
opentmk/src/arch/x86_64/mod.rs Make the io module public for reuse by shutdown code.
opentmk/src/arch/x86_64/io.rs Add word I/O helpers and improve docs for port I/O primitives.
flowey/flowey_lib_hvlite/src/lib.rs Export the new build_opentmk flow node.
flowey/flowey_lib_hvlite/src/build_opentmk.rs New node to build OpenTMK as a UEFI binary artifact.
flowey/flowey_lib_hvlite/src/_jobs/mod.rs Register a new local job for building OpenTMK.
flowey/flowey_lib_hvlite/src/_jobs/local_build_opentmk.rs Local job to package the EFI binary into a fixed VHD.
flowey/flowey_hvlite/src/pipelines/mod.rs Add a new build-opentmk CLI pipeline entrypoint.
flowey/flowey_hvlite/src/pipelines/build_opentmk.rs Implement the local-only pipeline wiring for build-opentmk.

Comment on lines +321 to +325
let x_pm1a: GenericAddress =
fadt_field!(x_pm1a_cnt_blk, GenericAddress, GenericAddress::default());
let x_pm1b: GenericAddress =
fadt_field!(x_pm1b_cnt_blk, GenericAddress, GenericAddress::default());
let sleep_reg: GenericAddress =
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_sleep_mechanism reads GenericAddress via read_unaligned into acpi_spec::fadt::GenericAddress, but that type contains Rust enums (AddressSpaceId, AddressWidth). Because the FADT contents come from firmware (untrusted), an invalid discriminant would make this read undefined behavior. Parse these fields as raw bytes (e.g., a local GenericAddressRaw { addr_space_id: u8, ..., access_size: u8, address: u64 }) and only convert to the enum after validating/handling unknown values.

Copilot uses AI. Check for mistakes.
Comment on lines +328 to +334
// Prefer extended addresses over legacy.
let x_pm1a_addr = x_pm1a.address;
let effective_pm1a = if x_pm1a_addr != 0 {
x_pm1a_addr as u16
} else {
pm1a as u16
};
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x_pm1a.address/pm1a_cnt_blk are truncated to u16 without validating the address space or range. If the FADT provides a SystemMemory GAS or an address > 0xFFFF, this will silently pick the wrong I/O port. Please validate that the chosen PM1* control block is in SystemIO space and fits in u16 (otherwise fall back to the HW-reduced sleep control path or return an ACPI error).

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +197
let port = reg_addr as u16;

// Hyper-V workaround: the sleep_control_reg often points one
// byte into PM1_CNT (e.g. 0x405 when PM1_CNT is at 0x404).
// The hypervisor only handles word-width writes at the aligned
// base, so mask off the low bit and do a 16-bit outw.
let pm1_cnt_port = port & !1;
log::info!(
"ACPI HW-reduced shutdown: writing word 0x{:04x} to PM1_CNT port 0x{:x}",
word_value,
pm1_cnt_port
);
crate::arch::io::outw(pm1_cnt_port, word_value);
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the SystemIO HW-reduced path, sleep_reg.address is cast to u16 (let port = reg_addr as u16;) without checking it fits. Since the address originates from firmware, validate reg_addr <= u16::MAX (and ideally that the register is actually byte/word addressable) before issuing outw/outb, otherwise the write may hit an unintended port.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +24
// Attempt a clean ACPI S5 shutdown. On UEFI this never returns --
// it either powers off the VM or spins forever internally.
// The loop below is unreachable but satisfies the Status return type.
let _ = crate::devices::shutdown::shutdown();
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments say the ACPI shutdown call “never returns” / the following loop is “unreachable”, but shutdown() can return Err (e.g., if FADT parsing fails). Consider either updating the comment to reflect that the loop is reachable on shutdown failure, or explicitly handle/log the error result so failures don’t look like a hang.

Suggested change
// Attempt a clean ACPI S5 shutdown. On UEFI this never returns --
// it either powers off the VM or spins forever internally.
// The loop below is unreachable but satisfies the Status return type.
let _ = crate::devices::shutdown::shutdown();
// Attempt a clean ACPI S5 shutdown. This should normally power off the
// VM. If shutdown fails or returns unexpectedly, log the error and fall
// back to spinning here.
if let Err(err) = crate::devices::shutdown::shutdown() {
log::error!("ACPI shutdown failed: {err:?}");
}

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +12
// Best-effort ACPI shutdown on panic. On UEFI this never returns --
// it either powers off the VM or spins forever internally.
let _ = crate::devices::shutdown::shutdown();
// Unreachable on UEFI, but required for the `-> !` return type.
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment claims the shutdown attempt “never returns”, but shutdown() can return Err (e.g., if it can’t determine a shutdown mechanism). Please adjust the comment and/or log/handle the error result so a shutdown failure is diagnosable.

Suggested change
// Best-effort ACPI shutdown on panic. On UEFI this never returns --
// it either powers off the VM or spins forever internally.
let _ = crate::devices::shutdown::shutdown();
// Unreachable on UEFI, but required for the `-> !` return type.
// Best-effort ACPI shutdown on panic. If shutdown cannot be performed,
// log the failure and fall back to spinning forever below.
if let Err(err) = crate::devices::shutdown::shutdown() {
log::error!("Failed to shut down after panic: {}", err);
}
// Fallback path if shutdown returns instead of powering off the VM.

Copilot uses AI. Check for mistakes.
use crate::tmkdefs::TmkError;
use crate::tmkdefs::TmkResult;

// --- Legacy PM1 constants ---
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no chance to use the 'true' defines here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

unsafe Related to unsafe code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants