Skip to content

firmware_uefi: implement MOR (Memory Overwrite Request) bit support#3397

Open
mebersol wants to merge 8 commits intomicrosoft:mainfrom
mebersol:user/mebersol/mor-bit-implementation
Open

firmware_uefi: implement MOR (Memory Overwrite Request) bit support#3397
mebersol wants to merge 8 commits intomicrosoft:mainfrom
mebersol:user/mebersol/mor-bit-implementation

Conversation

@mebersol
Copy link
Copy Markdown
Collaborator

Summary

Implements the MOR_SET_VARIABLE (0x31) UEFI device command, which was previously defined in the UefiCommand enum but silently dropped as an "unknown uefi write".

The MOR bit is part of the TCG Platform Reset Attack Mitigation Specification. The guest UEFI firmware uses this IO port command to persist the MemoryOverwriteRequestControl variable in NVRAM, signaling that memory should be cleared on the next boot/reset. Unlike the legacy HCL which only persisted the variable, the Underhill implementation additionally sets HvRegisterVsmPartitionConfig.zero_memory_on_reset via hypercall to ensure the hypervisor scrubs guest memory on partition reset.

Changes

  • uefi_specs: Define EFI_MEMORY_OVERWRITE_REQUEST_CONTROL_GUID ({e20939be-32d4-41be-a150-897f85d49829}) and MemoryOverwriteRequestControl variable constant
  • firmware_uefi: Handle MOR_SET_VARIABLE in write_data (persists 1-byte value in NVRAM) and read_data (returns 1/0 for last set status); add MorConfig platform trait for VMM-level notification
  • vmotherboard: Add mor_config field to HyperVFirmwareUefi device options and wire through to UefiRuntimeDeps
  • underhill_core: Implement UnderhillMorConfig — when MOR bit 0 is set, calls partition.set_zero_memory_on_reset(true) to ensure hypervisor memory scrub on reset
  • virt_mshv_vtl: Add UhPartition::set_zero_memory_on_reset() — reads current VsmPartitionConfig, sets the flag, writes it back
  • hcl: Add Hcl::get_vtl2_vsm_partition_config() getter for the VsmPartitionConfig register
  • openvmm_core: Wire mor_config: None (no hypervisor interaction needed for standalone OpenVMM)

Testing

  • Added unit test mor_set_variable in firmware_uefi::service::nvram::spec_services that verifies the MOR variable can be set to 0x01, read back, cleared to 0x00, and read back again through the NvramSpecServices layer
  • All 15 existing firmware_uefi tests continue to pass
  • Clippy, doc, and cargo xtask fmt all clean

Implement the MOR_SET_VARIABLE (0x31) UEFI device command, which was
previously defined but unhandled (falling through to 'unknown uefi write').

The MOR bit is part of the TCG Platform Reset Attack Mitigation
Specification. The guest UEFI firmware uses this IO port command to
persist the MemoryOverwriteRequestControl variable in NVRAM, signaling
that memory should be cleared on the next boot/reset.

Changes:
- Define EFI_MEMORY_OVERWRITE_REQUEST_CONTROL_GUID and the
  MemoryOverwriteRequestControl variable in uefi_specs
- Handle MOR_SET_VARIABLE in UefiDevice::write_data (persists the
  variable in NVRAM) and read_data (returns set status)
- Add MorConfig platform trait so the hosting VMM can react to MOR
  being set
- In Underhill, implement MorConfig by updating
  HvRegisterVsmPartitionConfig.zero_memory_on_reset via hypercall,
  ensuring the hypervisor scrubs guest memory on partition reset
- Add UhPartition::set_zero_memory_on_reset and Hcl getter for the
  VsmPartitionConfig register to support the above
- Wire mor_config=None for OpenVMM (no hypervisor interaction needed)

Bug: 59084623
Add an async unit test that verifies the MemoryOverwriteRequestControl
NVRAM variable can be set and read back through the NvramSpecServices
layer, exercising the MOR GUID and variable name constants.
Copilot AI review requested due to automatic review settings April 29, 2026 00:11
@mebersol mebersol requested a review from a team as a code owner April 29, 2026 00:11
@github-actions github-actions Bot added the unsafe Related to unsafe code label Apr 29, 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

