* Added functions for clearing/destroying the arch-specific thread debug
info. * Implemented arch_set_cpu_state() (Axel, please check, what registers the debugger should actually be allowed to modify). * Fixed arch_get_cpu_state(). We now explicitly copy the registers manually instead of using memcpy(). Added support for extended registers (FPU,...). * Completed break- and watchpoint support. * Implemented handling of debug and breakpoint exceptions. The x86 specific part of the userland debugging support should be complete now. That is in theory the entire debugging interface is available on x86 machines now. It's almost completely untested yet, though. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11526 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <debugger.h>
|
||||
#include <int.h>
|
||||
#include <thread.h>
|
||||
#include <arch/user_debugger.h>
|
||||
|
||||
@@ -33,6 +34,11 @@ static const uint32 sDR7L[4] = {
|
||||
X86_DR7_L0, X86_DR7_L1, X86_DR7_L2, X86_DR7_L3
|
||||
};
|
||||
|
||||
// maps breakpoint slot index to B_i bit number
|
||||
static const uint32 sDR6B[4] = {
|
||||
X86_DR6_B0, X86_DR6_B1, X86_DR6_B2, X86_DR6_B3
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
arch_clear_team_debug_info(struct arch_team_debug_info *info)
|
||||
@@ -51,17 +57,82 @@ arch_destroy_team_debug_info(struct arch_team_debug_info *info)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_clear_thread_debug_info(struct arch_thread_debug_info *info)
|
||||
{
|
||||
info->flags = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_destroy_thread_debug_info(struct arch_thread_debug_info *info)
|
||||
{
|
||||
arch_clear_thread_debug_info(info);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_set_debug_cpu_state(const struct debug_cpu_state *cpuState)
|
||||
{
|
||||
// ToDo: Implement!
|
||||
if (struct iframe *frame = i386_get_user_iframe()) {
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
|
||||
i386_frstor(cpuState->extended_regs);
|
||||
// For this to be correct the calling function must not use these
|
||||
// registers (not even indirectly).
|
||||
|
||||
frame->gs = cpuState->gs;
|
||||
frame->fs = cpuState->fs;
|
||||
frame->es = cpuState->es;
|
||||
frame->ds = cpuState->ds;
|
||||
frame->edi = cpuState->edi;
|
||||
frame->esi = cpuState->esi;
|
||||
frame->ebp = cpuState->ebp;
|
||||
frame->esp = cpuState->esp;
|
||||
frame->ebx = cpuState->ebx;
|
||||
frame->edx = cpuState->edx;
|
||||
frame->ecx = cpuState->ecx;
|
||||
frame->eax = cpuState->eax;
|
||||
// frame->vector = cpuState->vector;
|
||||
// frame->error_code = cpuState->error_code;
|
||||
frame->eip = cpuState->eip;
|
||||
frame->cs = cpuState->cs;
|
||||
// frame->eflags = cpuState->flags;
|
||||
frame->user_esp = cpuState->user_esp;
|
||||
frame->user_ss = cpuState->user_ss;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arch_get_debug_cpu_state(struct debug_cpu_state *cpuState)
|
||||
{
|
||||
struct iframe *frame = i386_get_current_iframe();
|
||||
memcpy(cpuState, frame, sizeof(debug_cpu_state));
|
||||
if (struct iframe *frame = i386_get_user_iframe()) {
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
|
||||
i386_fsave(cpuState->extended_regs);
|
||||
// For this to be correct the calling function must not use these
|
||||
// registers (not even indirectly).
|
||||
|
||||
cpuState->gs = frame->gs;
|
||||
cpuState->fs = frame->fs;
|
||||
cpuState->es = frame->es;
|
||||
cpuState->ds = frame->ds;
|
||||
cpuState->edi = frame->edi;
|
||||
cpuState->esi = frame->esi;
|
||||
cpuState->ebp = frame->ebp;
|
||||
cpuState->esp = frame->esp;
|
||||
cpuState->ebx = frame->ebx;
|
||||
cpuState->edx = frame->orig_edx;
|
||||
cpuState->ecx = frame->ecx;
|
||||
cpuState->eax = frame->orig_eax;
|
||||
cpuState->vector = frame->vector;
|
||||
cpuState->error_code = frame->error_code;
|
||||
cpuState->eip = frame->eip;
|
||||
cpuState->cs = frame->cs;
|
||||
cpuState->eflags = frame->flags;
|
||||
cpuState->user_esp = frame->user_esp;
|
||||
cpuState->user_ss = frame->user_ss;
|
||||
}
|
||||
}
|
||||
|
||||
static status_t
|
||||
@@ -108,14 +179,6 @@ set_breakpoint(void *address, uint32 type, uint32 length)
|
||||
info.dr7 |= (length << sDR7Len[slot])
|
||||
| (type << sDR7RW[slot])
|
||||
| (1 << sDR7L[slot]);
|
||||
|
||||
// set the respective debug address register (DR0-DR3)
|
||||
switch (slot) {
|
||||
case 0: asm("movl %0, %%dr0" : : "r"(address)); break;
|
||||
case 1: asm("movl %0, %%dr1" : : "r"(address)); break;
|
||||
case 2: asm("movl %0, %%dr2" : : "r"(address)); break;
|
||||
case 3: asm("movl %0, %%dr3" : : "r"(address)); break;
|
||||
}
|
||||
} else {
|
||||
if (type == X86_INSTRUCTION_BREAKPOINT)
|
||||
error = B_NO_MORE_BREAKPOINTS;
|
||||
@@ -242,3 +305,168 @@ arch_clear_watchpoint(void *address)
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
install_breakpoints(const arch_team_debug_info &teamInfo)
|
||||
{
|
||||
// set breakpoints
|
||||
asm("movl %0, %%dr0" : : "r"(teamInfo.breakpoints[0].address));
|
||||
asm("movl %0, %%dr1" : : "r"(teamInfo.breakpoints[1].address));
|
||||
asm("movl %0, %%dr2" : : "r"(teamInfo.breakpoints[2].address));
|
||||
// asm("movl %0, %%dr3" : : "r"(teamInfo.breakpoints[3].address));
|
||||
// DR3 is used to hold the current struct thread*.
|
||||
|
||||
// enable breakpoints
|
||||
asm("movl %0, %%dr7" : : "r"(teamInfo.dr7));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interrupts are enabled.
|
||||
*/
|
||||
void
|
||||
i386_init_user_debug_at_kernel_exit(struct iframe *frame)
|
||||
{
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||
|
||||
arch_team_debug_info &teamInfo = thread->team->debug_info.arch_info;
|
||||
|
||||
// install the breakpoints
|
||||
install_breakpoints(teamInfo);
|
||||
thread->debug_info.arch_info.flags |= X86_THREAD_DEBUG_DR7_SET;
|
||||
|
||||
// set/clear TF in EFLAGS depending on if single stepping is desired
|
||||
if (thread->debug_info.flags & B_THREAD_DEBUG_SINGLE_STEP)
|
||||
frame->flags |= (1 << X86_EFLAGS_TF);
|
||||
else
|
||||
frame->flags &= ~(1 << X86_EFLAGS_TF);
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interrupts may be enabled.
|
||||
*/
|
||||
void
|
||||
i386_exit_user_debug_at_kernel_entry()
|
||||
{
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
// disable breakpoints
|
||||
asm("movl %0, %%dr7" : : "r"(X86_BREAKPOINTS_DISABLED_DR7));
|
||||
thread->debug_info.arch_info.flags &= ~X86_THREAD_DEBUG_DR7_SET;
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interrupts are disabled and the thread lock is being held.
|
||||
*/
|
||||
void
|
||||
i386_reinit_user_debug_after_context_switch(struct thread *thread)
|
||||
{
|
||||
if (thread->debug_info.arch_info.flags & X86_THREAD_DEBUG_DR7_SET) {
|
||||
GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||
|
||||
install_breakpoints(thread->team->debug_info.arch_info);
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interrupts are disabled and will be enabled by the function.
|
||||
*/
|
||||
int
|
||||
i386_handle_debug_exception(struct iframe *frame)
|
||||
{
|
||||
// get debug status and control registers
|
||||
uint32 dr6, dr7;
|
||||
asm("movl %%dr6, %0" : "=r"(dr6));
|
||||
asm("movl %%dr7, %0" : "=r"(dr7));
|
||||
|
||||
// check, which exception condition applies
|
||||
if (dr6 & X86_DR6_BREAKPOINT_MASK) {
|
||||
// breakpoint
|
||||
|
||||
// check which breakpoint was taken
|
||||
bool watchpoint = true;
|
||||
for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++) {
|
||||
if (dr6 & (1 << sDR6B[i])) {
|
||||
// If it is an instruction breakpoint, we need to set RF in
|
||||
// EFLAGS to prevent triggering the same exception
|
||||
// again (breakpoint instructions are triggered *before*
|
||||
// executing the instruction).
|
||||
uint32 type = (dr7 >> sDR7RW[i]) & 0x3;
|
||||
if (type == X86_INSTRUCTION_BREAKPOINT) {
|
||||
frame->flags |= (1 << X86_EFLAGS_RF);
|
||||
watchpoint = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enable interrupts and notify the debugger
|
||||
enable_interrupts();
|
||||
|
||||
user_debug_break_or_watchpoint_hit(watchpoint);
|
||||
|
||||
} else if (dr6 & X86_DR6_BD) {
|
||||
// general detect exception
|
||||
// Occurs only, if GD in DR7 is set (which we don't do) and someone
|
||||
// tries to write to the debug registers.
|
||||
dprintf("i386_handle_debug_exception(): ignoring spurious general "
|
||||
"detect exception\n");
|
||||
|
||||
enable_interrupts();
|
||||
|
||||
} else if (dr6 & X86_DR6_BS) {
|
||||
// single step
|
||||
|
||||
// enable interrupts and notify the debugger
|
||||
enable_interrupts();
|
||||
|
||||
user_debug_single_stepped();
|
||||
|
||||
} else if (dr6 & X86_DR6_BT) {
|
||||
// task switch
|
||||
// Occurs only, if T in EFLAGS is set (which we don't do).
|
||||
dprintf("i386_handle_debug_exception(): ignoring spurious task switch "
|
||||
"exception\n");
|
||||
|
||||
enable_interrupts();
|
||||
|
||||
} else {
|
||||
dprintf("i386_handle_debug_exception(): ignoring spurious debug "
|
||||
"exception (no condition recognized)\n");
|
||||
|
||||
enable_interrupts();
|
||||
}
|
||||
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interrupts are disabled and will be enabled by the function.
|
||||
*/
|
||||
int
|
||||
i386_handle_breakpoint_exception(struct iframe *frame)
|
||||
{
|
||||
enable_interrupts();
|
||||
|
||||
user_debug_break_or_watchpoint_hit(false);
|
||||
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user