Skip to content

Commit edd59e9

Browse files
committed
gdb: move vcpu arch specific stop reason to different file
- update kvm_debug to use this hypervisor independent function Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent 6b1454c commit edd59e9

File tree

4 files changed

+93
-41
lines changed

4 files changed

+93
-41
lines changed

src/hyperlight_host/src/hypervisor/gdb/arch.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ limitations under the License.
1616

1717
//! This file contains architecture specific code for the x86_64
1818
19+
use std::collections::HashMap;
20+
21+
use super::VcpuStopReason;
22+
23+
// Described in Table 6-1. Exceptions and Interrupts at Page 6-13 Vol. 1
24+
// of Intel 64 and IA-32 Architectures Software Developer's Manual
25+
/// Exception id for #DB
26+
const DB_EX_ID: u32 = 1;
27+
/// Exception id for #BP - triggered by the INT3 instruction
28+
const BP_EX_ID: u32 = 3;
29+
1930
/// Software Breakpoint size in memory
2031
pub(crate) const SW_BP_SIZE: usize = 1;
2132
/// Software Breakpoint opcode - INT3
@@ -37,3 +48,64 @@ pub(crate) const DR6_BS_FLAG_MASK: u64 = 1 << DR6_BS_FLAG_POS;
3748
pub(crate) const DR6_HW_BP_FLAGS_POS: usize = 0;
3849
/// Bit mask of HW breakpoints status in DR6 debug register
3950
pub(crate) const DR6_HW_BP_FLAGS_MASK: u64 = 0x0F << DR6_HW_BP_FLAGS_POS;
51+
52+
/// Determine the reason the vCPU stopped
53+
/// This is done by checking the DR6 register and the exception id
54+
/// NOTE: Additional checks are done for the entrypoint, stored hw_breakpoints
55+
/// and sw_breakpoints to ensure the stop reason is valid with internal state
56+
pub(crate) fn vcpu_stop_reason(
57+
single_step: bool,
58+
rip: u64,
59+
dr6: u64,
60+
entrypoint: u64,
61+
exception: u32,
62+
hw_breakpoints: &[u64],
63+
sw_breakpoints: &HashMap<u64, [u8; SW_BP_SIZE]>,
64+
) -> VcpuStopReason {
65+
if DB_EX_ID == exception {
66+
// If the BS flag in DR6 register is set, it means a single step
67+
// instruction triggered the exit
68+
// Check page 19-4 Vol. 3B of Intel 64 and IA-32
69+
// Architectures Software Developer's Manual
70+
if dr6 & DR6_BS_FLAG_MASK != 0 && single_step {
71+
return VcpuStopReason::DoneStep;
72+
}
73+
74+
// If any of the B0-B3 flags in DR6 register is set, it means a
75+
// hardware breakpoint triggered the exit
76+
// Check page 19-4 Vol. 3B of Intel 64 and IA-32
77+
// Architectures Software Developer's Manual
78+
if DR6_HW_BP_FLAGS_MASK & dr6 != 0 && hw_breakpoints.contains(&rip) {
79+
if rip == entrypoint {
80+
return VcpuStopReason::EntryPointBp;
81+
}
82+
return VcpuStopReason::HwBp;
83+
}
84+
}
85+
86+
if BP_EX_ID == exception && sw_breakpoints.contains_key(&rip) {
87+
return VcpuStopReason::SwBp;
88+
}
89+
90+
// Log an error and provide internal debugging info
91+
log::error!(
92+
r"The vCPU exited because of an unknown reason:
93+
single_step: {:?}
94+
rip: {:?}
95+
dr6: {:?}
96+
entrypoint: {:?}
97+
exception: {:?}
98+
hw_breakpoints: {:?}
99+
sw_breakpoints: {:?}
100+
",
101+
single_step,
102+
rip,
103+
dr6,
104+
entrypoint,
105+
exception,
106+
hw_breakpoints,
107+
sw_breakpoints,
108+
);
109+
110+
VcpuStopReason::Unknown
111+
}