Implements end-to-end UEFI MOR (Memory Overwrite Request) support so guests can persist MemoryOverwriteRequestControl in NVRAM via the UEFI helper device, with an Underhill-specific hook to request hypervisor memory scrubbing on partition reset.

Changes:

  • Add the MOR NVRAM variable GUID/name definition to uefi_specs.
  • Handle MOR_SET_VARIABLE (0x31) in the UEFI helper device: persist a 1-byte value to NVRAM, expose last-command status on reads, and notify an optional platform hook (MorConfig).
  • Wire the hook through vmotherboard/runtime deps; implement Underhill behavior by setting HvRegisterVsmPartitionConfig.zero_memory_on_reset.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
vmm_core/vmotherboard/src/base_chipset.rs Threads mor_config option through device deps into UefiRuntimeDeps.
vm/devices/firmware/uefi_specs/src/uefi/nvram.rs Defines MOR variable GUID and MemoryOverwriteRequestControl var accessor.
vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/mod.rs Adds unit test that sets/gets/clears the MOR variable through spec services.
vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs Adds set_mor_variable() convenience wrapper over set_variable_ucs2.
vm/devices/firmware/firmware_uefi/src/platform/nvram.rs Introduces MorConfig platform callback trait.
vm/devices/firmware/firmware_uefi/src/lib.rs Implements MOR_SET_VARIABLE read/write behavior, tracks last status, and calls MorConfig.
openvmm/openvmm_core/src/worker/dispatch.rs Wires mor_config: None for standalone OpenVMM.
openhcl/virt_mshv_vtl/src/lib.rs Adds UhPartition::set_zero_memory_on_reset() to update VSM partition config.
openhcl/underhill_core/src/worker.rs Provides UnderhillMorConfig to firmware device deps.
openhcl/underhill_core/src/emuplat/firmware.rs Implements MorConfig by setting hypervisor zero_memory_on_reset on MOR set.
openhcl/hcl/src/ioctl/register.rs Adds getter for HvRegisterVsmPartitionConfig to support read-modify-write.

Comment thread vm/devices/firmware/firmware_uefi/src/lib.rs Outdated
Comment on lines 623 to +626
gm: _,
watchdog_recv: _,
mor_bit_status: _,
mor_config: _,
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

mor_bit_status affects guest-visible behavior for MOR_SET_VARIABLE reads, but it is not included in the saved state (it’s ignored during save() and never restored). Consider adding it to SavedState and restoring it so snapshots/migration preserve the MOR status semantics. #Closed

Copilot uses AI. Check for mistakes.
Comment thread openhcl/underhill_core/src/emuplat/firmware.rs Outdated
Comment thread openhcl/virt_mshv_vtl/src/lib.rs
- Thread 1: Log EfiStatus and NvramError on MOR set failure instead of
  dropping the error details
