From f665eabaaa1b4ac477428015979870a08f9814c5 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:51:10 -0700 Subject: [PATCH] mshv: use VP register page for RIP/RAX writes in run_vcpu Replace set_reg() hypercalls with direct VP register page writes for the RIP increment on IO exits and the RAX write on IO-in (hw-interrupts). The VP register page is a shared memory page between userspace and the hypervisor that is picked up automatically on the next DISPATCH_VP, eliminating one hypercall per IO exit. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> --- .../src/hypervisor/virtual_machine/mod.rs | 2 + .../hypervisor/virtual_machine/mshv/x86_64.rs | 59 +++++++++++-------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs index 9edaa1f87..d442f4159 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs @@ -200,6 +200,8 @@ pub enum RunVcpuError { GetDr6(HypervisorError), #[error("Increment RIP failed: {0}")] IncrementRip(HypervisorError), + #[error("VP register page not available")] + NoVpRegisterPage, #[error("Parse GPA access info failed")] ParseGpaAccessInfo, #[error("Unknown error: {0}")] diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/mshv/x86_64.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/mshv/x86_64.rs index 0a768bd7a..6e150fb8d 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/mshv/x86_64.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/mshv/x86_64.rs @@ -26,17 +26,16 @@ use mshv_bindings::LapicState; #[cfg(gdb)] use mshv_bindings::{DebugRegisters, hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT}; use mshv_bindings::{ - FloatingPointUnit, SpecialRegisters, StandardRegisters, XSave, hv_message_type, - hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA, + FloatingPointUnit, HV_X64_REGISTER_CLASS_IP, SpecialRegisters, StandardRegisters, XSave, + hv_message_type, hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA, hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT, hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES, - hv_partition_synthetic_processor_features, hv_register_assoc, - hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_create_partition_v2, - mshv_user_mem_region, + hv_partition_synthetic_processor_features, mshv_create_partition_v2, mshv_user_mem_region, }; #[cfg(feature = "hw-interrupts")] use mshv_bindings::{ - hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED, hv_register_name_HV_X64_REGISTER_RAX, + HV_X64_REGISTER_CLASS_GENERAL, hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED, + hv_register_assoc, hv_register_value, }; #[cfg(feature = "hw-interrupts")] use mshv_ioctls::InterruptRequest; @@ -219,16 +218,21 @@ impl VirtualMachine for MshvVm { let instruction_length = io_message.header.instruction_length() as u64; let is_write = io_message.header.intercept_access_type != 0; - // mshv, unlike kvm, does not automatically increment RIP - self.vcpu_fd - .set_reg(&[hv_register_assoc { - name: hv_register_name_HV_X64_REGISTER_RIP, - value: hv_register_value { - reg64: rip + instruction_length, - }, - ..Default::default() - }]) - .map_err(|e| RunVcpuError::IncrementRip(e.into()))?; + // mshv, unlike kvm, does not automatically increment RIP. + { + let page = self + .vcpu_fd + .get_vp_reg_page() + .ok_or(RunVcpuError::NoVpRegisterPage)?; + // SAFETY: The register page is a valid mmap'd page + // from create_vcpu and is always populated after a + // vcpu run returns an intercept message. + unsafe { + (*page.0).__bindgen_anon_1.__bindgen_anon_1.rip = + rip + instruction_length; + (*page.0).dirty |= 1 << HV_X64_REGISTER_CLASS_IP; + } + } // VmAction::Halt always means "I'm done", regardless // of whether a timer is active. @@ -253,13 +257,22 @@ impl VirtualMachine for MshvVm { } else if let Some(val) = super::super::x86_64::hw_interrupts::handle_io_in(port_number) { - self.vcpu_fd - .set_reg(&[hv_register_assoc { - name: hv_register_name_HV_X64_REGISTER_RAX, - value: hv_register_value { reg64: val }, - ..Default::default() - }]) - .map_err(|e| RunVcpuError::Unknown(e.into()))?; + let page = self + .vcpu_fd + .get_vp_reg_page() + .ok_or(RunVcpuError::NoVpRegisterPage)?; + // SAFETY: The register page is a valid mmap'd page + // from create_vcpu and is always populated after a + // vcpu run returns an intercept message. + unsafe { + (*page.0) + .__bindgen_anon_1 + .__bindgen_anon_1 + .__bindgen_anon_1 + .__bindgen_anon_1 + .rax = val; + (*page.0).dirty |= 1 << HV_X64_REGISTER_CLASS_GENERAL; + } continue; } }