diff --git a/src/kernel/core/arch/x86/arch_user_debugger.cpp b/src/kernel/core/arch/x86/arch_user_debugger.cpp index 917ee8fcd7..85a2fe6617 100644 --- a/src/kernel/core/arch/x86/arch_user_debugger.cpp +++ b/src/kernel/core/arch/x86/arch_user_debugger.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -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; +}