src/hyperlight_host/src/hypervisor/gdb/event_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
4949
// Resume execution if unknown reason for stop
5050
let stop_response = match stop_reason {
5151
VcpuStopReason::DoneStep => BaseStopReason::DoneStep,
52+
VcpuStopReason::EntryPointBp => BaseStopReason::HwBreak(()),
5253
VcpuStopReason::SwBp => BaseStopReason::SwBreak(()),
5354
VcpuStopReason::HwBp => BaseStopReason::HwBreak(()),
5455
// This is a consequence of the GDB client sending an interrupt signal

src/hyperlight_host/src/hypervisor/gdb/kvm_debug.rs

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@ use kvm_bindings::{
2222
};
2323
use kvm_ioctls::VcpuFd;
2424

25-
use super::arch::{DR6_BS_FLAG_MASK, DR6_HW_BP_FLAGS_MASK, MAX_NO_OF_HW_BP, SW_BP_SIZE};
25+
use super::arch::{vcpu_stop_reason, MAX_NO_OF_HW_BP, SW_BP_SIZE};
2626
use super::{GuestDebug, VcpuStopReason, X86_64Regs};
2727
use crate::{new_error, HyperlightError, Result};
2828

29-
/// Exception id for SW breakpoint
30-
const SW_BP_ID: u32 = 3;
31-
3229
/// KVM Debug struct
3330
/// This struct is used to abstract the internal details of the kvm
3431
/// guest debugging settings
@@ -116,51 +113,29 @@ impl KvmDebug {
116113
debug_exit: kvm_debug_exit_arch,
117114
entrypoint: u64,
118115
) -> Result<VcpuStopReason> {
119-
// If the BS flag in DR6 register is set, it means a single step
120-
// instruction triggered the exit
121-
// Check page 19-4 Vol. 3B of Intel 64 and IA-32
122-
// Architectures Software Developer's Manual
123-
if debug_exit.dr6 & DR6_BS_FLAG_MASK != 0 && self.single_step {
124-
return Ok(VcpuStopReason::DoneStep);
125-
}
116+
let rip = self.get_instruction_pointer(vcpu_fd)?;
117+
let rip = self.translate_gva(vcpu_fd, rip)?;
126118

127-
let ip = self.get_instruction_pointer(vcpu_fd)?;
128-
let gpa = self.translate_gva(vcpu_fd, ip)?;
119+
// Check if the vCPU stopped because of a hardware breakpoint
120+
let reason = vcpu_stop_reason(
121+
self.single_step,
122+
rip,
123+
debug_exit.dr6,
124+
entrypoint,
125+
debug_exit.exception,
126+
&self.hw_breakpoints,
127+
&self.sw_breakpoints,
128+
);
129129

130-
// If any of the B0-B3 flags in DR6 register is set, it means a
131-
// hardware breakpoint triggered the exit
132-
// Check page 19-4 Vol. 3B of Intel 64 and IA-32
133-
// Architectures Software Developer's Manual
134-
if DR6_HW_BP_FLAGS_MASK & debug_exit.dr6 != 0 && self.hw_breakpoints.contains(&gpa) {
130+
if let VcpuStopReason::EntryPointBp = reason {
135131
// In case the hw breakpoint is the entry point, remove it to
136132
// avoid hanging here as gdb does not remove breakpoints it
137133
// has not set.
138134
// Gdb expects the target to be stopped when connected.
139-
if gpa == entrypoint {
140-
self.remove_hw_breakpoint(vcpu_fd, entrypoint)?;
141-
}
142-
return Ok(VcpuStopReason::HwBp);
135+
self.remove_hw_breakpoint(vcpu_fd, entrypoint)?;
143136
}
144137

145-
// If the exception ID matches #BP (3) - it means a software breakpoint
146-
// caused the exit
147-
if SW_BP_ID == debug_exit.exception && self.sw_breakpoints.contains_key(&gpa) {
148-
return Ok(VcpuStopReason::SwBp);
149-
}
150-
151-
// Log an error and provide internal debugging info for fixing
152-
log::error!(
153-
r"The vCPU exited because of an unknown reason:
154-
kvm_debug_exit_arch: {:?}
155-
single_step: {:?}
156-
hw_breakpoints: {:?}
157-
sw_breakpoints: {:?}",
158-
debug_exit,
159-
self.single_step,
160-
self.hw_breakpoints,
161-
self.sw_breakpoints,
162-
);
163-
Ok(VcpuStopReason::Unknown)
138+
Ok(reason)
164139
}
165140
}
166141

src/hyperlight_host/src/hypervisor/gdb/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ pub(crate) struct X86_64Regs {
109109
#[derive(Debug)]
110110
pub enum VcpuStopReason {
111111
DoneStep,
112+
/// Hardware breakpoint inserted by the hypervisor so the guest can be stopped
113+
/// at the entry point. This is used to avoid the guest from executing
114+
/// the entry point code before the debugger is connected
115+
EntryPointBp,
112116
HwBp,
113117
SwBp,
114118
Interrupt,

0 commit comments

Comments
 (0)