From 9f27954fbd996f6e310901aa297a1513ddf1f80c Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Sun, 20 Feb 2022 11:49:25 +0900 Subject: [PATCH] hypervisor: Add `set_guest_debug` for x86/KVM This commit adds `set_guest_debug` implementation for x86/KVM. This function sets hardware breakpoints and single step to debug registers. NOTE: The `set_guest_debug` implementation is based on the crosvm implementation [1]. [1] https://github.com/google/crosvm/blob/main/hypervisor/src/kvm/x86_64.rs Signed-off-by: Akira Moroo --- hypervisor/src/cpu.rs | 7 +++++++ hypervisor/src/kvm/mod.rs | 42 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/hypervisor/src/cpu.rs b/hypervisor/src/cpu.rs index abee886a3..8b57269a2 100644 --- a/hypervisor/src/cpu.rs +++ b/hypervisor/src/cpu.rs @@ -30,6 +30,8 @@ use crate::Xsave; #[cfg(feature = "tdx")] use crate::{TdxExitDetails, TdxExitStatus}; use thiserror::Error; +#[cfg(all(feature = "kvm", target_arch = "x86_64"))] +use vm_memory::GuestAddress; #[derive(Error, Debug)] /// @@ -397,6 +399,11 @@ pub trait Vcpu: Send + Sync { /// potential soft lockups when being resumed. /// fn notify_guest_clock_paused(&self) -> Result<()>; + #[cfg(all(feature = "kvm", target_arch = "x86_64"))] + /// + /// Sets debug registers to set hardware breakpoints and/or enable single step. + /// + fn set_guest_debug(&self, addrs: &[GuestAddress], singlestep: bool) -> Result<()>; /// /// Sets the type of CPU to be exposed to the guest and optional features. /// diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 6ed9696d2..d272c7657 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -42,7 +42,8 @@ use crate::arch::x86::NUM_IOAPIC_PINS; use aarch64::{RegList, Register, StandardRegisters}; #[cfg(target_arch = "x86_64")] use kvm_bindings::{ - kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP, + kvm_enable_cap, kvm_guest_debug, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, + KVM_CAP_SPLIT_IRQCHIP, KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_SINGLESTEP, KVM_GUESTDBG_USE_HW_BP, }; #[cfg(target_arch = "x86_64")] use x86_64::{check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters}; @@ -1125,6 +1126,45 @@ impl cpu::Vcpu for KvmVcpu { Ok(()) } + #[cfg(target_arch = "x86_64")] + /// + /// Sets debug registers to set hardware breakpoints and/or enable single step. + /// + fn set_guest_debug( + &self, + addrs: &[vm_memory::GuestAddress], + singlestep: bool, + ) -> cpu::Result<()> { + if addrs.len() > 4 { + return Err(cpu::HypervisorCpuError::SetDebugRegs(anyhow!( + "Support 4 breakpoints at most but {} addresses are passed", + addrs.len() + ))); + } + + let mut dbg = kvm_guest_debug { + control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP, + ..Default::default() + }; + if singlestep { + dbg.control |= KVM_GUESTDBG_SINGLESTEP; + } + + // Set bits 9 and 10. + // bit 9: GE (global exact breakpoint enable) flag. + // bit 10: always 1. + dbg.arch.debugreg[7] = 0x0600; + + for (i, addr) in addrs.iter().enumerate() { + dbg.arch.debugreg[i] = addr.0; + // Set global breakpoint enable flag + dbg.arch.debugreg[7] |= 2 << (i * 2); + } + + self.fd + .set_guest_debug(&dbg) + .map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into())) + } #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> { self.fd