Debug Register Allocation on x86
RFC PATCH 1/1 Debug Register Allocation on x86
Currently, debug register settings in Linux are per-process. This is all that is needed for debugging (user space) processes. However, tools that can be used to debug the kernel (none at present in the stock kernel, but available as external patches like SGI KDB and IBM Dynamic Probes) need debug register settings that are "global" in nature and require them to be preserved across context switches. These facilities currently operate on the presumption that they are the only users of the debug registers. This is clearly wrong and leads to interoperability problems when two or more debugging facilities are active on the system, as they could end up overwriting each other's debug register settings without being aware of it, leading to unexpected results.
Here is a patch to the 2.4.2 kernel that provides interfaces to allocate and free debug registers. This enables the debug registers to be treated as a resource that can shared among multiple facilities, without unexpected behaviour when two facilities are active at the same time.
int dr_alloc(int dr_num_requested, int local_or_global) int dr_free(int dr_num)
This patch helps coordinate the allocation of hardware debug registers amongst multiple debugging facilities, kernel space as well as user space. The interfaces provided can be used to allocate debug registers for both "local" (process-context specific) usage, applied via ptrace by user mode debuggers like gdb, as well as for "global" usage by kernel debugging facilities. Debug registers allocated for local usage can be allocated to multiple processes where as those allocated for global usage can be allocated only to one debug facility at a time. We ensure that global debug register settings are preserved across context switches.
NOTE: The patch, at present is minimalist in nature. It only facilitates different debug tools in selecting debug register(s) that are not in use. It is currently up to the debug tools themselves to handle the actual updates to the registers, propagate them to other processors in an SMP system if required, and to hook into the systems debug exception handler to identify and act on exceptions generated due to their debug registers. Since these requirements exist for any component that uses debug registers, we are considering building on the basic patch to provide these features as well for a more complete debug register management API, if there is sufficient interest.
The watchpoint probes feature in Dynamic Probes v2.0 makes use of the interfaces provided in this patch to set up global watchpoints that work consistently even in the presence of user mode debuggers already using hardware watchpoints. If other debugging facilities like KDB also use this interface to reserve debug registers, then DProbes watchpoints could coexist with kdb watchpoints as well with consistent behaviour.
Kernel changes:
- new dr_alloc/dr_free infrastructure
- ptrace modified to make use of dr_alloc/dr_free
- switch_to now reloads only the debug registers with local watchpoint settings on a task-switch
- signal handling restores the debug register settings only for local debug register settings
- reference count for a debug register having local watchpoint settings is incremented when a process is cloned and decremented when a process exits
Caveats:
- gdb doesn't check for failures in setting debug registers today
Todos:
- we don't yet handle multiple registers being enabled in a single ptrace call (not a problem with gdb today)
- consider allocating registers in the reverse order for global watchpoints (to reduce chances of allocation failures for gdb, which relies on being able to use a fixed debug register)
Kernel debug facilities that could use this:
http://oss.software.ibm.com/developerworks/opensource/linux/projects/dprobes/
http://oss.sgi.com/projects/kdb/
Regards
Bharata B. Rao.
IBM Dynamic Probes Team
arch/i386/config.in +1 -0
@@ -366,4 +366,5 @@ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'x86 Global/Local Debug Register Management (IBM)' CONFIG_DR_ALLOC endmenu
arch/i386/kernel/Makefile +1 -0
@@ -40,5 +40,6 @@ obj-$(CONFIG_X86_LOCAL_APIC) += apic.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o mpparse.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o +obj-$(CONFIG_DR_ALLOC) += dr_alloc.o include $(TOPDIR)/Rules.make
arch/i386/kernel/dr_alloc.c +248 -0
248 lines not on this page · raw message
arch/i386/kernel/process.c +43 -2
81 lines not on this page · raw message
arch/i386/kernel/ptrace.c +51 -0
69 lines not on this page · raw message
arch/i386/kernel/signal.c +15 -1
@@ -22,6 +22,9 @@#include <asm/ucontext.h> #include <asm/uaccess.h> #include <asm/i387.h> +#ifdef CONFIG_DR_ALLOC +#include <asm/dr_alloc.h> +#endif #define DEBUG_SIG 0
@@ -694,7 +697,18 @@* have been cleared if the watchpoint triggered * inside the kernel. */ - __asm__("movl %0,%%db7" : :"r" (current->thread.debugreg[7 ])); +#ifdef CONFIG_DR_ALLOC +{ +/* +* Don't change the global watchpoint bit settings in dr7. +*/ +if (current->thread.debugreg[7 ]) { +write_dr (7 , (read_dr (7 ) & dr7_global_mask) | current->thread.debugreg[7 ]); + } +} +#else + __asm__("movl %0,%%db7" : :"r" (current->thread.debugreg[7 ])); +#endif /* Whee! Actually deliver the signal. */ handle_signal (signr, ka, &info, oldset, regs);
arch/i386/kernel/traps.c +11 -0
@@ -46,6 +46,9 @@#include <asm/cobalt.h> #include <asm/lithium.h> #endif +#ifdef CONFIG_DR_ALLOC +#include <asm/dr_alloc.h> +#endif #include <linux/irq.h>
@@ -566,9 +569,17 @@* the signal is delivered. */ clear_dr7: +#ifdef CONFIG_DR_ALLOC +/* +* Don't clear the entire dr7. Leave out the bits corresponding +* to global watchpoints. +*/ +write_dr (7 , (read_dr (7 ) & dr7_global_mask)); +#else __asm__("movl %0,%%db7" :/* no output */ :"r" (0 )); +#endif return ; debug_vm86:
include/asm-i386/dr_alloc.h +132 -0
132 lines not on this page · raw message