improve x86 hw breakpoint handling

This commit is contained in:
Stanislav Shwartsman 2012-07-11 15:07:54 +00:00
parent fef13ddb95
commit ec06475dbf
6 changed files with 45 additions and 45 deletions

View File

@ -546,7 +546,12 @@ void BX_CPU_C::prefetch(void)
// check only if not fetching page cross instruction
// this check is 32-bit wrap safe as well
if (EIP == (Bit32u) BX_CPU_THIS_PTR prev_rip) {
if (code_breakpoint_match(laddr)) exception(BX_DB_EXCEPTION, 0);
Bit32u dr6_bits = code_breakpoint_match(laddr);
if (dr6_bits & BX_DEBUG_TRAP_HIT) {
BX_ERROR(("#DB: x86 code breakpoint catched"));
BX_CPU_THIS_PTR debug_trap |= dr6_bits;
exception(BX_DB_EXCEPTION, 0);
}
}
}
}

View File

@ -1442,12 +1442,7 @@ Bit32u BX_CPU_C::code_breakpoint_match(bx_address laddr)
if (BX_CPU_THIS_PTR dr7.get_bp_enabled()) {
Bit32u dr6_bits = hwdebug_compare(laddr, 1, BX_HWDebugInstruction, BX_HWDebugInstruction);
if (dr6_bits) {
// Add to the list of debug events thus far.
BX_CPU_THIS_PTR debug_trap |= dr6_bits;
BX_ERROR(("#DB: x86 code breakpoint catched"));
return dr6_bits;
}
return dr6_bits;
}
return 0;
@ -1466,7 +1461,10 @@ void BX_CPU_C::hwbreakpoint_match(bx_address laddr, unsigned len, unsigned rw)
Bit32u dr6_bits = hwdebug_compare(laddr, len, opa, opb);
if (dr6_bits) {
BX_CPU_THIS_PTR debug_trap |= dr6_bits;
BX_CPU_THIS_PTR async_event = 1;
if (BX_CPU_THIS_PTR debug_trap & BX_DEBUG_TRAP_HIT) {
BX_ERROR(("#DB: Code/Data breakpoint hit - report debug trap on next instruction"));
BX_CPU_THIS_PTR async_event = 1;
}
}
}
}
@ -1482,7 +1480,13 @@ Bit32u BX_CPU_C::hwdebug_compare(bx_address laddr_0, unsigned size,
bx_address laddr_n = laddr_0 + (size - 1);
Bit32u dr_op[4], dr_len[4];
bx_bool ibpoint_found_n[4], ibpoint_found = 0;
// If *any* enabled breakpoints matched, then we need to
// set status bits for *all* breakpoints, even disabled ones,
// as long as they meet the other breakpoint criteria.
// dr6_mask is the return value. These bits represent the bits
// to be OR'd into DR6 as a result of the debug event.
Bit32u dr6_mask = 0;
dr_len[0] = BX_CPU_THIS_PTR dr7.get_LEN0();
dr_len[1] = BX_CPU_THIS_PTR dr7.get_LEN1();
@ -1497,33 +1501,19 @@ Bit32u BX_CPU_C::hwdebug_compare(bx_address laddr_0, unsigned size,
for (unsigned n=0;n<4;n++) {
bx_address dr_start = BX_CPU_THIS_PTR dr[n] & ~alignment_mask[dr_len[n]];
bx_address dr_end = dr_start + alignment_mask[dr_len[n]];
ibpoint_found_n[n] = 0;
// See if this instruction address matches any breakpoints
if (dr7 & (3 << n*2)) {
if ((dr_op[n]==opa || dr_op[n]==opb) &&
(laddr_0 <= dr_end) &&
(laddr_n >= dr_start)) {
ibpoint_found_n[n] = 1;
ibpoint_found = 1;
if ((dr_op[n]==opa || dr_op[n]==opb) &&
(laddr_0 <= dr_end) &&
(laddr_n >= dr_start)) {
dr6_mask |= (1<<n);
// tell if breakpoint was enabled
if (dr7 & (3 << n*2)) {
dr6_mask |= BX_DEBUG_TRAP_HIT;
}
}
}
// If *any* enabled breakpoints matched, then we need to
// set status bits for *all* breakpoints, even disabled ones,
// as long as they meet the other breakpoint criteria.
// dr6_mask is the return value. These bits represent the bits
// to be OR'd into DR6 as a result of the debug event.
Bit32u dr6_mask = 0;
if (ibpoint_found) {
if (ibpoint_found_n[0]) dr6_mask |= 0x1;
if (ibpoint_found_n[1]) dr6_mask |= 0x2;
if (ibpoint_found_n[2]) dr6_mask |= 0x4;
if (ibpoint_found_n[3]) dr6_mask |= 0x8;
}
return dr6_mask;
}
@ -1536,7 +1526,10 @@ void BX_CPU_C::iobreakpoint_match(unsigned port, unsigned len)
Bit32u dr6_bits = hwdebug_compare(port, len, BX_HWDebugIO, BX_HWDebugIO);
if (dr6_bits) {
BX_CPU_THIS_PTR debug_trap |= dr6_bits;
BX_CPU_THIS_PTR async_event = 1;
if (BX_CPU_THIS_PTR debug_trap & BX_DEBUG_TRAP_HIT) {
BX_ERROR(("#DB: I/O breakpoint hit - report debug trap on next instruction"));
BX_CPU_THIS_PTR async_event = 1;
}
}
}
}

View File

@ -148,6 +148,7 @@ struct bx_dr6_t {
IMPLEMENT_CRREG_ACCESSORS(B2, 2);
IMPLEMENT_CRREG_ACCESSORS(B3, 3);
#define BX_DEBUG_TRAP_HIT (1 << 12)
#define BX_DEBUG_DR_ACCESS_BIT (1 << 13)
#define BX_DEBUG_SINGLE_STEP_BIT (1 << 14)
#define BX_DEBUG_TRAP_TASK_SWITCH_BIT (1 << 15)

View File

@ -180,7 +180,7 @@ bx_bool BX_CPU_C::handleAsyncEvent(void)
// ignored and discarded if GIF == 0
// debug traps due to EFLAGS.TF remain untouched
if (! BX_CPU_THIS_PTR svm_gif)
BX_CPU_THIS_PTR debug_trap &= ~BX_DEBUG_SINGLE_STEP_BIT;
BX_CPU_THIS_PTR debug_trap &= BX_DEBUG_SINGLE_STEP_BIT;
#endif
// Priority 2: Trap on Task Switch
@ -233,12 +233,17 @@ bx_bool BX_CPU_C::handleAsyncEvent(void)
// Breakpoints
// Debug Trap Exceptions (TF flag set or data/IO breakpoint)
if (! interrupts_inhibited(BX_INHIBIT_DEBUG)) {
// A trap may be inhibited on this boundary due to an instruction which loaded SS.
// A trap may be inhibited on this boundary due to an instruction which loaded SS
#if BX_X86_DEBUGGER
code_breakpoint_match(get_laddr(BX_SEG_REG_CS, BX_CPU_THIS_PTR prev_rip));
// Pages with code breakpoints always have async_event=1 and therefore come here
BX_CPU_THIS_PTR debug_trap |= code_breakpoint_match(get_laddr(BX_SEG_REG_CS, BX_CPU_THIS_PTR prev_rip));
#endif
if (BX_CPU_THIS_PTR debug_trap)
if (BX_CPU_THIS_PTR debug_trap & 0xf000) {
exception(BX_DB_EXCEPTION, 0); // no error, not interrupt
}
else {
BX_CPU_THIS_PTR debug_trap = 0;
}
}
// Priority 5: External Interrupts

View File

@ -900,15 +900,9 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code)
if (vector == BX_DB_EXCEPTION) {
// Commit debug events to DR6
#if BX_CPU_LEVEL <= 4
// On 386/486 bit12 is settable
BX_CPU_THIS_PTR dr6.val32 = (BX_CPU_THIS_PTR dr6.val32 & 0xffff0ff0) |
(BX_CPU_THIS_PTR debug_trap & 0x0000f00f);
#else
// On Pentium+, bit12 is always zero
BX_CPU_THIS_PTR dr6.val32 = (BX_CPU_THIS_PTR dr6.val32 & 0xffff0ff0) |
(BX_CPU_THIS_PTR debug_trap & 0x0000e00f);
#endif
// clear GD flag in the DR7 prior entering debug exception handler
BX_CPU_THIS_PTR dr7.set_GD(0);
}

View File

@ -1613,8 +1613,10 @@ Bit32u BX_CPU_C::VMenterLoadCheckGuestState(Bit64u *qualification)
BX_CPU_THIS_PTR debug_trap = guest.tmpDR6 & 0x0000400F;
else
BX_CPU_THIS_PTR debug_trap = guest.tmpDR6 & 0x00004000;
if (BX_CPU_THIS_PTR debug_trap)
if (BX_CPU_THIS_PTR debug_trap) {
BX_CPU_THIS_PTR debug_trap |= BX_DEBUG_TRAP_HIT;
BX_CPU_THIS_PTR async_event = 1;
}
if (guest.interruptibility_state & BX_VMX_INTERRUPTS_BLOCKED_BY_STI)
inhibit_interrupts(BX_INHIBIT_INTERRUPTS);
@ -1855,9 +1857,9 @@ void BX_CPU_C::VMexitSaveGuestState(void)
#endif
#endif
Bit32u tmpDR6 = BX_CPU_THIS_PTR debug_trap;
Bit32u tmpDR6 = BX_CPU_THIS_PTR debug_trap & 0x0000400f;
if (tmpDR6 & 0xf) tmpDR6 |= (1 << 12);
VMwrite_natural(VMCS_GUEST_PENDING_DBG_EXCEPTIONS, tmpDR6 & 0x0000500f);
VMwrite_natural(VMCS_GUEST_PENDING_DBG_EXCEPTIONS, tmpDR6);
Bit32u interruptibility_state = 0;
if (interrupts_inhibited(BX_INHIBIT_INTERRUPTS)) {