#[cfg(mshv2)]
extern crate mshv_bindings2 as mshv_bindings;
#[cfg(mshv2)]
extern crate mshv_ioctls2 as mshv_ioctls;
#[cfg(mshv3)]
extern crate mshv_bindings3 as mshv_bindings;
#[cfg(mshv3)]
extern crate mshv_ioctls3 as mshv_ioctls;
use std::collections::HashMap;
use mshv_bindings::{
DebugRegisters, StandardRegisters, HV_TRANSLATE_GVA_VALIDATE_READ,
HV_TRANSLATE_GVA_VALIDATE_WRITE,
};
use mshv_ioctls::VcpuFd;
use super::{
GuestDebug, VcpuStopReason, X86_64Regs, DR6_BS_FLAG_MASK, DR6_HW_BP_FLAGS_MASK,
MAX_NO_OF_HW_BP, SW_BP_SIZE,
};
use crate::{new_error, HyperlightError, Result};
#[derive(Debug, Default)]
pub(crate) struct MshvDebug {
single_step: bool,
hw_breakpoints: Vec<u64>,
sw_breakpoints: HashMap<u64, [u8; SW_BP_SIZE]>,
dbg_cfg: DebugRegisters,
}
impl MshvDebug {
pub(crate) fn new() -> Self {
Self {
single_step: false,
hw_breakpoints: vec![],
sw_breakpoints: HashMap::new(),
dbg_cfg: DebugRegisters::default(),
}
}
fn get_instruction_pointer(&self, vcpu_fd: &VcpuFd) -> Result<u64> {
let regs = vcpu_fd
.get_regs()
.map_err(|e| new_error!("Could not retrieve registers from vCPU: {:?}", e))?;
Ok(regs.rip)
}
fn set_debug_config(&mut self, vcpu_fd: &VcpuFd, step: bool) -> Result<()> {
let addrs = &self.hw_breakpoints;
let mut dbg_cfg = DebugRegisters::default();
for (k, addr) in addrs.iter().enumerate() {
match k {
0 => {
dbg_cfg.dr0 = *addr;
}
1 => {
dbg_cfg.dr1 = *addr;
}
2 => {
dbg_cfg.dr2 = *addr;
}
3 => {
dbg_cfg.dr3 = *addr;
}
_ => {
Err(new_error!("Tried to set more than 4 HW breakpoints"))?;
}
}
dbg_cfg.dr7 |= 1 << (k * 2);
}
self.dbg_cfg = dbg_cfg;
vcpu_fd
.set_debug_regs(&self.dbg_cfg)
.map_err(|e| new_error!("Could not set guest debug: {:?}", e))?;
self.single_step = step;
let mut regs = vcpu_fd
.get_regs()
.map_err(|e| new_error!("Could not get registers: {:?}", e))?;
if self.single_step {
regs.rflags |= 1 << 8;
} else {
regs.rflags &= !(1 << 8);
}
vcpu_fd
.set_regs(®s)
.map_err(|e| new_error!("Could not set registers: {:?}", e))?;
Ok(())
}
pub(crate) fn get_stop_reason(
&mut self,
vcpu_fd: &VcpuFd,
entrypoint: u64,
) -> Result<VcpuStopReason> {
let regs = vcpu_fd
.get_debug_regs()
.map_err(|e| new_error!("Cannot retrieve debug registers from vCPU: {}", e))?;
let debug_status = regs.dr6;
if debug_status & DR6_BS_FLAG_MASK != 0 && self.single_step {
return Ok(VcpuStopReason::DoneStep);
}
let ip = self.get_instruction_pointer(vcpu_fd)?;
let gpa = self.translate_gva(vcpu_fd, ip)?;
if debug_status & DR6_HW_BP_FLAGS_MASK != 0 && self.hw_breakpoints.contains(&gpa) {
if gpa == entrypoint {
self.remove_hw_breakpoint(vcpu_fd, entrypoint)?;
}
return Ok(VcpuStopReason::HwBp);
}
if self.sw_breakpoints.contains_key(&gpa) {
return Ok(VcpuStopReason::SwBp);
}
Ok(VcpuStopReason::Unknown)
}
}
impl GuestDebug for MshvDebug {
type Vcpu = VcpuFd;
fn is_hw_breakpoint(&self, addr: &u64) -> bool {
self.hw_breakpoints.contains(addr)
}
fn is_sw_breakpoint(&self, addr: &u64) -> bool {
self.sw_breakpoints.contains_key(addr)
}
fn save_hw_breakpoint(&mut self, addr: &u64) -> bool {
if self.hw_breakpoints.len() >= MAX_NO_OF_HW_BP {
false
} else {
self.hw_breakpoints.push(*addr);
true
}
}
fn save_sw_breakpoint_data(&mut self, addr: u64, data: [u8; 1]) {
_ = self.sw_breakpoints.insert(addr, data);
}
fn delete_hw_breakpoint(&mut self, addr: &u64) {
self.hw_breakpoints.retain(|&a| a != *addr);
}
fn delete_sw_breakpoint_data(&mut self, addr: &u64) -> Option<[u8; 1]> {
self.sw_breakpoints.remove(addr)
}
fn read_regs(&self, vcpu_fd: &Self::Vcpu, regs: &mut X86_64Regs) -> Result<()> {
log::debug!("Read registers");
let vcpu_regs = vcpu_fd
.get_regs()
.map_err(|e| new_error!("Could not read guest registers: {:?}", e))?;
regs.rax = vcpu_regs.rax;
regs.rbx = vcpu_regs.rbx;
regs.rcx = vcpu_regs.rcx;
regs.rdx = vcpu_regs.rdx;
regs.rsi = vcpu_regs.rsi;
regs.rdi = vcpu_regs.rdi;
regs.rbp = vcpu_regs.rbp;
regs.rsp = vcpu_regs.rsp;
regs.r8 = vcpu_regs.r8;
regs.r9 = vcpu_regs.r9;
regs.r10 = vcpu_regs.r10;
regs.r11 = vcpu_regs.r11;
regs.r12 = vcpu_regs.r12;
regs.r13 = vcpu_regs.r13;
regs.r14 = vcpu_regs.r14;
regs.r15 = vcpu_regs.r15;
regs.rip = vcpu_regs.rip;
regs.rflags = vcpu_regs.rflags;
Ok(())
}
fn set_single_step(&mut self, vcpu_fd: &Self::Vcpu, enable: bool) -> Result<()> {
self.set_debug_config(vcpu_fd, enable)
}
fn translate_gva(&self, vcpu_fd: &Self::Vcpu, gva: u64) -> Result<u64> {
let flags = (HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_WRITE) as u64;
let (addr, _) = vcpu_fd
.translate_gva(gva, flags)
.map_err(|_| HyperlightError::TranslateGuestAddress(gva))?;
Ok(addr)
}
fn write_regs(&self, vcpu_fd: &Self::Vcpu, regs: &X86_64Regs) -> Result<()> {
log::debug!("Write registers");
let new_regs = StandardRegisters {
rax: regs.rax,
rbx: regs.rbx,
rcx: regs.rcx,
rdx: regs.rdx,
rsi: regs.rsi,
rdi: regs.rdi,
rbp: regs.rbp,
rsp: regs.rsp,
r8: regs.r8,
r9: regs.r9,
r10: regs.r10,
r11: regs.r11,
r12: regs.r12,
r13: regs.r13,
r14: regs.r14,
r15: regs.r15,
rip: regs.rip,
rflags: regs.rflags,
};
vcpu_fd
.set_regs(&new_regs)
.map_err(|e| new_error!("Could not write guest registers: {:?}", e))
}
}