- Thread 2: Include mor_bit_status in SavedState (#[mesh(8)]) so it
  survives save/restore and migration
- Thread 3: Always write the computed clear_memory value (true/false)
  to the partition config, so clearing MOR also clears
  zero_memory_on_reset
- Thread 4: Early-return in set_zero_memory_on_reset when the flag is
  already in the desired state (avoids redundant hypercall on
  guest-triggerable path), and downgrade log from info to debug
anyhow::Error does not implement std::error::Error directly. Use
err.as_ref() to get a &dyn std::error::Error for the tracing field.
Copilot AI review requested due to automatic review settings May 1, 2026 15:00
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

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Comment thread vm/devices/firmware/firmware_uefi/src/lib.rs
Comment thread vm/devices/firmware/firmware_uefi/src/lib.rs
Comment on lines +20 to +33
/// Callbacks for MOR (Memory Overwrite Request) bit changes.
///
/// When the guest sets the MOR bit via the UEFI device, the platform may need
/// to take action to ensure memory is scrubbed on the next reset. In Underhill,
/// this is done by setting the `zero_memory_on_reset` flag in
/// `HvRegisterVsmPartitionConfig`.
pub trait MorConfig: Send {
/// Called when the guest sets the MOR variable.
///
/// `mor_value` is the raw byte written by the guest. Bit 0
/// (`MOR_CLEAR_MEMORY_BIT_MASK`) indicates whether memory should be cleared
/// on the next reset.
fn notify_mor_set(&self, mor_value: u8);
}
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

This PR adds a new guest-visible firmware feature (MOR / memory-overwrite-request support) and a new platform callback (MorConfig). The Guide has a reference page for mu_msvm UEFI + the firmware_uefi device (Guide/src/reference/devices/firmware/mu_msvm_uefi.md), but it doesn't mention MOR. Consider updating that page (or adding a short note) so users know MOR is supported and what behavior to expect on reset.

Copilot uses AI. Check for mistakes.
Comment thread openhcl/virt_mshv_vtl/src/lib.rs Outdated
mebersol added 2 commits May 1, 2026 08:34
- Reset mor_bit_status to true on device reset (thread 5)
- Use Option<bool> for SavedState mor_bit_status so older snapshots
  without field 8 deserialize as None -> default true (thread 6)
- Use anyhow::Context instead of anyhow!() in set_zero_memory_on_reset
  to preserve the error chain (thread 8)
Use plain bool instead of Option<bool> for SavedState.mor_bit_status.
Protobuf defaults missing bool fields to false, which is the correct
behavior for older snapshots that didn't track MOR status (indicating
MOR support was not confirmed).
Copilot AI review requested due to automatic review settings May 1, 2026 17:10
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

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment on lines +88 to +94
if let Err(err) = partition.set_zero_memory_on_reset(clear_memory) {
tracing::warn!(
CVM_ALLOWED,
error = err.as_ref() as &dyn std::error::Error,
"failed to update zero_memory_on_reset for MOR"
);
}
Comment on lines +307 to +339
/// Handle the MOR_SET_VARIABLE command from the guest.
///
/// Persists the MOR variable in NVRAM and notifies the platform so it can
/// arrange for memory to be scrubbed on the next reset.
async fn handle_mor_set_variable(&mut self, value: u8) {
use uefi_specs::uefi::nvram::EfiVariableAttributes;
use uefi_specs::uefi::nvram::vars;

let attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES;

let result = self
.service
.nvram
.set_mor_variable(vars::MEMORY_OVERWRITE_REQUEST_CONTROL(), value, attr.into())
.await;

self.mor_bit_status = result.is_ok();

match result {
Ok(_) => {
if let Some(mor_config) = &self.mor_config {
mor_config.notify_mor_set(value);
}
}
Err((status, error)) => {
tracelimit::warn_ratelimited!(
?status,
?error,
"failed to set MOR variable in NVRAM"
);
}
}
}
Switch tracing::warn! to tracelimit::warn_ratelimited! in the MOR
notify_mor_set callback, since this path is triggered by guest IO
port writes and could spam logs.

// MOR (Memory Overwrite Request) state
mor_bit_status: bool,
#[inspect(skip)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe worth inspecting with an is_some?

None,
);
}
UefiCommand::MOR_SET_VARIABLE => block_on(self.handle_mor_set_variable(data as u8)),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can't be a block_on. If it really needs to be async then this device will have to do the whole PollDevice dance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Namely store a future and poll it inside poll_device

Copilot AI review requested due to automatic review settings May 4, 2026 23:51
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

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Comment on lines +1978 to +1980
let NvramResult(data, status, _err) = nvram
.uefi_get_variable(Some(name.as_bytes()), vendor, &mut 0u32, &mut 256u32, false)
.await;
Comment on lines +1992 to +1994
let NvramResult(data, status, _err) = nvram
.uefi_get_variable(Some(name.as_bytes()), vendor, &mut 0u32, &mut 256u32, false)
.await;
Comment on lines +83 to +88
const MOR_CLEAR_MEMORY_BIT_MASK: u8 = 0x01;

let clear_memory = (mor_value & MOR_CLEAR_MEMORY_BIT_MASK) != 0;

if let Some(partition) = self.partition.upgrade() {
if let Err(err) = partition.set_zero_memory_on_reset(clear_memory) {
Comment on lines +107 to +110
attr: u32,
) -> Result<(), (EfiStatus, Option<NvramError>)> {
self.services
.set_variable_ucs2(vendor, name, attr, vec![value])
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.

4 participants