diff --git a/src/hyperlight_host/src/hypervisor/gdb/hyperv_debug.rs b/src/hyperlight_host/src/hypervisor/gdb/hyperv_debug.rs index 7a902d6de..3835a2ff0 100644 --- a/src/hyperlight_host/src/hypervisor/gdb/hyperv_debug.rs +++ b/src/hyperlight_host/src/hypervisor/gdb/hyperv_debug.rs @@ -211,6 +211,18 @@ impl GuestDebug for HypervDebug { regs.rip = vcpu_regs.rip; regs.rflags = vcpu_regs.rflags; + // Fetch XMM from WHVP + if let Ok(fpu) = vcpu_fd.get_fpu() { + regs.xmm = [ + fpu.xmm0, fpu.xmm1, fpu.xmm2, fpu.xmm3, fpu.xmm4, fpu.xmm5, fpu.xmm6, fpu.xmm7, + fpu.xmm8, fpu.xmm9, fpu.xmm10, fpu.xmm11, fpu.xmm12, fpu.xmm13, fpu.xmm14, + fpu.xmm15, + ]; + regs.mxcsr = fpu.mxcsr; + } else { + log::warn!("Failed to read FPU/XMM via WHVP for debug registers"); + } + Ok(()) } diff --git a/src/hyperlight_host/src/hypervisor/gdb/kvm_debug.rs b/src/hyperlight_host/src/hypervisor/gdb/kvm_debug.rs index c20b3b279..6ed312993 100644 --- a/src/hyperlight_host/src/hypervisor/gdb/kvm_debug.rs +++ b/src/hyperlight_host/src/hypervisor/gdb/kvm_debug.rs @@ -16,6 +16,7 @@ limitations under the License. use std::collections::HashMap; +use gdbstub_arch::msp430::reg; use kvm_bindings::{ KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_GUESTDBG_USE_HW_BP, KVM_GUESTDBG_USE_SW_BP, kvm_debug_exit_arch, kvm_guest_debug, kvm_regs, @@ -193,6 +194,31 @@ impl GuestDebug for KvmDebug { regs.rip = vcpu_regs.rip; regs.rflags = vcpu_regs.rflags; + // Read XMM registers from FPU state + // note kvm get_fpu doesn't actually set or read the mxcsr value + // https://elixir.bootlin.com/linux/v6.16/source/arch/x86/kvm/x86.c#L12229 + match vcpu_fd.get_fpu() { + Ok(fpu) => { + // Convert KVM XMM registers ([u8; 16] x 16) to [u128; 16] + regs.xmm = fpu.xmm.map(u128::from_le_bytes); + }, + Err(e) => { + log::warn!("Failed to read FPU state for XMM registers: {:?}", e); + } + } + + // Read MXCSR from XSAVE (MXCSR is at byte offset 24 -> u32 index 6) + // Todo maybe I could use xsave to read the registers too instead of a separate call to get_fpu + match vcpu_fd.get_xsave() { + Ok(xsave) => { + regs.mxcsr = xsave.region[6]; + + } + Err(e) => { + log::warn!("Failed to read XSAVE for MXCSR: {:?}", e); + } + } + Ok(()) } diff --git a/src/hyperlight_host/src/hypervisor/gdb/mod.rs b/src/hyperlight_host/src/hypervisor/gdb/mod.rs index 3cc8ecd7c..985f0fa09 100644 --- a/src/hyperlight_host/src/hypervisor/gdb/mod.rs +++ b/src/hyperlight_host/src/hypervisor/gdb/mod.rs @@ -108,6 +108,8 @@ pub(crate) struct X86_64Regs { pub(crate) r15: u64, pub(crate) rip: u64, pub(crate) rflags: u64, + pub(crate) xmm: [u128; 16], + pub(crate) mxcsr: u32, } /// Defines the possible reasons for which a vCPU can be stopped when debugging diff --git a/src/hyperlight_host/src/hypervisor/gdb/mshv_debug.rs b/src/hyperlight_host/src/hypervisor/gdb/mshv_debug.rs index 9688d9ed1..3a82592a2 100644 --- a/src/hyperlight_host/src/hypervisor/gdb/mshv_debug.rs +++ b/src/hyperlight_host/src/hypervisor/gdb/mshv_debug.rs @@ -220,6 +220,18 @@ impl GuestDebug for MshvDebug { regs.rip = vcpu_regs.rip; regs.rflags = vcpu_regs.rflags; + // Try to read XMM from the FPU state + match vcpu_fd.get_fpu() { + Ok(fpu) => { + // MSHV exposes XMM as [[u8; 16]; 16]. Convert to [u128; 16] + regs.xmm = fpu.xmm.map(u128::from_le_bytes); + regs.mxcsr = fpu.mxcsr; + } + Err(e) => { + log::warn!("Failed to read FPU state for XMM registers (MSHV): {:?}", e); + } + } + Ok(()) } @@ -258,6 +270,8 @@ impl GuestDebug for MshvDebug { rip: regs.rip, rflags: regs.rflags, + + }; vcpu_fd diff --git a/src/hyperlight_host/src/hypervisor/gdb/x86_64_target.rs b/src/hyperlight_host/src/hypervisor/gdb/x86_64_target.rs index 3248e6082..07d0577ad 100644 --- a/src/hyperlight_host/src/hypervisor/gdb/x86_64_target.rs +++ b/src/hyperlight_host/src/hypervisor/gdb/x86_64_target.rs @@ -227,6 +227,8 @@ impl SingleThreadBase for HyperlightSandboxTarget { regs.regs[15] = read_regs.r15; regs.rip = read_regs.rip; regs.eflags = read_regs.rflags as u32; + regs.xmm = read_regs.xmm; + regs.mxcsr = read_regs.mxcsr; Ok(()) } @@ -267,6 +269,8 @@ impl SingleThreadBase for HyperlightSandboxTarget { r15: regs.regs[15], rip: regs.rip, rflags: u64::from(regs.eflags), + xmm: regs.xmm, + mxcsr: regs.mxcsr, }; match self.send_command(DebugMsg::WriteRegisters(regs))? { diff --git a/src/hyperlight_host/src/hypervisor/kvm.rs b/src/hyperlight_host/src/hypervisor/kvm.rs index 26eae3e25..36bfdeb62 100644 --- a/src/hyperlight_host/src/hypervisor/kvm.rs +++ b/src/hyperlight_host/src/hypervisor/kvm.rs @@ -591,6 +591,9 @@ impl Hypervisor for KVMDriver { mxcsr: MXCSR_DEFAULT, ..Default::default() // zero out the rest }; + + // note kvm set_fpu doesn't actually set or read the mxcsr value + // https://elixir.bootlin.com/linux/v6.16/source/arch/x86/kvm/x86.c#L12229 self.vcpu_fd.set_fpu(&fpu)?; // run diff --git a/src/hyperlight_host/src/hypervisor/windows_hypervisor_platform.rs b/src/hyperlight_host/src/hypervisor/windows_hypervisor_platform.rs index d6064443b..0b1f36b04 100644 --- a/src/hyperlight_host/src/hypervisor/windows_hypervisor_platform.rs +++ b/src/hyperlight_host/src/hypervisor/windows_hypervisor_platform.rs @@ -696,6 +696,124 @@ impl VMProcessor { Ok(()) } + pub(super) fn get_fpu(&self) -> Result { + use windows::Win32::System::Hypervisor::*; + + const LEN: usize = 26; + let names: [WHV_REGISTER_NAME; LEN] = [ + WHvX64RegisterXmm0, + WHvX64RegisterXmm1, + WHvX64RegisterXmm2, + WHvX64RegisterXmm3, + WHvX64RegisterXmm4, + WHvX64RegisterXmm5, + WHvX64RegisterXmm6, + WHvX64RegisterXmm7, + WHvX64RegisterXmm8, + WHvX64RegisterXmm9, + WHvX64RegisterXmm10, + WHvX64RegisterXmm11, + WHvX64RegisterXmm12, + WHvX64RegisterXmm13, + WHvX64RegisterXmm14, + WHvX64RegisterXmm15, + WHvX64RegisterFpMmx0, + WHvX64RegisterFpMmx1, + WHvX64RegisterFpMmx2, + WHvX64RegisterFpMmx3, + WHvX64RegisterFpMmx4, + WHvX64RegisterFpMmx5, + WHvX64RegisterFpMmx6, + WHvX64RegisterFpMmx7, + WHvX64RegisterFpControlStatus, + WHvX64RegisterXmmControlStatus, + ]; + + let mut out: [WHV_REGISTER_VALUE; LEN] = unsafe { std::mem::zeroed() }; + unsafe { + WHvGetVirtualProcessorRegisters( + self.get_partition_hdl(), + 0, + names.as_ptr(), + LEN as u32, + out.as_mut_ptr(), + )?; + } + + // Helper to read a WHV_UINT128 -> u128 + fn u128_from_whv(fp: WHV_REGISTER_VALUE) -> u128 { + unsafe { + let low = fp.Fp.AsUINT128.Anonymous.Low64 as u128; + let high = fp.Fp.AsUINT128.Anonymous.High64 as u128; + (high << 64) | low + } + } + + let xmm = [ + u128_from_whv(out[0]), + u128_from_whv(out[1]), + u128_from_whv(out[2]), + u128_from_whv(out[3]), + u128_from_whv(out[4]), + u128_from_whv(out[5]), + u128_from_whv(out[6]), + u128_from_whv(out[7]), + u128_from_whv(out[8]), + u128_from_whv(out[9]), + u128_from_whv(out[10]), + u128_from_whv(out[11]), + u128_from_whv(out[12]), + u128_from_whv(out[13]), + u128_from_whv(out[14]), + u128_from_whv(out[15]), + ]; + + let mmx = [ + out[16].Reg64, + out[17].Reg64, + out[18].Reg64, + out[19].Reg64, + out[20].Reg64, + out[21].Reg64, + out[22].Reg64, + out[23].Reg64, + ]; + + let fp_control_word = unsafe { out[24].FpControlStatus.Anonymous.FpControl }; + let fp_tag_word = unsafe { out[24].FpControlStatus.Anonymous.FpTag }; + let mxcsr = unsafe { out[25].XmmControlStatus.Anonymous.XmmStatusControl }; + + Ok(WHvFPURegisters { + xmm0: xmm[0], + xmm1: xmm[1], + xmm2: xmm[2], + xmm3: xmm[3], + xmm4: xmm[4], + xmm5: xmm[5], + xmm6: xmm[6], + xmm7: xmm[7], + xmm8: xmm[8], + xmm9: xmm[9], + xmm10: xmm[10], + xmm11: xmm[11], + xmm12: xmm[12], + xmm13: xmm[13], + xmm14: xmm[14], + xmm15: xmm[15], + mmx0: mmx[0], + mmx1: mmx[1], + mmx2: mmx[2], + mmx3: mmx[3], + mmx4: mmx[4], + mmx5: mmx[5], + mmx6: mmx[6], + mmx7: mmx[7], + fp_control_word, + fp_tag_word, + mxcsr, + }) + } + #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] pub(super) fn run(&mut self) -> Result { let partition_handle = self.get_partition_hdl();