openhcl: add IPMI KCS emulation and SEL forwarding - #4396
openhcl: add IPMI KCS emulation and SEL forwarding#4396ayusharora221204 wants to merge 5 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dda9bd55-db87-46d6-a512-15d8a769b9a3
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces a new chipset device and expands a guest↔host protocol surface area, so it warrants careful human review of contract compatibility and failure-handling paths.
Pull request overview
This PR adds a minimal virtual IPMI BMC to OpenHCL, exposing a KCS interface to VTL0 guests and forwarding completed SEL records to the host over GET. It threads a new EnableIpmi device-platform setting through UEFI config and attestation claims, and wires up architecture-specific chipset resources and resolvers.
Changes:
- Introduce a new
ipmi_kcschipset device crate implementing KCS + bounded SEL with save/restore and rate-limited host forwarding. - Extend GET protocol and transport/device plumbing to send “fire-and-forget” IPMI SEL host notifications.
- Add a new device-platform setting (
EnableIpmi) and wire it through UEFI flags, attestation, and VM manifest/chipset construction.
File summaries
| File | Description |
|---|---|
| vmm_core/vm_manifest_builder/src/lib.rs | Adds manifest-builder opt-in and resource handle wiring for the IPMI KCS device. |
| vm/loader/src/uefi/config.rs | Adds ipmi_enabled to UEFI config flags and tests its bit position. |
| vm/devices/get/guest_emulation_transport/src/resolver.rs | Adds a GET-backed resolver for the IPMI SEL event sink. |
| vm/devices/get/guest_emulation_transport/src/process_loop.rs | Adds a new host notification message path for IPMI SEL. |
| vm/devices/get/guest_emulation_transport/src/lib.rs | Updates DPS parsing tests and adds a GET notification test for IPMI SEL. |
| vm/devices/get/guest_emulation_transport/src/client.rs | Adds client API to enqueue IPMI SEL host notifications. |
| vm/devices/get/guest_emulation_transport/src/api.rs | Adds an exported IpmiSelRecord type and platform setting field. |
| vm/devices/get/guest_emulation_device/src/test_utilities.rs | Extends test harness to decode/store IPMI SEL notifications. |
| vm/devices/get/guest_emulation_device/src/lib.rs | Adds parsing/acceptance of the new IPMI SEL host notification. |
| vm/devices/get/get_protocol/src/lib.rs | Defines the IPMI_SEL host notification ID and its 22-byte wire struct. |
| vm/devices/get/get_protocol/src/dps_json.rs | Adds EnableIpmi JSON setting parsing and tests. |
| vm/devices/chipset/ipmi_kcs/src/tests.rs | Adds comprehensive unit tests for KCS/SEL behavior, forwarding, and save/restore. |
| vm/devices/chipset/ipmi_kcs/src/sel.rs | Implements bounded SEL storage, timestamps, and forwarding rate limiting. |
| vm/devices/chipset/ipmi_kcs/src/save_restore.rs | Implements validated save/restore state for the device. |
| vm/devices/chipset/ipmi_kcs/src/resolver.rs | Adds resource resolution for time source + SEL sink, producing chipset devices. |
| vm/devices/chipset/ipmi_kcs/src/protocol.rs | Implements minimal IPMI command handling and completion staging. |
| vm/devices/chipset/ipmi_kcs/src/lib.rs | Adds the core KCS state machine + public device API/types. |
| vm/devices/chipset/ipmi_kcs/src/device.rs | Implements AMD64 PIO and ARM64 MMIO intercept adapters, plus inspect + save/restore. |
| vm/devices/chipset/ipmi_kcs/Cargo.toml | New crate definition and dependencies. |
| vm/devices/chipset_resources/src/lib.rs | Adds shared IPMI SEL sink traits/types and IPMI KCS resource handles/addresses. |
| openhcl/underhill_core/src/worker.rs | Validates UEFI-only gating; wires GET resolver and chipset opt-in based on DPS. |
| openhcl/underhill_core/src/loader/mod.rs | Emits the new ipmi_enabled bit into UEFI config. |
| openhcl/underhill_attestation/src/lib.rs | Threads ipmi_enabled into attestation config structures/tests. |
| openhcl/underhill_attestation/src/igvm_attest/mod.rs | Updates attestation serialization expectations for ipmi-enabled. |
| openhcl/underhill_attestation/src/hardware_key_sealing.rs | Updates tests/inputs to include the new field. |
| openhcl/openvmm_hcl_resources/src/lib.rs | Registers the new IpmiKcsResolver. |
| openhcl/openvmm_hcl_resources/Cargo.toml | Adds ipmi_kcs dependency. |
| openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs | Adds ipmi_enabled to runtime claims with serde defaults. |
| Cargo.toml | Adds workspace dependency entry for the new ipmi_kcs crate. |
| Cargo.lock | Locks the new crate into the workspace graph. |
Review details
- Files reviewed: 29/30 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| impl SelEventSink for GetIpmiSelEventSink { | ||
| fn try_send( | ||
| &mut self, | ||
| record_id: u16, | ||
| record: [u8; IPMI_SEL_RECORD_SIZE], | ||
| ) -> SelEventDisposition { | ||
| self.0.ipmi_sel(record_id, record); | ||
| SelEventDisposition::Accepted | ||
| } |
There was a problem hiding this comment.
🔵 Needs a closer look
The GET-backed SEL event sink currently reports forwarded notifications as accepted even when the underlying mesh send can silently drop messages, which can break SEL forwarding accounting/semantics.
Review details
Suppressed comments (1)
vm/devices/get/guest_emulation_transport/src/resolver.rs:54
SelEventSink::try_sendalways returnsAcceptedeven thoughGuestEmulationTransportClient::ipmi_selultimately usesmesh::Sender::send, which can silently drop messages when the receiver is closed. This can cause the IPMI KCS device to over-report forwarded SEL records and under-report drops. Consider havingipmi_sel(orProcessLoopControl::notify) return whether the notification was actually queued (e.g., usingSender::is_closed()), and map that toSelEventDisposition::Droppedhere.
self.0.ipmi_sel(record_id, record);
SelEventDisposition::Accepted
}
- Files reviewed: 29/30 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
It should be straightforward to add a vmm_test for this for both linux and windows, could you do that as part of this PR? |
Enable IPMI in OpenVMM-hosted OpenHCL tests and observe host SEL notifications. Add Linux and Windows guest coverage for the KCS Add SEL path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dda9bd55-db87-46d6-a512-15d8a769b9a3
There was a problem hiding this comment.
🟡 Changes recommended
Multiple #[derive(MeshPayload)] structs had new fields inserted mid-struct, which changes derived field numbering and risks breaking compatibility with mixed-version binaries.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 38/39 changed files
- Comments generated: 4
- Review effort level: Lite
| /// Whether the serial console, if enabled, is interactive | ||
| pub interactive_console_enabled: bool, | ||
| /// Whether the IPMI KCS interface is enabled | ||
| #[serde(default, skip_serializing_if = "is_false")] | ||
| pub ipmi_enabled: bool, |
| /// Notification of firmware events. | ||
| pub firmware_event_send: Option<mesh::Sender<FirmwareEvent>>, | ||
| /// Test observer for IPMI SEL notifications received from OpenHCL. | ||
| pub ipmi_sel_event_send: Option<mesh::Sender<IpmiSelEvent>>, | ||
| /// Enable secure boot. |
| /// Enable battery. | ||
| pub enable_battery: bool, | ||
| /// Enable the IPMI KCS interface. | ||
| pub enable_ipmi: bool, | ||
| /// Suppress attestation and disable TPM state persistence. |
| /// An IPMI SEL notification received from OpenHCL. | ||
| #[derive(Debug, Clone, Copy, MeshPayload, PartialEq, Eq)] | ||
| pub struct IpmiSelEvent { | ||
| /// BMC-assigned SEL record identifier. | ||
| pub record_id: u16, | ||
| /// Completed SEL record. | ||
| pub record: [u8; 16], | ||
| } |
Chris Oo (chris-oo)
left a comment
There was a problem hiding this comment.
some initial feedback.
do you also plan on adding openvmm support as a follow up?
| Signer, | ||
| } | ||
|
|
||
| fn is_false(value: &bool) -> bool { |
| /// Whether the serial console, if enabled, is interactive | ||
| pub interactive_console_enabled: bool, | ||
| /// Whether the IPMI KCS interface is enabled | ||
| #[serde(default, skip_serializing_if = "is_false")] |
There was a problem hiding this comment.
is this really the right thing to do for runtime claims? shouldn't we always include it whether its' true or false, like the other things?
There was a problem hiding this comment.
The reason for is_false and skip_serializing_if was to preserve the previous serialized VM configuration when IPMI is disabled. I was concerned because this JSON is also used as input to hardware-key derivation, so even adding "ipmi-enabled": false changes that input for existing VMs. However, I agree that the runtime-claims contract should be consistent with the other Boolean fields and explicitly include both true and false. I think it is reasonable to follow the existing pattern here.
| ); | ||
|
|
||
| // Read measured config from VTL0 memory. When restoring, it is already gone. | ||
| let (firmware_type, mut measured_vtl0_info, load_kind) = { |
There was a problem hiding this comment.
The measured-VTL0 block was not deleted, it moved earlier. We need firmware_type earlier to reject IPMI on PCAT/Linux-direct before generating runtime claims or deriving hardware keys.
| use sel::RateLimiter; | ||
| use sel::SelState; | ||
|
|
||
| /// Maximum KCS request or response size. |
There was a problem hiding this comment.
are these spec defns for the ipmi device? probably should be in some kind of spec subcrate?
There was a problem hiding this comment.
Agreed. These are device specification definitions. I’ll separate those into an IPMI spec/protocol crate.
|
|
||
| /// Creates a virtual BMC with a best-effort SEL event sink. | ||
| pub fn with_event_sink( | ||
| clock: impl TrustedClock + 'static, |
There was a problem hiding this comment.
hm is it better to have the caller give us a box here, rather than us boxing it ourselves?
There was a problem hiding this comment.
Yes, that fits. The caller can give us an explicit box here.
| @@ -0,0 +1,108 @@ | |||
| // Copyright (c) Microsoft Corporation. | |||
| // Licensed under the MIT License. | |||
|
|
|||
There was a problem hiding this comment.
missing module comments (applies to all files missing them)
There was a problem hiding this comment.
Ohh yeah, I’ll add module-level documentation to the IPMI KCS source files describing each module’s responsibility.
| } | ||
| } | ||
|
|
||
| fn get_sel_info(&mut self, out: &mut [u8; KCS_MESSAGE_MAX]) -> usize { |
There was a problem hiding this comment.
a lot of these functions here do this manual byte munging/editing, is there no way to use zerocopy here with header/struct definitions to make it more obvious what we're writing to the buffer?
There was a problem hiding this comment.
Agreed. Fixed-format requests, responses and response headers can use zerocopy structures. But Get SEL Entry has a runtime length record payload determined by the requested offset and byte count, so I think only that trailing payload will retain a small bounded slice copy.
| use super::*; | ||
|
|
||
| #[test] | ||
| fn smoke_test_sample() { |
There was a problem hiding this comment.
was it intentional this test was removed?
There was a problem hiding this comment.
The sample parsing was not intentionally removed, the test was renamed and extended to verify that the existing sample defaults IPMI to false. I can restore smoke_test_sample and add a separate IPMI default/parsing test.
| type Input<'a> = (); | ||
| } | ||
|
|
||
| /// The size of an IPMI System Event Log record. |
There was a problem hiding this comment.
is this really the right places for these defns?
There was a problem hiding this comment.
The resource handle belongs in chipset_resources because it follows the existing device-resource pattern. I can move the IPMI-specific sink definitions into the existing chipset_resources::ipmi_kcs module for clearer organization. The SEL record-size constant is a protocol definition and can move to the proposed IPMI protocol crate.
|
|
||
| /// Result of attempting to forward an IPMI SEL record. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub enum SelEventDisposition { |
There was a problem hiding this comment.
hm not really a fan of this name, seems long/wordy?
There was a problem hiding this comment.
umm...yeah, maybe it can be changed to SendOutcome
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dda9bd55-db87-46d6-a512-15d8a769b9a3
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces a new guest-facing emulated device and extends host notification/attestation plumbing across multiple subsystems, warranting careful human review beyond minor findings.
Review details
- Files reviewed: 42/43 changed files
- Comments generated: 1
- Review effort level: Lite
| let request = self.transaction.request; | ||
| let request = &request[..self.transaction.request_len]; |
Overview
Add a minimal virtual IPMI BMC to OpenHCL so VTL0 guests can use the KCS interface and forward System Event Log records to the host.
Changes
0xCA2/0xCA3and the ARM64 MMIO registers at0xEFFE7000/0xEFFE7004.EnableIpmidevice-platform setting and include the setting in attestation and UEFI configuration.Validation
IPMIDRVstarts and exposes an activeMicrosoft_IPMIinstance.