firmware_uefi: implement MOR (Memory Overwrite Request) bit support#3397
firmware_uefi: implement MOR (Memory Overwrite Request) bit support#3397mebersol wants to merge 8 commits intomicrosoft:mainfrom
Conversation
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.
|
This PR modifies files containing For more on why we check whole files, instead of just diffs, check out the Rustonomicon |
There was a problem hiding this comment.
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. |
| gm: _, | ||
| watchdog_recv: _, | ||
| mor_bit_status: _, | ||
| mor_config: _, |
Copilot
AI
Apr 29, 2026
•
There was a problem hiding this comment.
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
- 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.
| /// 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); | ||
| } |
There was a problem hiding this comment.
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.
- 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).
| 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" | ||
| ); | ||
| } |
| /// 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)] |
There was a problem hiding this comment.
maybe worth inspecting with an is_some?
| None, | ||
| ); | ||
| } | ||
| UefiCommand::MOR_SET_VARIABLE => block_on(self.handle_mor_set_variable(data as u8)), |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Namely store a future and poll it inside poll_device
| let NvramResult(data, status, _err) = nvram | ||
| .uefi_get_variable(Some(name.as_bytes()), vendor, &mut 0u32, &mut 256u32, false) | ||
| .await; |
| let NvramResult(data, status, _err) = nvram | ||
| .uefi_get_variable(Some(name.as_bytes()), vendor, &mut 0u32, &mut 256u32, false) | ||
| .await; |
| 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) { |
| attr: u32, | ||
| ) -> Result<(), (EfiStatus, Option<NvramError>)> { | ||
| self.services | ||
| .set_variable_ucs2(vendor, name, attr, vec![value]) |
Summary
Implements the
MOR_SET_VARIABLE(0x31) UEFI device command, which was previously defined in theUefiCommandenum 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
MemoryOverwriteRequestControlvariable 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 setsHvRegisterVsmPartitionConfig.zero_memory_on_resetvia hypercall to ensure the hypervisor scrubs guest memory on partition reset.Changes
EFI_MEMORY_OVERWRITE_REQUEST_CONTROL_GUID({e20939be-32d4-41be-a150-897f85d49829}) andMemoryOverwriteRequestControlvariable constantMOR_SET_VARIABLEinwrite_data(persists 1-byte value in NVRAM) andread_data(returns 1/0 for last set status); addMorConfigplatform trait for VMM-level notificationmor_configfield toHyperVFirmwareUefidevice options and wire through toUefiRuntimeDepsUnderhillMorConfig— when MOR bit 0 is set, callspartition.set_zero_memory_on_reset(true)to ensure hypervisor memory scrub on resetUhPartition::set_zero_memory_on_reset()— reads currentVsmPartitionConfig, sets the flag, writes it backHcl::get_vtl2_vsm_partition_config()getter for theVsmPartitionConfigregistermor_config: None(no hypervisor interaction needed for standalone OpenVMM)Testing
mor_set_variableinfirmware_uefi::service::nvram::spec_servicesthat verifies the MOR variable can be set to0x01, read back, cleared to0x00, and read back again through theNvramSpecServiceslayerfirmware_uefitests continue to passcargo xtask fmtall clean