* 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:
Ingo Weinhold
2005-03-02 00:14:38 +00:00
parent 86cc722477
commit 5d2536d2af
+239 -11
View File
@@ -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;
}