diff --git a/src/hyperlight_host/src/hypervisor/regs.rs b/src/hyperlight_host/src/hypervisor/regs.rs index d29edf4bf..5d940ba69 100644 --- a/src/hyperlight_host/src/hypervisor/regs.rs +++ b/src/hyperlight_host/src/hypervisor/regs.rs @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +mod debug_regs; mod fpu; mod special_regs; mod standard_regs; @@ -21,6 +22,7 @@ mod standard_regs; #[cfg(target_os = "windows")] use std::collections::HashSet; +pub(crate) use debug_regs::*; pub(crate) use fpu::*; pub(crate) use special_regs::*; pub(crate) use standard_regs::*; diff --git a/src/hyperlight_host/src/hypervisor/regs/debug_regs.rs b/src/hyperlight_host/src/hypervisor/regs/debug_regs.rs new file mode 100644 index 000000000..201b83fcd --- /dev/null +++ b/src/hyperlight_host/src/hypervisor/regs/debug_regs.rs @@ -0,0 +1,270 @@ +/* +Copyright 2025 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#[cfg(kvm)] +use kvm_bindings::kvm_debugregs; +#[cfg(mshv3)] +use mshv_bindings::DebugRegisters; + +/// Common abstraction for x86 debug registers (DR0-DR7). +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub(crate) struct CommonDebugRegs { + pub dr0: u64, + pub dr1: u64, + pub dr2: u64, + pub dr3: u64, + pub dr6: u64, + pub dr7: u64, +} + +#[cfg(kvm)] +impl From for CommonDebugRegs { + fn from(kvm_regs: kvm_debugregs) -> Self { + Self { + dr0: kvm_regs.db[0], + dr1: kvm_regs.db[1], + dr2: kvm_regs.db[2], + dr3: kvm_regs.db[3], + dr6: kvm_regs.dr6, + dr7: kvm_regs.dr7, + } + } +} +#[cfg(kvm)] +impl From<&CommonDebugRegs> for kvm_debugregs { + fn from(common_regs: &CommonDebugRegs) -> Self { + kvm_debugregs { + db: [ + common_regs.dr0, + common_regs.dr1, + common_regs.dr2, + common_regs.dr3, + ], + dr6: common_regs.dr6, + dr7: common_regs.dr7, + ..Default::default() + } + } +} +#[cfg(mshv3)] +impl From for CommonDebugRegs { + fn from(mshv_regs: DebugRegisters) -> Self { + Self { + dr0: mshv_regs.dr0, + dr1: mshv_regs.dr1, + dr2: mshv_regs.dr2, + dr3: mshv_regs.dr3, + dr6: mshv_regs.dr6, + dr7: mshv_regs.dr7, + } + } +} +#[cfg(mshv3)] +impl From<&CommonDebugRegs> for DebugRegisters { + fn from(common_regs: &CommonDebugRegs) -> Self { + DebugRegisters { + dr0: common_regs.dr0, + dr1: common_regs.dr1, + dr2: common_regs.dr2, + dr3: common_regs.dr3, + dr6: common_regs.dr6, + dr7: common_regs.dr7, + } + } +} + +#[cfg(target_os = "windows")] +use windows::Win32::System::Hypervisor::*; + +#[cfg(target_os = "windows")] +impl From<&CommonDebugRegs> + for [(WHV_REGISTER_NAME, Align16); WHP_DEBUG_REGS_NAMES_LEN] +{ + fn from(regs: &CommonDebugRegs) -> Self { + [ + ( + WHvX64RegisterDr0, + Align16(WHV_REGISTER_VALUE { Reg64: regs.dr0 }), + ), + ( + WHvX64RegisterDr1, + Align16(WHV_REGISTER_VALUE { Reg64: regs.dr1 }), + ), + ( + WHvX64RegisterDr2, + Align16(WHV_REGISTER_VALUE { Reg64: regs.dr2 }), + ), + ( + WHvX64RegisterDr3, + Align16(WHV_REGISTER_VALUE { Reg64: regs.dr3 }), + ), + ( + WHvX64RegisterDr6, + Align16(WHV_REGISTER_VALUE { Reg64: regs.dr6 }), + ), + ( + WHvX64RegisterDr7, + Align16(WHV_REGISTER_VALUE { Reg64: regs.dr7 }), + ), + ] + } +} + +#[cfg(target_os = "windows")] +use std::collections::HashSet; + +#[cfg(target_os = "windows")] +use super::{Align16, FromWhpRegisterError}; + +#[cfg(target_os = "windows")] +pub(crate) const WHP_DEBUG_REGS_NAMES_LEN: usize = 6; +#[cfg(target_os = "windows")] +pub(crate) const WHP_DEBUG_REGS_NAMES: [WHV_REGISTER_NAME; WHP_DEBUG_REGS_NAMES_LEN] = [ + WHvX64RegisterDr0, + WHvX64RegisterDr1, + WHvX64RegisterDr2, + WHvX64RegisterDr3, + WHvX64RegisterDr6, + WHvX64RegisterDr7, +]; + +#[cfg(target_os = "windows")] +impl TryFrom<&[(WHV_REGISTER_NAME, Align16)]> for CommonDebugRegs { + type Error = FromWhpRegisterError; + + #[expect( + non_upper_case_globals, + reason = "Windows API has lowercase register names" + )] + fn try_from( + regs: &[(WHV_REGISTER_NAME, Align16)], + ) -> Result { + if regs.len() != WHP_DEBUG_REGS_NAMES_LEN { + return Err(FromWhpRegisterError::InvalidLength(regs.len())); + } + let mut registers = CommonDebugRegs::default(); + let mut seen_registers = HashSet::new(); + + for &(name, value) in regs { + let name_id = name.0; + + // Check for duplicates + if !seen_registers.insert(name_id) { + return Err(FromWhpRegisterError::DuplicateRegister(name_id)); + } + + unsafe { + match name { + WHvX64RegisterDr0 => registers.dr0 = value.0.Reg64, + WHvX64RegisterDr1 => registers.dr1 = value.0.Reg64, + WHvX64RegisterDr2 => registers.dr2 = value.0.Reg64, + WHvX64RegisterDr3 => registers.dr3 = value.0.Reg64, + WHvX64RegisterDr6 => registers.dr6 = value.0.Reg64, + WHvX64RegisterDr7 => registers.dr7 = value.0.Reg64, + _ => { + // Given unexpected register + return Err(FromWhpRegisterError::InvalidRegister(name_id)); + } + } + } + } + + // Set of all expected register names + let expected_registers: HashSet = WHP_DEBUG_REGS_NAMES + .map(|name| name.0) + .into_iter() + .collect(); + + // Technically it should not be possible to have any missing registers at this point + // since we are guaranteed to have WHP_DEBUG_REGS_NAMES_LEN (6) non-duplicate registers that have passed the match-arm above, but leaving this here for safety anyway + let missing: HashSet<_> = expected_registers + .difference(&seen_registers) + .cloned() + .collect(); + + if !missing.is_empty() { + return Err(FromWhpRegisterError::MissingRegister(missing)); + } + + Ok(registers) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn common_debug_regs() -> CommonDebugRegs { + CommonDebugRegs { + dr0: 1, + dr1: 2, + dr2: 3, + dr3: 4, + dr6: 5, + dr7: 6, + } + } + + #[cfg(kvm)] + #[test] + fn round_trip_kvm_debug_regs() { + let original = common_debug_regs(); + let kvm_regs: kvm_debugregs = (&original).into(); + let converted: CommonDebugRegs = kvm_regs.into(); + assert_eq!(original, converted); + } + + #[cfg(mshv3)] + #[test] + fn round_trip_mshv_debug_regs() { + let original = common_debug_regs(); + let mshv_regs: DebugRegisters = (&original).into(); + let converted: CommonDebugRegs = mshv_regs.into(); + assert_eq!(original, converted); + } + + #[cfg(target_os = "windows")] + #[test] + fn round_trip_whp_debug_regs() { + let original = common_debug_regs(); + let whp_regs: [(WHV_REGISTER_NAME, Align16); WHP_DEBUG_REGS_NAMES_LEN] = + (&original).into(); + let converted: CommonDebugRegs = whp_regs.as_ref().try_into().unwrap(); + assert_eq!(original, converted); + + // test for duplicate register error handling + let original = common_debug_regs(); + let mut whp_regs: [(WHV_REGISTER_NAME, Align16); + WHP_DEBUG_REGS_NAMES_LEN] = (&original).into(); + whp_regs[0].0 = WHvX64RegisterDr1; + let err = CommonDebugRegs::try_from(whp_regs.as_ref()).unwrap_err(); + assert_eq!( + err, + FromWhpRegisterError::DuplicateRegister(WHvX64RegisterDr1.0) + ); + + // test for passing non-standard register (e.g. CR8) + let original = common_debug_regs(); + let mut whp_regs: [(WHV_REGISTER_NAME, Align16); + WHP_DEBUG_REGS_NAMES_LEN] = (&original).into(); + whp_regs[0].0 = WHvX64RegisterCr8; + let err = CommonDebugRegs::try_from(whp_regs.as_ref()).unwrap_err(); + assert_eq!( + err, + FromWhpRegisterError::InvalidRegister(WHvX64RegisterCr8.0) + ); + } +} diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/kvm.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/kvm.rs index b5ca402fe..5bf9d0756 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/kvm.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/kvm.rs @@ -18,14 +18,16 @@ use std::sync::LazyLock; #[cfg(gdb)] use kvm_bindings::kvm_guest_debug; -use kvm_bindings::{kvm_fpu, kvm_regs, kvm_sregs, kvm_userspace_memory_region}; +use kvm_bindings::{kvm_debugregs, kvm_fpu, kvm_regs, kvm_sregs, kvm_userspace_memory_region}; use kvm_ioctls::Cap::UserMemory; use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd}; use tracing::{Span, instrument}; #[cfg(gdb)] use crate::hypervisor::gdb::DebuggableVm; -use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters}; +use crate::hypervisor::regs::{ + CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters, +}; use crate::hypervisor::virtual_machine::{VirtualMachine, VmExit}; use crate::mem::memory_region::MemoryRegion; use crate::{Result, new_error}; @@ -58,7 +60,7 @@ pub(crate) struct KvmVm { vm_fd: VmFd, vcpu_fd: VcpuFd, - // KVM as opposed to mshv/whp has no way to get current debug regs, so need to keep a copy here + // KVM, as opposed to mshv/whp, has no get_guest_debug() ioctl, so we must track the state ourselves #[cfg(gdb)] debug_regs: kvm_guest_debug, } @@ -161,6 +163,17 @@ impl VirtualMachine for KvmVm { Ok(()) } + fn debug_regs(&self) -> Result { + let kvm_debug_regs = self.vcpu_fd.get_debug_regs()?; + Ok(kvm_debug_regs.into()) + } + + fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> { + let kvm_debug_regs: kvm_debugregs = drs.into(); + self.vcpu_fd.set_debug_regs(&kvm_debug_regs)?; + Ok(()) + } + #[cfg(crashdump)] fn xsave(&self) -> Result> { let xsave = self.vcpu_fd.get_xsave()?; diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs index 55af78ebb..5403197ce 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs @@ -20,7 +20,9 @@ use std::sync::OnceLock; use tracing::{Span, instrument}; use crate::Result; -use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters}; +use crate::hypervisor::regs::{ + CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters, +}; use crate::mem::memory_region::MemoryRegion; /// KVM (Kernel-based Virtual Machine) functionality (linux) @@ -164,6 +166,12 @@ pub(crate) trait VirtualMachine: Debug + Send { fn sregs(&self) -> Result; /// Set special regs fn set_sregs(&self, sregs: &CommonSpecialRegisters) -> Result<()>; + /// Get the debug registers of the vCPU + #[allow(dead_code)] + fn debug_regs(&self) -> Result; + /// Set the debug registers of the vCPU + #[allow(dead_code)] + fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()>; /// xsave #[cfg(crashdump)] diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/mshv.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/mshv.rs index c0e352604..2888d2fc5 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/mshv.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/mshv.rs @@ -33,7 +33,9 @@ use tracing::{Span, instrument}; #[cfg(gdb)] use crate::hypervisor::gdb::DebuggableVm; -use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters}; +use crate::hypervisor::regs::{ + CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters, +}; use crate::hypervisor::virtual_machine::{VirtualMachine, VmExit}; use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; use crate::{Result, new_error}; @@ -209,6 +211,17 @@ impl VirtualMachine for MshvVm { Ok(()) } + fn debug_regs(&self) -> Result { + let debug_regs = self.vcpu_fd.get_debug_regs()?; + Ok(debug_regs.into()) + } + + fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> { + let mshv_debug_regs = drs.into(); + self.vcpu_fd.set_debug_regs(&mshv_debug_regs)?; + Ok(()) + } + #[cfg(crashdump)] fn xsave(&self) -> Result> { let xsave = self.vcpu_fd.get_xsave()?; @@ -282,42 +295,30 @@ impl DebuggableVm for MshvVm { fn add_hw_breakpoint(&mut self, addr: u64) -> Result<()> { use crate::hypervisor::gdb::arch::MAX_NO_OF_HW_BP; - let mut debug_regs = self.vcpu_fd.get_debug_regs()?; + let mut regs = self.debug_regs()?; // Check if breakpoint already exists - if [ - debug_regs.dr0, - debug_regs.dr1, - debug_regs.dr2, - debug_regs.dr3, - ] - .contains(&addr) - { + if [regs.dr0, regs.dr1, regs.dr2, regs.dr3].contains(&addr) { return Ok(()); } // Find the first available LOCAL (L0–L3) slot let i = (0..MAX_NO_OF_HW_BP) - .position(|i| debug_regs.dr7 & (1 << (i * 2)) == 0) + .position(|i| regs.dr7 & (1 << (i * 2)) == 0) .ok_or_else(|| new_error!("Tried to add more than 4 hardware breakpoints"))?; // Assign to corresponding debug register - *[ - &mut debug_regs.dr0, - &mut debug_regs.dr1, - &mut debug_regs.dr2, - &mut debug_regs.dr3, - ][i] = addr; + *[&mut regs.dr0, &mut regs.dr1, &mut regs.dr2, &mut regs.dr3][i] = addr; // Enable LOCAL bit - debug_regs.dr7 |= 1 << (i * 2); + regs.dr7 |= 1 << (i * 2); - self.vcpu_fd.set_debug_regs(&debug_regs)?; + self.set_debug_regs(®s)?; Ok(()) } fn remove_hw_breakpoint(&mut self, addr: u64) -> Result<()> { - let mut debug_regs = self.vcpu_fd.get_debug_regs()?; + let mut debug_regs = self.debug_regs()?; let regs = [ &mut debug_regs.dr0, @@ -331,7 +332,7 @@ impl DebuggableVm for MshvVm { *regs[i] = 0; // Disable LOCAL bit debug_regs.dr7 &= !(1 << (i * 2)); - self.vcpu_fd.set_debug_regs(&debug_regs)?; + self.set_debug_regs(&debug_regs)?; Ok(()) } else { Err(new_error!("Tried to remove non-existing hw-breakpoint")) diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs index 7effa3e9f..4ada3714d 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs @@ -26,7 +26,8 @@ use windows_result::HRESULT; #[cfg(gdb)] use crate::hypervisor::gdb::DebuggableVm; use crate::hypervisor::regs::{ - Align16, CommonFpu, CommonRegisters, CommonSpecialRegisters, WHP_FPU_NAMES, WHP_FPU_NAMES_LEN, + Align16, CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters, + WHP_DEBUG_REGS_NAMES, WHP_DEBUG_REGS_NAMES_LEN, WHP_FPU_NAMES, WHP_FPU_NAMES_LEN, WHP_REGS_NAMES, WHP_REGS_NAMES_LEN, WHP_SREGS_NAMES, WHP_SREGS_NAMES_LEN, }; use crate::hypervisor::surrogate_process::SurrogateProcess; @@ -386,6 +387,38 @@ impl VirtualMachine for WhpVm { Ok(()) } + fn debug_regs(&self) -> Result { + let mut whp_debug_regs_values: [Align16; WHP_DEBUG_REGS_NAMES_LEN] = + Default::default(); + + unsafe { + WHvGetVirtualProcessorRegisters( + self.partition, + 0, + WHP_DEBUG_REGS_NAMES.as_ptr(), + whp_debug_regs_values.len() as u32, + whp_debug_regs_values.as_mut_ptr() as *mut WHV_REGISTER_VALUE, + )?; + } + + let whp_debug_regs: [(WHV_REGISTER_NAME, Align16); + WHP_DEBUG_REGS_NAMES_LEN] = + std::array::from_fn(|i| (WHP_DEBUG_REGS_NAMES[i], whp_debug_regs_values[i])); + whp_debug_regs.as_slice().try_into().map_err(|e| { + new_error!( + "Failed to convert WHP registers to CommonDebugRegs: {:?}", + e + ) + }) + } + + fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> { + let whp_regs: [(WHV_REGISTER_NAME, Align16); WHP_DEBUG_REGS_NAMES_LEN] = + drs.into(); + self.set_registers(&whp_regs)?; + Ok(()) + } + #[cfg(crashdump)] fn xsave(&self) -> Result> { use crate::HyperlightError; @@ -528,137 +561,46 @@ impl DebuggableVm for WhpVm { use crate::hypervisor::gdb::arch::MAX_NO_OF_HW_BP; // Get current debug registers - const LEN: usize = 6; - let names: [WHV_REGISTER_NAME; LEN] = [ - WHvX64RegisterDr0, - WHvX64RegisterDr1, - WHvX64RegisterDr2, - WHvX64RegisterDr3, - WHvX64RegisterDr6, - WHvX64RegisterDr7, - ]; - - let mut out: [Align16; LEN] = unsafe { std::mem::zeroed() }; - unsafe { - WHvGetVirtualProcessorRegisters( - self.partition, - 0, - names.as_ptr(), - LEN as u32, - out.as_mut_ptr() as *mut WHV_REGISTER_VALUE, - )?; - } - - let mut dr0 = unsafe { out[0].0.Reg64 }; - let mut dr1 = unsafe { out[1].0.Reg64 }; - let mut dr2 = unsafe { out[2].0.Reg64 }; - let mut dr3 = unsafe { out[3].0.Reg64 }; - let mut dr7 = unsafe { out[5].0.Reg64 }; + let mut regs = self.debug_regs()?; // Check if breakpoint already exists - if [dr0, dr1, dr2, dr3].contains(&addr) { + if [regs.dr0, regs.dr1, regs.dr2, regs.dr3].contains(&addr) { return Ok(()); } // Find the first available LOCAL (L0–L3) slot let i = (0..MAX_NO_OF_HW_BP) - .position(|i| dr7 & (1 << (i * 2)) == 0) + .position(|i| regs.dr7 & (1 << (i * 2)) == 0) .ok_or_else(|| new_error!("Tried to add more than 4 hardware breakpoints"))?; // Assign to corresponding debug register - *[&mut dr0, &mut dr1, &mut dr2, &mut dr3][i] = addr; + *[&mut regs.dr0, &mut regs.dr1, &mut regs.dr2, &mut regs.dr3][i] = addr; // Enable LOCAL bit - dr7 |= 1 << (i * 2); + regs.dr7 |= 1 << (i * 2); - // Set the debug registers - let registers = vec![ - ( - WHvX64RegisterDr0, - Align16(WHV_REGISTER_VALUE { Reg64: dr0 }), - ), - ( - WHvX64RegisterDr1, - Align16(WHV_REGISTER_VALUE { Reg64: dr1 }), - ), - ( - WHvX64RegisterDr2, - Align16(WHV_REGISTER_VALUE { Reg64: dr2 }), - ), - ( - WHvX64RegisterDr3, - Align16(WHV_REGISTER_VALUE { Reg64: dr3 }), - ), - ( - WHvX64RegisterDr7, - Align16(WHV_REGISTER_VALUE { Reg64: dr7 }), - ), - ]; - self.set_registers(®isters)?; + self.set_debug_regs(®s)?; Ok(()) } fn remove_hw_breakpoint(&mut self, addr: u64) -> Result<()> { // Get current debug registers - const LEN: usize = 6; - let names: [WHV_REGISTER_NAME; LEN] = [ - WHvX64RegisterDr0, - WHvX64RegisterDr1, - WHvX64RegisterDr2, - WHvX64RegisterDr3, - WHvX64RegisterDr6, - WHvX64RegisterDr7, - ]; - - let mut out: [Align16; LEN] = unsafe { std::mem::zeroed() }; - unsafe { - WHvGetVirtualProcessorRegisters( - self.partition, - 0, - names.as_ptr(), - LEN as u32, - out.as_mut_ptr() as *mut WHV_REGISTER_VALUE, - )?; - } + let mut debug_regs = self.debug_regs()?; - let mut dr0 = unsafe { out[0].0.Reg64 }; - let mut dr1 = unsafe { out[1].0.Reg64 }; - let mut dr2 = unsafe { out[2].0.Reg64 }; - let mut dr3 = unsafe { out[3].0.Reg64 }; - let mut dr7 = unsafe { out[5].0.Reg64 }; - - let regs = [&mut dr0, &mut dr1, &mut dr2, &mut dr3]; + let regs = [ + &mut debug_regs.dr0, + &mut debug_regs.dr1, + &mut debug_regs.dr2, + &mut debug_regs.dr3, + ]; if let Some(i) = regs.iter().position(|&&mut reg| reg == addr) { // Clear the address *regs[i] = 0; // Disable LOCAL bit - dr7 &= !(1 << (i * 2)); - - // Set the debug registers - let registers = vec![ - ( - WHvX64RegisterDr0, - Align16(WHV_REGISTER_VALUE { Reg64: dr0 }), - ), - ( - WHvX64RegisterDr1, - Align16(WHV_REGISTER_VALUE { Reg64: dr1 }), - ), - ( - WHvX64RegisterDr2, - Align16(WHV_REGISTER_VALUE { Reg64: dr2 }), - ), - ( - WHvX64RegisterDr3, - Align16(WHV_REGISTER_VALUE { Reg64: dr3 }), - ), - ( - WHvX64RegisterDr7, - Align16(WHV_REGISTER_VALUE { Reg64: dr7 }), - ), - ]; - self.set_registers(®isters)?; + debug_regs.dr7 &= !(1 << (i * 2)); + + self.set_debug_regs(&debug_regs)?; Ok(()) } else { Err(new_error!("Tried to remove non-existing hw-breakpoint"))