Hardware Breakpoint interfaces
Subsystems (4)
cover Hardware Breakpoint interfaces
Hi All,
In the ensuing mails, please find the patches that provide
interfaces to use Hardware Breakpoint (or Hardware watchpoint registers) for addresses in kernel- and user-space.
This work is primarily based on the patches that were discussed earlier on Linux Kernel Mailing List (http://lkml.org/lkml/2007/3/2/221) through a discussion initiated by Alan Stern and carried forward with extensive help from Roland McGrath (copied). They now ported to the most recent kernel version (2.6.27-rc9) along with changes to the invocation of '@triggered' function (presently split into pre_ and post_handler routines).
These patches add a new feature to the kernel i.e. use HW breakpoint registers to watch kernel-space data (for read/write operations) and kernel instructions for execute operations. The interfaces introduced through these patches for handling HW Breakpoint registers, namely (un)register_<kernel/user>_hw_breakpoint() are intended to serve as core-routines that will interact with the hardware registers directly, while the users of breakpoint registers will do so through these interfaces. Given the large number of such users in the kernel (such as KGDB, KVM for kernel-space), not all of them have been modified to use the proposed interfaces - atleast in this iteration of patches.
The patches are marked as [RFC], meaning that there is plenty of 'work-in-progress' and here is a near-exhaustive list of them:
- Enable KGDB and KVM to use the register_kernel_hw_breakpoint() interface for their HW Breakpoint usage, in the absence of which they will be broken during simultaneous use.
- Stress test the patches to study the behaviour during multiple requests from various sources/requests beyond available number of registers.
- Support multiple handlers for a given breakpoint in kernel- or user-space address.
- Provide complete breakpoint functionality in x86 i.e. include breakpoints on I/O operations.
- Addition of Documentation/hw_bkpt.txt and samples/hw_breakpoint/*.c files for better readability.
- Code re-arrangement to avoid the #include of kernel/hw_breakpoint.c in arch/x86/kernel/hw_breakpoint.c
Thanks,
K.Prasad
RFC PATCH 1/9 Introducing generic hardware breakpoint handler interfaces
This patch introduces two new files hw_breakpoint.[ch] which defines the generic interfaces to use hardware breakpoint infrastructure of the system.
--- include/asm-generic/hw_breakpoint.h | 236 +++++++++++ kernel/hw_breakpoint.c | 731 ++++++++++++++++++++++++++++++++++++ 2 files changed, 967 insertions(+)
include/asm-generic/hw_breakpoint.h new file +236 -0
@@ -0,0 +1,236 @@ +#ifndef _ASM_GENERIC_HW_BREAKPOINT_H +#define _ASM_GENERIC_HW_BREAKPOINT_H + +#ifndef __ARCH_HW_BREAKPOINT_H +#error "Please don't include this file directly" +#endif + +#ifdef __KERNEL__ +#include <linux/list.h> +#include <linux/types.h> + +/** +* struct hw_breakpoint - unified kernel/user-space hardware breakpoint +* @node: internal linked-list management +* @triggered: callback invoked when the breakpoint is hit +* @installed: callback invoked when the breakpoint is installed +* @uninstalled: callback invoked when the breakpoint is uninstalled +* @info: arch-specific breakpoint info (address, length, and type) +* @priority: requested priority level +* @status: current registration/installation status +* +* %hw_breakpoint structures are the kernel's way of representing +* hardware breakpoints. These can be either execute breakpoints +* (triggered on instruction execution) or data breakpoints (also known +* as "watchpoints", triggered on data access), and the breakpoint's +* target address can be located in either kernel space or user space. +* +* The breakpoint's address, length, and type are highly +* architecture-specific. The values are encoded in the @info field; you +* specify them when registering the breakpoint. To examine the encoded +* values use hw_breakpoint_get_{kaddress,uaddress,len,type}(), declared +* below. +* +* The address is specified as a regular kernel pointer (for kernel-space +* breakponts) or as an %__user pointer (for user-space breakpoints). +* With register_user_hw_breakpoint(), the address must refer to a +* location in user space. The breakpoint will be active only while the +* requested task is running. Conversely with +* register_kernel_hw_breakpoint(), the address must refer to a location +* in kernel space, and the breakpoint will be active on all CPUs +* regardless of the current task. +* +* The length is the breakpoint's extent in bytes, which is subject to +* certain limitations. include/asm/hw_breakpoint.h contains macros +* defining the available lengths for a specific architecture. Note that +* the address's alignment must match the length. The breakpoint will +* catch accesses to any byte in the range from address to address + +* (length - 1). +* +* The breakpoint's type indicates the sort of access that will cause it +* to trigger. Possible values may include: +* +* %HW_BREAKPOINT_EXECUTE (triggered on instruction execution), +* %HW_BREAKPOINT_RW (triggered on read or write access), +* %HW_BREAKPOINT_WRITE (triggered on write access), and +* %HW_BREAKPOINT_READ (triggered on read access). +* +* Appropriate macros are defined in include/asm/hw_breakpoint.h; not all +* possibilities are available on all architectures. Execute breakpoints +* must have length equal to the special value %HW_BREAKPOINT_LEN_EXECUTE. +* +* When a breakpoint gets hit, the @triggered callback is invoked +* in_interrupt with a pointer to the %hw_breakpoint structure and the +* processor registers. Execute-breakpoint traps occur before the +* breakpointed instruction runs; when the callback returns the +* instruction is restarted (this time without a debug exception). All +* other types of trap occur after the memory access has taken place. +* Breakpoints are disabled while @triggered runs, to avoid recursive +* traps and allow unhindered access to breakpointed memory. +* +* Hardware breakpoints are implemented using the CPU's debug registers, +* which are a limited hardware resource. Requests to register a +* breakpoint will always succeed provided the parameters are valid, +* but the breakpoint may not be installed in a debug register right +* away. Physical debug registers are allocated based on the priority +* level stored in @priority (higher values indicate higher priority). +* User-space breakpoints within a single thread compete with one +* another, and all user-space breakpoints compete with all kernel-space +* breakpoints; however user-space breakpoints in different threads do +* not compete. %HW_BREAKPOINT_PRIO_PTRACE is the level used for ptrace +* requests; an unobtrusive kernel-space breakpoint will use +* %HW_BREAKPOINT_PRIO_NORMAL to avoid disturbing user programs. A +* kernel-space breakpoint that always wants to be installed and doesn't +* care about disrupting user debugging sessions can specify +* %HW_BREAKPOINT_PRIO_HIGH. +* +* A particular breakpoint may be allocated (installed in) a debug +* register or deallocated (uninstalled) from its debug register at any +* time, as other breakpoints are registered and unregistered. The +* @installed and @uninstalled callbacks are invoked in_atomic when these +* events occur. It is legal for @installed or @uninstalled to be %NULL, +* however @triggered must not be. Note that it is not possible to +* register or unregister a user-space breakpoint from within a callback +* routine, since doing so requires a process context. Note that for user +* breakpoints, while in @installed and @uninstalled the thread may be context +* switched. Hence it may not be safe to call printk(). +* +* For kernel-space breakpoints, @installed is invoked after the +* breakpoint is actually installed and @uninstalled is invoked before +* the breakpoint is actually uninstalled. As a result @triggered can +* be called when you may not expect it, but this way you will know that +* during the time interval from @installed to @uninstalled, all events +* are faithfully reported. (It is not possible to do any better than +* this in general, because on SMP systems there is no way to set a debug +* register simultaneously on all CPUs.) The same isn't always true with +* user-space breakpoints, but the differences should not be visible to a +* user process. +* +* If you need to know whether your kernel-space breakpoint was installed +* immediately upon registration, you can check the return value from +* register_kernel_hw_breakpoint(). If the value is not > 0, you can +* give up and unregister the breakpoint right away. +* +* @node and @status are intended for internal use. However @status +* may be read to determine whether or not the breakpoint is currently +* installed. (The value is not reliable unless local interrupts are +* disabled.) +* +* This sample code sets a breakpoint on pid_max and registers a callback +* function for writes to that variable. Note that it is not portable +* as written, because not all architectures support HW_BREAKPOINT_LEN_4. +* +* ---------------------------------------------------------------------- +* +* #include <asm/hw_breakpoint.h> +* +* static void triggered(struct hw_breakpoint *bp, struct pt_regs *regs) +* { +* printk(KERN_DEBUG "Breakpoint triggered\n"); +* dump_stack(); +* .......<more debugging output>........ +* } +* +* static struct hw_breakpoint my_bp; +* +* static int init_module(void) +* { +* ..........<do anything>............ +* my_bp.triggered = triggered; +* my_bp.priority = HW_BREAKPOINT_PRIO_NORMAL; +* rc = register_kernel_hw_breakpoint(&my_bp, &pid_max, +* HW_BREAKPOINT_LEN_4, HW_BREAKPOINT_WRITE); +* ..........<do anything>............ +* } +* +* static void cleanup_module(void) +* { +* ..........<do anything>............ +* unregister_kernel_hw_breakpoint(&my_bp); +* ..........<do anything>............ +* } +* +* ---------------------------------------------------------------------- +* +*/ +struct hw_breakpoint { +struct list_head node; +void (*pre_handler)(struct hw_breakpoint *, +struct pt_regs *); +void (*post_handler)(struct hw_breakpoint *, +struct pt_regs *); +void (*installed)(struct hw_breakpoint *); +void (*uninstalled)(struct hw_breakpoint *); +struct arch_hw_breakpoint info; +u8 priority; +u8 status; +}; + +/* +* Inline accessor routines to retrieve the arch-specific parts of +* a breakpoint structure: +*/ +static const void *hw_breakpoint_get_kaddress (struct hw_breakpoint *bp); +static const void __user *hw_breakpoint_get_uaddress (struct hw_breakpoint *bp); +static unsigned hw_breakpoint_get_len (struct hw_breakpoint *bp); +static unsigned hw_breakpoint_get_type (struct hw_breakpoint *bp); + +/* +* len and type values are defined in include/asm/hw_breakpoint.h. +* Available values vary according to the architecture. On i386 the +* possibilities are: +* +* HW_BREAKPOINT_LEN_1 +* HW_BREAKPOINT_LEN_2 +* HW_BREAKPOINT_LEN_4 +* HW_BREAKPOINT_LEN_EXECUTE +* HW_BREAKPOINT_RW +* HW_BREAKPOINT_READ +* HW_BREAKPOINT_EXECUTE +* +* On other architectures HW_BREAKPOINT_LEN_8 may be available, and the +* 1-, 2-, and 4-byte lengths may be unavailable. There also may be +* HW_BREAKPOINT_WRITE. You can use #ifdef to check at compile time. +*/ + +/* Standard HW breakpoint priority levels (higher value = higher priority) */ +#define HW_BREAKPOINT_PRIO_NORMAL 25 +#define HW_BREAKPOINT_PRIO_PTRACE 50 +#define HW_BREAKPOINT_PRIO_HIGH 75 + +/* HW breakpoint status values (0 = not registered) */ +#define HW_BREAKPOINT_REGISTERED 1 +#define HW_BREAKPOINT_INSTALLED 2 + +/* +* The following two routines are meant to be called only from within +* the ptrace or utrace subsystems. The tsk argument will usually be a +* process being debugged by the current task, although it is also legal +* for tsk to be the current task. In any case it must be guaranteed +* that tsk will not start running in user mode while its breakpoints are +* being modified. +*/ +int register_user_hw_breakpoint (struct task_struct *tsk, +struct hw_breakpoint *bp, +const void __user *address,unsigned len,unsigned type); +void unregister_user_hw_breakpoint (struct task_struct *tsk, +struct hw_breakpoint *bp); + +/* +* The following routines help the user determine if the architecture supports +* a trigger-before access/execution mechanism or a trigger-after +* access/execution. This is dependent on the type of breakpoint and the +* architecture +*/ +int trigger_before_arch (unsigned type); +int trigger_after_arch (unsigned type); + +/* +* Kernel breakpoints are not associated with any particular thread. +*/ +int register_kernel_hw_breakpoint (struct hw_breakpoint *bp, +const void *address,unsigned len,unsigned type); +void unregister_kernel_hw_breakpoint (struct hw_breakpoint *bp); + +#endif /* __KERNEL__ */ +#endif /* _ASM_GENERIC_HW_BREAKPOINT_H */
kernel/hw_breakpoint.c new file +731 -0
731 lines not on this page · raw message
Re: [RFC Patch 1/9] Introducing generic hardware breakpoint handler interfaces
Quoted 2 lines · K.Prasad
This patch introduces two new files hw_breakpoint.[ch] which defines the generic interfaces to use hardware breakpoint infrastructure of the system.
...
Quoted 8 lines
include/asm-generic/hw_breakpoint.h new file +5
-0@@ -0,0 +1,236 @@ +#ifndef _ASM_GENERIC_HW_BREAKPOINT_H +#define _ASM_GENERIC_HW_BREAKPOINT_H + +#ifndef __ARCH_HW_BREAKPOINT_H +#error "Please don't include this file directly"
include/asm-generic/hw_breakpoint.h line 15
Quoted 10 lines
include/asm-generic/hw_breakpoint.h new file +10
-0+#endif + +#ifdef __KERNEL__ +#include <linux/list.h> +#include <linux/types.h> + +/** +* struct hw_breakpoint - unified kernel/user-space hardware breakpoint +* @node: internal linked-list management +* @triggered: callback invoked when the breakpoint is hit
The kerneldoc needs to be updated to match the structure. @triggered doesn't exist any more; instead there are @pre_handler and @post_handler.
...
include/asm-generic/hw_breakpoint.h line 69
Quoted 8 lines
include/asm-generic/hw_breakpoint.h new file +8
-0+* When a breakpoint gets hit, the @triggered callback is invoked +* in_interrupt with a pointer to the %hw_breakpoint structure and the +* processor registers. Execute-breakpoint traps occur before the +* breakpointed instruction runs; when the callback returns the +* instruction is restarted (this time without a debug exception). All +* other types of trap occur after the memory access has taken place. +* Breakpoints are disabled while @triggered runs, to avoid recursive +* traps and allow unhindered access to breakpointed memory.
This paragraph needs to be fixed as well.
...
include/asm-generic/hw_breakpoint.h line 226
Quoted 8 lines
include/asm-generic/hw_breakpoint.h new file +8
-0+/* +* The following routines help the user determine if the architecture supports +* a trigger-before access/execution mechanism or a trigger-after +* access/execution. This is dependent on the type of breakpoint and the +* architecture +*/ +int trigger_before_arch (unsigned type); +int trigger_after_arch (unsigned type);
These routines should have hwbp_ in their name (or something similar). And they don't need to have _arch.
Alan Stern
Re: [RFC Patch 1/9] Introducing generic hardware breakpoint handler interfaces
Quoted 54 lines · Alan Stern
Quoted 2 lines · K.Prasad
This patch introduces two new files hw_breakpoint.[ch] which defines the generic interfaces to use hardware breakpoint infrastructure of the system.
...
Quoted 8 lines
include/asm-generic/hw_breakpoint.h new file +5
-0@@ -0,0 +1,236 @@ +#ifndef _ASM_GENERIC_HW_BREAKPOINT_H +#define _ASM_GENERIC_HW_BREAKPOINT_H + +#ifndef __ARCH_HW_BREAKPOINT_H +#error "Please don't include this file directly"include/asm-generic/hw_breakpoint.h line 15
Quoted 10 lines
include/asm-generic/hw_breakpoint.h new file +10
-0+#endif + +#ifdef __KERNEL__ +#include <linux/list.h> +#include <linux/types.h> + +/** +* struct hw_breakpoint - unified kernel/user-space hardware breakpoint +* @node: internal linked-list management +* @triggered: callback invoked when the breakpoint is hit The kerneldoc needs to be updated to match the structure. @triggered doesn't exist any more; instead there are @pre_handler and @post_handler.
...
include/asm-generic/hw_breakpoint.h line 69
Quoted 8 lines
include/asm-generic/hw_breakpoint.h new file +8
-0+* When a breakpoint gets hit, the @triggered callback is invoked +* in_interrupt with a pointer to the %hw_breakpoint structure and the +* processor registers. Execute-breakpoint traps occur before the +* breakpointed instruction runs; when the callback returns the +* instruction is restarted (this time without a debug exception). All +* other types of trap occur after the memory access has taken place. +* Breakpoints are disabled while @triggered runs, to avoid recursive +* traps and allow unhindered access to breakpointed memory. This paragraph needs to be fixed as well.
...
include/asm-generic/hw_breakpoint.h line 226
Quoted 8 lines
include/asm-generic/hw_breakpoint.h new file +8
-0+/* +* The following routines help the user determine if the architecture supports +* a trigger-before access/execution mechanism or a trigger-after +* access/execution. This is dependent on the type of breakpoint and the +* architecture +*/ +int trigger_before_arch (unsigned type); +int trigger_after_arch (unsigned type);These routines should have
hwbp_in their name (or something similar). And they don't need to have_arch.
Quoted 2 lines
Alan Stern
I agree this part of the patch needs cleanup. Will do so during the next iteration of the patchset. trigger_<before/after>_arch() routines are rendered redundant through the pre_ and post_handler_allowed() routines and I will remove the above two lines too.
Thanks,
K.Prasad
RFC PATCH 2/9 x86 architecture implementation of Hardware Breakpoint interfaces
In reply to K.PrasadThis patch introduces two new files named hw_breakpoint.[ch] inside x86 specific directories. They contain functions which help validate and serve requests for using Hardware Breakpoint registers on x86 processors.
--- arch/x86/kernel/Makefile | 2 arch/x86/kernel/hw_breakpoint.c | 684 ++++++++++++++++++++++++++++++++++++++++ include/asm-x86/hw_breakpoint.h | 121 +++++++ 3 files changed, 806 insertions(+), 1 deletion(-)
arch/x86/kernel/hw_breakpoint.c new file +684 -0
@@ -0,0 +1,684 @@ +/* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Copyright (C) 2007 Alan Stern +* Copyright (C) 2008 IBM Corporation +*/ + +/* +* HW_breakpoint: a unified kernel/user-space hardware breakpoint facility, +* using the CPU's debug registers. +*/ + +#include <linux/init.h> +#include <linux/irqflags.h> +#include <linux/kdebug.h> +#include <linux/kernel.h> +#include <linux/kprobes.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/rculist.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/percpu.h> + +#include <asm/debugreg.h> +#include <asm/hw_breakpoint.h> +#include <asm/processor.h> + +DEFINE_PER_CPU(unsigned int, sstep_reason) = 0; + +/* Arch-specific hook routines */ + +/* +* Install the kernel breakpoints in their debug registers. +*/ +static void arch_install_chbi (struct cpu_hw_breakpoint *chbi) +{ +struct hw_breakpoint **bps; + +/* Don't allow debug exceptions while we update the registers */ +set_debugreg (0UL ,7 ); + chbi->cur_kbpdata =rcu_dereference (cur_kbpdata); + +/* Kernel breakpoints are stored starting in DR0 and going up */ + bps = chbi->cur_kbpdata->bps; +switch (chbi->cur_kbpdata->num_kbps) { +case 4 : +set_debugreg (bps[3 ]->info.address,3 ); +case 3 : +set_debugreg (bps[2 ]->info.address,2 ); +case 2 : +set_debugreg (bps[1 ]->info.address,1 ); +case 1 : +set_debugreg (bps[0 ]->info.address,0 ); + } +/* No need to set DR6 */ +set_debugreg (chbi->cur_kbpdata->mkdr7,7 ); +} + +/* +* Update an out-of-date thread hw_breakpoint info structure. +*/ +static void arch_update_thbi (struct thread_hw_breakpoint *thbi, +struct kernel_bp_data *thr_kbpdata) +{ +int num = thr_kbpdata->num_kbps; + + thbi->tkdr7 = thr_kbpdata->mkdr7 | (thbi->tdr7 & ~kdr7_masks[num]); +} + +/* +* Install the thread breakpoints in their debug registers. +*/ +static void arch_install_thbi (struct thread_hw_breakpoint *thbi) +{ +/* Install the user breakpoints. Kernel breakpoints are stored +* starting in DR0 and going up; there are num_kbps of them. +* User breakpoints are stored starting in DR3 and going down, +* as many as we have room for. +*/ +switch (thbi->num_installed) { +case 4 : +set_debugreg (thbi->tdr[0 ],0 ); +case 3 : +set_debugreg (thbi->tdr[1 ],1 ); +case 2 : +set_debugreg (thbi->tdr[2 ],2 ); +case 1 : +set_debugreg (thbi->tdr[3 ],3 ); + } +/* No need to set DR6 */ +set_debugreg (thbi->tkdr7,7 ); +} + +/* +* Install the debug register values for just the kernel, no thread. +*/ +static void arch_install_none (struct cpu_hw_breakpoint *chbi) +{ +set_debugreg (chbi->cur_kbpdata->mkdr7,7 ); +} + +/* +* Create a new kbpdata entry. +*/ +static void arch_new_kbpdata (struct kernel_bp_data *new_kbpdata) +{ +int num = new_kbpdata->num_kbps; + + new_kbpdata->mkdr7 = kdr7 & (kdr7_masks[num] |DR_GLOBAL_SLOWDOWN ); +} + +/* +* Store a thread breakpoint array entry's address +*/ +static void arch_store_thread_bp_array (struct thread_hw_breakpoint *thbi, +struct hw_breakpoint *bp,int i) +{ + thbi->tdr[i] = bp->info.address; +} + +int pre_handler_allowed (unsigned type) +{ +if (type ==HW_BREAKPOINT_EXECUTE ) +return 1 ; +else +return -EINVAL ; +} + +int post_handler_allowed (unsigned type) +{ +/* We can have a post handler for all types of breakpoints */ +return 1 ; +} + +/* +* Store a breakpoint's encoded address, length, and type. +*/ +static void arch_store_info (struct hw_breakpoint *bp, +unsigned long address,unsigned len,unsigned type) +{ + bp->info.address = address; + bp->info.len = len; + bp->info.type = type; +} + +/* +* Validate the arch-specific HW Breakpoint register settings +*/ +static int arch_validate_hwbkpt_settings (struct hw_breakpoint *bp, +unsigned long address,unsigned len,unsigned int type, +unsigned int *align) +{ +int ret = -EINVAL ; + +switch (type) { +case HW_BREAKPOINT_EXECUTE : +if (len !=HW_BREAKPOINT_LEN_EXECUTE ) +return ret; +break ; +case HW_BREAKPOINT_WRITE : +break ; +case HW_BREAKPOINT_IO : +break ; +case HW_BREAKPOINT_RW : +break ; +default : +return ret; + } + +switch (len) { +case HW_BREAKPOINT_LEN_1 : + *align =0 ; +break ; +case HW_BREAKPOINT_LEN_2 : + *align =1 ; +break ; +case HW_BREAKPOINT_LEN_4 : + *align =3 ; +break ; +default : +return ret; + } + +if ((pre_handler_allowed (type) && (bp->pre_handler)) || + (post_handler_allowed (type) && (bp->post_handler))) { + ret =0 ; +arch_store_info (bp, address, len, type); + } +return ret; +} + +/* +* Check for virtual address in user space. +*/ +static int arch_check_va_in_userspace (unsigned long va, +struct task_struct *tsk) +{ +#ifndef CONFIG_X86_64 +#define TASK_SIZE_OF (t) TASK_SIZE +#endif +return (va <TASK_SIZE_OF (tsk)); +} + +/* +* Check for virtual address in kernel space. +*/ +static int arch_check_va_in_kernelspace (unsigned long va) +{ +#ifndef CONFIG_X86_64 +#define TASK_SIZE64 TASK_SIZE +#endif +return (va >=TASK_SIZE64 ); +} + +/* +* Encode the length, type, Exact, and Enable bits for a particular breakpoint +* as stored in debug register 7. +*/ +static unsigned long encode_dr7 (int drnum,unsigned len,unsigned type) +{ +unsigned long temp; + + temp = (len | type) &0xf ; + temp <<= (DR_CONTROL_SHIFT + drnum *DR_CONTROL_SIZE ); + temp |= (DR_GLOBAL_ENABLE << (drnum *DR_ENABLE_SIZE )) | +DR_GLOBAL_SLOWDOWN ; +return temp; +} + +/* +* Calculate the DR7 value for a list of kernel or user breakpoints. +*/ +static unsigned long calculate_dr7 (struct thread_hw_breakpoint *thbi) +{ +int is_user; +struct list_head *bp_list; +struct hw_breakpoint *bp; +int i; +int drnum; +unsigned long dr7; + +if (thbi) { + is_user =1 ; + bp_list = &thbi->thread_bps; + drnum =HB_NUM -1 ; + }else { + is_user =0 ; + bp_list = &kernel_bps; + drnum =0 ; + } + +/* Kernel bps are assigned from DR0 on up, and user bps are assigned +* from DR3 on down. Accumulate all 4 bps; the kernel DR7 mask will +* select the appropriate bits later. +*/ + dr7 =0 ; + i =0 ; +list_for_each_entry (bp, bp_list, node) { + +/* Get the debug register number and accumulate the bits */ + dr7 |=encode_dr7 (drnum, bp->info.len, bp->info.type); +if (++i >=HB_NUM ) +break ; +if (is_user) + --drnum; +else + ++drnum; + } +return dr7; +} + +/* +* Register a new user breakpoint structure. +*/ +static void arch_register_user_hw_breakpoint (struct hw_breakpoint *bp, +struct thread_hw_breakpoint *thbi) +{ + thbi->tdr7 =calculate_dr7 (thbi); + +/* If this is an execution breakpoint for the current PC address, +* we should clear the task's RF so that the bp will be certain +* to trigger. +* +* FIXME: It's not so easy to get hold of the task's PC as a linear +* address! ptrace.c does this already... +*/ +} + +/* +* Unregister a user breakpoint structure. +*/ +static void arch_unregister_user_hw_breakpoint (struct hw_breakpoint *bp, +struct thread_hw_breakpoint *thbi) +{ + thbi->tdr7 =calculate_dr7 (thbi); +} + +/* +* Register a kernel breakpoint structure. +*/ +static void arch_register_kernel_hw_breakpoint ( +struct hw_breakpoint *bp) +{ + kdr7 =calculate_dr7 (NULL ); +} + +/* +* Unregister a kernel breakpoint structure. +*/ +static void arch_unregister_kernel_hw_breakpoint ( +struct hw_breakpoint *bp) +{ + kdr7 =calculate_dr7 (NULL ); +} + + +/* End of arch-specific hook routines */ + + +/* +* Copy out the debug register information for a core dump. +* +* tsk must be equal to current. +*/ +void dump_thread_hw_breakpoint (struct task_struct *tsk,int u_debugreg[8 ]) +{ +struct thread_hw_breakpoint *thbi = tsk->thread.hw_breakpoint_info; +int i; + +memset (u_debugreg,0 ,sizeof u_debugreg); +if (thbi) { +for (i =0 ; i <HB_NUM ; ++i) + u_debugreg[i] = thbi->vdr_bps[i].info.address; + u_debugreg[7 ] = thbi->vdr7; + } + u_debugreg[6 ] = tsk->thread.vdr6; +} + +/* +* Ptrace support: breakpoint trigger routine. +*/ + +static struct thread_hw_breakpoint *alloc_thread_hw_breakpoint ( +struct task_struct *tsk); +static int __register_user_hw_breakpoint (struct task_struct *tsk, +struct hw_breakpoint *bp, +unsigned long address,unsigned len,unsigned type); +static void __unregister_user_hw_breakpoint (struct task_struct *tsk, +struct hw_breakpoint *bp); + +static void ptrace_triggered (struct hw_breakpoint *bp,struct pt_regs *regs) +{ +struct task_struct *tsk = current; +struct thread_hw_breakpoint *thbi = tsk->thread.hw_breakpoint_info; +int i; + +/* Store in the virtual DR6 register the fact that the breakpoint +* was hit so the thread's debugger will see it. +*/ +if (thbi) { + i = bp - thbi->vdr_bps; + tsk->thread.vdr6 |= (DR_TRAP0 << i); + } +} + +/* +* Handle PTRACE_PEEKUSR calls for the debug register area. +*/ +unsigned long thread_get_debugreg (struct task_struct *tsk,int n) +{ +struct thread_hw_breakpoint *thbi; +unsigned long val =0 ; + +mutex_lock (&hw_breakpoint_mutex); + thbi = tsk->thread.hw_breakpoint_info; +if (n <HB_NUM ) { +if (thbi) + val = thbi->vdr_bps[n].info.address; + }else if (n ==6 ) { + val = tsk->thread.vdr6; + }else if (n ==7 ) { +if (thbi) + val = thbi->vdr7; + } +mutex_unlock (&hw_breakpoint_mutex); +return val; +} + +/* +* Decode the length and type bits for a particular breakpoint as +* stored in debug register 7. Return the "enabled" status. +*/ +static int decode_dr7 (unsigned long dr7,int bpnum,unsigned *len, +unsigned *type) +{ +int temp = dr7 >> (DR_CONTROL_SHIFT + bpnum *DR_CONTROL_SIZE ); + + *len = (temp &0xc ) |0x40 ; + *type = (temp &0x3 ) |0x80 ; +return (dr7 >> (bpnum *DR_ENABLE_SIZE )) &0x3 ; +} + +/* +* Handle ptrace writes to debug register 7. +*/ +static int ptrace_write_dr7 (struct task_struct *tsk, +struct thread_hw_breakpoint *thbi,unsigned long data) +{ +struct hw_breakpoint *bp; +int i; +int rc =0 ; +unsigned long old_dr7 = thbi->vdr7; + + data &= ~DR_CONTROL_RESERVED ; + +/* Loop through all the hardware breakpoints, making the +* appropriate changes to each. +*/ + restore_settings: + thbi->vdr7 = data; + bp = &thbi->vdr_bps[0 ]; +for (i =0 ; i <HB_NUM ; (++i, ++bp)) { +int enabled; +unsigned len, type; + + enabled =decode_dr7 (data, i, &len, &type); + +/* Unregister the breakpoint before trying to change it */ +if (bp->status) +__unregister_user_hw_breakpoint (tsk, bp); + +/* Now register the breakpoint if it should be enabled. +* New invalid entries will raise an error here. +*/ +if (enabled) { +/* TODO: Change this portion of the code to use pre_ +* and post_ handler_allowed() routine +*/ +if (type ==HW_BREAKPOINT_EXECUTE ) + bp->pre_handler = ptrace_triggered; +else + bp->post_handler = ptrace_triggered; + + bp->priority =HW_BREAKPOINT_PRIO_PTRACE ; +if (rc ==0 &&__register_user_hw_breakpoint (tsk, bp, + bp->info.address, len, type) <0 ) +break ; + } + } + +/* If anything above failed, restore the original settings */ +if (i <HB_NUM ) { + rc = -EIO ; + data = old_dr7; + goto restore_settings; + } +return rc; +} + +/* +* Handle PTRACE_POKEUSR calls for the debug register area. +*/ +int thread_set_debugreg (struct task_struct *tsk,int n,unsigned long val) +{ +struct thread_hw_breakpoint *thbi; +int rc = -EIO ; + +/* We have to hold this lock the entire time, to prevent thbi +* from being deallocated out from under us. +*/ +mutex_lock (&hw_breakpoint_mutex); + +/* There are no DR4 or DR5 registers */ +if (n ==4 || n ==5 ) + ; + +/* Writes to DR6 modify the virtualized value */ +else if (n ==6 ) { + tsk->thread.vdr6 = val; + rc =0 ; + } + +else if (!tsk->thread.hw_breakpoint_info && val ==0 ) + rc =0 ;/* Minor optimization */ + +else if ((thbi =alloc_thread_hw_breakpoint (tsk)) ==NULL ) + rc = -ENOMEM ; + +/* Writes to DR0 - DR3 change a breakpoint address */ +else if (n <HB_NUM ) { +struct hw_breakpoint *bp = &thbi->vdr_bps[n]; + +/* If the breakpoint is registered then unregister it, +* change it, and re-register it. Revert to the original +* address if an error occurs. +*/ +if (bp->status) { +unsigned long old_addr = bp->info.address; + +__unregister_user_hw_breakpoint (tsk, bp); + rc =__register_user_hw_breakpoint (tsk, bp, + val, bp->info.len, bp->info.type); +if (rc <0 ) { +__register_user_hw_breakpoint (tsk, bp, + old_addr, + bp->info.len, bp->info.type); + } + }else { + bp->info.address = val; + rc =0 ; + } + } + +/* All that's left is DR7 */ +else + rc =ptrace_write_dr7 (tsk, thbi, val); + +mutex_unlock (&hw_breakpoint_mutex); +return rc; +} + + +/* +* Handle debug exception notifications. +*/ + +static void switch_to_none_hw_breakpoint (void ); +struct hw_breakpoint *last_hit_bp; +struct thread_hw_breakpoint *last_hit_thbi; + +static int __kprobes hw_breakpoint_handler (struct die_args *args) +{ +struct cpu_hw_breakpoint *chbi; +int i; +struct hw_breakpoint *bp; +struct thread_hw_breakpoint *thbi =NULL ; +unsigned int *ssr = &(__get_cpu_var (sstep_reason)); + +/* The DR6 value is stored in args->err */ +#define DR6 (args->err) + + chbi = &per_cpu (cpu_info,get_cpu ()); + +/* Disable all breakpoints so that the callbacks can run without +* triggering recursive debug exceptions. +*/ +set_debugreg (0UL ,7 ); + +if ((DR6 &DR_STEP ) && !((*ssr) &SSTEP_HWBKPT )) +return NOTIFY_DONE ; + +/* If none of the breakpoint detection flags are enabled, it means we +* have arrived here due to single-stepping of a target instruction +*/ +if ((!(DR6 & (DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 ))) && + (DR6 &DR_STEP )) { + args->regs->flags &= ~X86_EFLAGS_TF ; + (*ssr) &= ~SSTEP_HWBKPT ; + (last_hit_bp->post_handler)(last_hit_bp, args->regs); +/* Re-enable the breakpoints */ +set_debugreg (last_hit_thbi ? last_hit_thbi->tkdr7 : + chbi->cur_kbpdata->mkdr7,7 ); +put_cpu_no_resched (); + current->thread.vdr6 &= ~(DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 ); + +return NOTIFY_STOP ; + } + +/* Assert that local interrupts are disabled */ +/* Reset the DRn bits in the virtualized register value. +* The ptrace trigger routine will add in whatever is needed. +*/ + current->thread.vdr6 &= ~(DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 ); + +/* Are we a victim of lazy debug-register switching? */ +if (!chbi->bp_task) + ; +else if (chbi->bp_task != current) { + +/* No user breakpoints are valid. Perform the belated +* debug-register switch. +*/ +switch_to_none_hw_breakpoint (); + }else { + thbi = chbi->bp_task->thread.hw_breakpoint_info; + } + +/* Handle all the breakpoints that were triggered */ +for (i =0 ; i <HB_NUM ; ++i) { +if (likely (!(DR6 & (DR_TRAP0 << i)))) +continue ; + +/* Find the corresponding hw_breakpoint structure and +* invoke its triggered callback. +*/ +if (i < chbi->cur_kbpdata->num_kbps) + bp = chbi->cur_kbpdata->bps[i]; +else if (thbi) + bp = thbi->bps[i]; +else /* False alarm due to lazy DR switching */ +continue ; +if (bp) { + (*ssr) =0 ; +/* Enable single-stepping over the watched insn */ +switch (bp->info.type) { +case HW_BREAKPOINT_EXECUTE : +if (bp->pre_handler) + (bp->pre_handler)(bp, args->regs); + +if (bp->post_handler) { + (*ssr) |=SSTEP_HWBKPT ; + args->regs->flags |= + (X86_EFLAGS_TF |X86_EFLAGS_RF ); + last_hit_bp = bp; + last_hit_thbi = thbi; +return NOTIFY_DONE ; + } +break ; +case HW_BREAKPOINT_WRITE : +case HW_BREAKPOINT_RW : +if (bp->post_handler) + (bp->post_handler)(bp, args->regs); +/* Re-enable the breakpoints */ +set_debugreg (thbi ? thbi->tkdr7 : + chbi->cur_kbpdata->mkdr7,7 ); +put_cpu_no_resched (); + +return NOTIFY_DONE ; + } + } + } +/* Stop processing further if the exception is a stray one */ +if (!(DR6 & ~(DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 ))) +return NOTIFY_STOP ; + +return NOTIFY_DONE ; +#undef DR6 +} + +/* +* Handle debug exception notifications. +*/ +static int __kprobes hw_breakpoint_exceptions_notify ( +struct notifier_block *unused,unsigned long val,void *data) +{ +int ret; + +if (val !=DIE_DEBUG ) +return NOTIFY_DONE ; + ret =hw_breakpoint_handler (data); +return ret; +} + +static struct notifier_block hw_breakpoint_exceptions_nb = { + .notifier_call = hw_breakpoint_exceptions_notify, + .priority =0x00 +}; + +static int __init init_hw_breakpoint (void ) +{ +load_debug_registers (); +return register_die_notifier (&hw_breakpoint_exceptions_nb); +} + +core_initcall (init_hw_breakpoint); + + +/* Grab the arch-independent code */ +#include "../../../kernel/hw_breakpoint.c" +
include/asm-x86/hw_breakpoint.h new file +121 -0
121 lines not on this page · raw message
arch/x86/kernel/Makefile +1 -1
@@ -33,7 +33,7 @@ obj-$(CONFIG_X86_64) += sys_x86_64.o x86 obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o obj-y += bootflag.o e820.o obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o -obj-y += alternative.o i8253.o pci-nommu.o +obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o obj-y += tsc.o io_delay.o rtc.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
Re: [RFC Patch 2/9] x86 architecture implementation of Hardware Breakpoint interfaces
Quoted 7 lines · K.Prasad
This patch introduces two new files named hw_breakpoint.[ch] inside x86 specific directories. They contain functions which help validate and serve requests for using Hardware Breakpoint registers on x86 processors.
arch/x86/kernel/hw_breakpoint.c new file +0
-0@@ -0,0 +1,684 @@
...
arch/x86/kernel/hw_breakpoint.c line 141
Quoted 7 lines
arch/x86/kernel/hw_breakpoint.c new file +7
-0+int pre_handler_allowed (unsigned type) +{ +if (type ==HW_BREAKPOINT_EXECUTE ) +return 1 ; +else +return -EINVAL ; +}
The routine's name should match the name in the header file. "allowed" isn't right: You're _allowed_ to have pre_handlers -- they just won't get invoked. "supported" would be better.
Also, the comment in the header file should explain the meaning of the return value -- you should return 0 if a pre_handler is not supported, not -EINVAL. Better yet, define the function (both here and in the header file) as returning bool rather than int.
arch/x86/kernel/hw_breakpoint.c line 147
Quoted 6 lines
arch/x86/kernel/hw_breakpoint.c new file +6
-0+ +int post_handler_allowed (unsigned type) +{ +/* We can have a post handler for all types of breakpoints */ +return 1 ; +}
Same comments as above.
Also, in this initial version I would prefer to avoid the complications of single-stepping. It can always be added later. So for now, the x86 implementation should not support post_handlers for execution breakpoints.
...
arch/x86/kernel/hw_breakpoint.c line 165
Quoted 6 lines
arch/x86/kernel/hw_breakpoint.c new file +6
-0+/* +* Validate the arch-specific HW Breakpoint register settings +*/ +static int arch_validate_hwbkpt_settings (struct hw_breakpoint *bp, +unsigned long address,unsigned len,unsigned int type, +unsigned int *align)
Why did you move this routine into the arch-specific code?
...
arch/x86/kernel/hw_breakpoint.c line 543
Quoted 7 lines
arch/x86/kernel/hw_breakpoint.c new file +7
-0+/* +* Handle debug exception notifications. +*/ + +static void switch_to_none_hw_breakpoint (void ); +struct hw_breakpoint *last_hit_bp; +struct thread_hw_breakpoint *last_hit_thbi;
Shouldn't these variables be static? Although if they're needed only for single-stepping, they can be removed entirely for now...
Re: [RFC Patch 2/9] x86 architecture implementation of Hardware Breakpoint interfaces
Quoted 11 lines · Alan Stern
Quoted 7 lines · K.Prasad
This patch introduces two new files named hw_breakpoint.[ch] inside x86 specific directories. They contain functions which help validate and serve requests for using Hardware Breakpoint registers on x86 processors.
arch/x86/kernel/hw_breakpoint.c new file +0
-0@@ -0,0 +1,684 @@...
Quoted 11 lines
arch/x86/kernel/hw_breakpoint.c line 141
Quoted 7 lines
arch/x86/kernel/hw_breakpoint.c new file +7
-0+int pre_handler_allowed (unsigned type) +{ +if (type ==HW_BREAKPOINT_EXECUTE ) +return 1 ; +else +return -EINVAL ; +}The routine's name should match the name in the header file. "allowed" isn't right: You're _allowed_ to have
pre_handlers-- they just won't get invoked. "supported" would be better.
pre_handler_supported() definitely sounds better. I will change them.
Quoted 6 lines
Also, the comment in the header file should explain the meaning of the return value -- you should return 0 if a
pre_handleris not supported, not-EINVAL. Better yet, define the function (both here and in the header file) as returning bool rather than int.
I will change them to boolean.
Quoted 9 lines
arch/x86/kernel/hw_breakpoint.c line 147
Quoted 6 lines
arch/x86/kernel/hw_breakpoint.c new file +6
-0+ +int post_handler_allowed (unsigned type) +{ +/* We can have a post handler for all types of breakpoints */ +return 1 ; +}Same comments as above.
Quoted 5 lines
Also, in this initial version I would prefer to avoid the complications of single-stepping. It can always be added later. So for now, the x86 implementation should not support
post_handlersfor execution breakpoints.
There's been a perceivable inclination to let the user learn the limitations/features of the underlying processor's breakpointing ability (since the previous email mail thread on this topic) and the routines pre_ and post_handler_allowed() are just a step towards that.
I can nullify the post_handler for x86-instruction breakpoint for now, but it wouldn't simplify things very extensively (but for a few lines of code in hw_breakpoint_handler() and the flag 'sstep_reason'). It also benefits the code by bringing an understanding that there can be multiple users of processor single-stepping (and therefore the need to de-multiplex the exception and invoke the appropriate handler).
Left to me, I would like to retain the post_handler routine, unless you strongly feel otherwise.
Quoted 11 lines
...
arch/x86/kernel/hw_breakpoint.c line 165
Quoted 6 lines
arch/x86/kernel/hw_breakpoint.c new file +6
-0+/* +* Validate the arch-specific HW Breakpoint register settings +*/ +static int arch_validate_hwbkpt_settings (struct hw_breakpoint *bp, +unsigned long address,unsigned len,unsigned int type, +unsigned int *align)Why did you move this routine into the arch-specific code?
...
Quoted 11 lines
arch/x86/kernel/hw_breakpoint.c line 543
Quoted 7 lines
arch/x86/kernel/hw_breakpoint.c new file +7
-0+/* +* Handle debug exception notifications. +*/ + +static void switch_to_none_hw_breakpoint (void ); +struct hw_breakpoint *last_hit_bp; +struct thread_hw_breakpoint *last_hit_thbi;Shouldn't these variables be static? Although if they're needed only for single-stepping, they can be removed entirely for now...
Agreed. Will make them static.
Thanks,
K.Prasad
Re: [RFC Patch 2/9] x86 architecture implementation of Hardware Breakpoint interfaces
Quoted 12 lines · K.Prasad
There's been a perceivable inclination to let the user learn the limitations/features of the underlying processor's breakpointing ability (since the previous email mail thread on this topic) and the routines
pre_andpost_handler_allowed()are just a step towards that.I can nullify the
post_handlerfor x86-instruction breakpoint for now, but it wouldn't simplify things very extensively (but for a few lines of code inhw_breakpoint_handler()and the flag 'sstep_reason'). It also benefits the code by bringing an understanding that there can be multiple users of processor single-stepping (and therefore the need to de-multiplex the exception and invoke the appropriate handler).
Quoted 2 lines
Left to me, I would like to retain the
post_handlerroutine, unless you strongly feel otherwise.
Let's hear what Roland has to say.
Alan Stern
Re: [RFC Patch 2/9] x86 architecture implementation of Hardware Breakpoint interfaces
In reply to Alan SternQuoted 43 lines · Alan Stern
Quoted 7 lines · K.Prasad
This patch introduces two new files named hw_breakpoint.[ch] inside x86 specific directories. They contain functions which help validate and serve requests for using Hardware Breakpoint registers on x86 processors.
arch/x86/kernel/hw_breakpoint.c new file +0
-0@@ -0,0 +1,684 @@...
arch/x86/kernel/hw_breakpoint.c line 141
Quoted 7 lines
arch/x86/kernel/hw_breakpoint.c new file +7
-0+int pre_handler_allowed (unsigned type) +{ +if (type ==HW_BREAKPOINT_EXECUTE ) +return 1 ; +else +return -EINVAL ; +}The routine's name should match the name in the header file. "allowed" isn't right: You're _allowed_ to have
pre_handlers-- they just won't get invoked. "supported" would be better.Also, the comment in the header file should explain the meaning of the return value -- you should return 0 if a
pre_handleris not supported, not-EINVAL. Better yet, define the function (both here and in the header file) as returning bool rather than int.arch/x86/kernel/hw_breakpoint.c line 147
Quoted 6 lines
arch/x86/kernel/hw_breakpoint.c new file +6
-0+ +int post_handler_allowed (unsigned type) +{ +/* We can have a post handler for all types of breakpoints */ +return 1 ; +}Same comments as above.
Also, in this initial version I would prefer to avoid the complications of single-stepping. It can always be added later. So for now, the x86 implementation should not support
post_handlersfor execution breakpoints....
Quoted 9 lines
arch/x86/kernel/hw_breakpoint.c line 165
Quoted 6 lines
arch/x86/kernel/hw_breakpoint.c new file +6
-0+/* +* Validate the arch-specific HW Breakpoint register settings +*/ +static int arch_validate_hwbkpt_settings (struct hw_breakpoint *bp, +unsigned long address,unsigned len,unsigned int type, +unsigned int *align)Why did you move this routine into the arch-specific code?
I missed a reply for this comment in my previous mail.
Given that arch_validate_hwbkpt_settings() validates the type of breakpoint operation requested and length of address to be monitored - both of which are specific to the underlying processor architecture, I thought it would be appropriate to place them in arch/x86/kernel/hw_breakpoint.c.
Thanks,
K.Prasad
RFC PATCH 3/9 Modifying generic debug exception to use virtual debug registers
In reply to K.PrasadThis patch modifies the breakpoint exception handler code to use the abstract register names.
--- arch/x86/kernel/traps_32.c | 67 ++++++++++++++++----------------------------- arch/x86/kernel/traps_64.c | 64 ++++++++++++++++++++---------------------- 2 files changed, 54 insertions(+), 77 deletions(-)
arch/x86/kernel/traps_32.c +24 -43
@@ -890,11 +890,12 @@ void __kprobes do_int3(struct pt_regs *rvoid __kprobes do_debug (struct pt_regs *regs,long error_code) {struct task_struct *tsk = current; -unsigned int condition; +unsigned long dr6;trace_hardirqs_fixup (); -get_debugreg (condition,6 ); +get_debugreg (dr6,6 ); +set_debugreg (0 ,6 );/* DR6 may or may not be cleared by the CPU */ /* * The processor cleared BTF, so don't mark that we need it set.
@@ -902,60 +903,40 @@ void __kprobes do_debug(struct pt_regs *clear_tsk_thread_flag (tsk,TIF_DEBUGCTLMSR ); tsk->thread.debugctlmsr =0 ; -if (notify_die (DIE_DEBUG ,"debug" , regs, condition, error_code, -SIGTRAP ) ==NOTIFY_STOP ) +/* Store the virtualized DR6 value */ + tsk->thread.vdr6 = dr6; + +if (notify_die (DIE_DEBUG ,"debug" , regs, dr6, error_code, +SIGTRAP ) ==NOTIFY_STOP )return ;/* It's safe to allow irq's after DR6 has been saved */ if (regs->flags &X86_EFLAGS_IF )local_irq_enable (); -/* Mask out spurious debug traps due to lazy DR7 setting */ -if (condition & (DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 )) { -if (!tsk->thread.debugreg7) - goto clear_dr7; +if (regs->flags &X86_VM_MASK ) { +handle_vm86_trap ((struct kernel_vm86_regs *) regs, + error_code,1 ); +return ; } -if (regs->flags &X86_VM_MASK ) - goto debug_vm86; - -/* Save debug status register where ptrace can see it */ - tsk->thread.debugreg6 = condition; -/* -* Single-stepping through TF: make sure we ignore any events in -* kernel space (but re-enable TF when returning to user mode). +* Single-stepping through system calls: ignore any exceptions in +* kernel space, but re-enable TF when returning to user mode. +* +* We already checked v86 mode above, so we can check for kernel mode +* by just checking the CPL of CS. */ -if (condition &DR_STEP ) { -/* -* We already checked v86 mode above, so we can -* check for kernel mode by just checking the CPL -* of CS. -*/ -if (!user_mode (regs)) - goto clear_TF_reenable; +if ((dr6 &DR_STEP ) && !user_mode (regs)) { + tsk->thread.vdr6 &= ~DR_STEP ; +set_tsk_thread_flag (tsk,TIF_SINGLESTEP ); + regs->flags &= ~X86_EFLAGS_TF ; } -/* Ok, finally something we can handle */ -send_sigtrap (tsk, regs, error_code); - -/* -* Disable additional traps. They'll be re-enabled when -* the signal is delivered. -*/ -clear_dr7: -set_debugreg (0 ,7 ); -return ; - -debug_vm86: -handle_vm86_trap ((struct kernel_vm86_regs *) regs, error_code,1 ); -return ; - -clear_TF_reenable: -set_tsk_thread_flag (tsk,TIF_SINGLESTEP ); - regs->flags &= ~X86_EFLAGS_TF ; -return ; +if (tsk->thread.vdr6 & (DR_STEP |DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 )) +send_sigtrap (tsk, regs, error_code); } +/* * Note that we play around with the 'TS' bit in an attempt to get * the correct behaviour even in the presence of the asynchronous
arch/x86/kernel/traps_64.c +30 -34
91 lines not on this page · raw message
RFC PATCH 4/9 Modify kprobe exception handler to recognise single-stepping by HW Breakpoint handler
In reply to K.PrasadThis patch modifies the kprobe handler to help it recognise single-stepping by the HW Breakpoint exception code. A per-cpu variable called 'sstep_reason' to distinguish the source of single-step exceptions.
--- arch/x86/kernel/kprobes.c | 18 ++++++++++++++++-- include/asm-x86/kprobes.h | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-)
arch/x86/kernel/kprobes.c +16 -2
@@ -54,6 +54,7 @@#include <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/alternative.h> +#include <asm/debugreg.h> void jprobe_return_end (void );
@@ -517,6 +518,7 @@ static int __kprobes kprobe_handler(strukprobe_opcode_t *addr;struct kprobe *p;struct kprobe_ctlblk *kcb; +unsigned int *ssr; addr = (kprobe_opcode_t *)(regs->ip -sizeof (kprobe_opcode_t));if (*addr !=BREAKPOINT_INSTRUCTION ) {
@@ -541,6 +543,7 @@ static int __kprobes kprobe_handler(stru*/ preempt_disable (); + ssr = &(__get_cpu_var (sstep_reason)); kcb =get_kprobe_ctlblk (); p =get_kprobe (addr);
@@ -560,14 +563,17 @@ static int __kprobes kprobe_handler(stru* for jprobe processing, so get out doing nothing * more here. */ -if (!p->pre_handler || !p->pre_handler (p, regs)) +if (!p->pre_handler || !p->pre_handler (p, regs)) {setup_singlestep (p, regs, kcb); + (*ssr) |=SSTEP_KPROBE ; + }return 1 ; } }else if (kprobe_running ()) { p =__get_cpu_var (current_kprobe);if (p->break_handler && p->break_handler (p, regs)) {setup_singlestep (p, regs, kcb); + (*ssr) |=SSTEP_KPROBE ;return 1 ; } }/* else: not a kprobe fault; let the kernel handle it */
@@ -952,6 +958,7 @@ int __kprobes kprobe_exceptions_notify(s {struct die_args *args = data;int ret =NOTIFY_DONE ; +unsigned int *ssr = &(__get_cpu_var (sstep_reason));if (args->regs &&user_mode_vm (args->regs))return ret;
@@ -962,8 +969,15 @@ int __kprobes kprobe_exceptions_notify(s ret =NOTIFY_STOP ;break ;case DIE_DEBUG : -if (post_kprobe_handler (args->regs)) +/* We could be here due to single-stepping after a pre-handler +* execution of HW Breakpoint or kprobes. We determine the cause +* using the bitmask flag 'sstep_reason'. +*/ +if (((*ssr) &SSTEP_KPROBE ) && +post_kprobe_handler (args->regs)) { + current->thread.vdr6 &= ~DR_STEP ; ret =NOTIFY_STOP ; + }break ;case DIE_GPF :/*
include/asm-x86/kprobes.h +8 -0
@@ -30,6 +30,14 @@struct pt_regs ;struct kprobe ; +/* Single stepping can be initiated for kprobes post handler or following HW +* Breakpoint exception. The bitmask below is used to identify the cause. +*/ +#define SSTEP_KPROBE 1 +#define SSTEP_HWBKPT 2 + +DECLARE_PER_CPU(unsigned int, sstep_reason); +typedef u8 kprobe_opcode_t ;#define BREAKPOINT_INSTRUCTION 0xcc#define RELATIVEJUMP_INSTRUCTION 0xe9
RFC PATCH 5/9 Use wrapper routines around debug registers in processor related functions
In reply to K.PrasadThis patch enables the use of wrapper routines to access the debug/breakpoint registers.
--- arch/x86/kernel/smpboot.c | 3 +++ arch/x86/power/cpu_32.c | 16 +++------------- arch/x86/power/cpu_64.c | 15 +++------------ include/asm-x86/debugreg.h | 29 +++++++++++++++++++++++++++++ include/asm-x86/processor.h | 10 +++------- 5 files changed, 41 insertions(+), 32 deletions(-)
arch/x86/power/cpu_32.c +3 -13
@@ -11,6 +11,7 @@#include <linux/suspend.h> #include <asm/mtrr.h> #include <asm/mce.h> +#include <asm/debugreg.h> static struct saved_context saved_context;
@@ -46,6 +47,7 @@ static void __save_processor_state(struc ctxt->cr2 =read_cr2 (); ctxt->cr3 =read_cr3 (); ctxt->cr4 =read_cr4_safe (); +disable_debug_registers (); }/* Needed by apm.c */
@@ -78,19 +80,7 @@ static void fix_processor_context(void)load_TR_desc ();/* This does ltr */ load_LDT (¤t->active_mm->context);/* This does lldt */ -/* -* Now maybe reload the debug registers -*/ -if (current->thread.debugreg7) { -set_debugreg (current->thread.debugreg0,0 ); -set_debugreg (current->thread.debugreg1,1 ); -set_debugreg (current->thread.debugreg2,2 ); -set_debugreg (current->thread.debugreg3,3 ); -/* no 4 and 5 */ -set_debugreg (current->thread.debugreg6,6 ); -set_debugreg (current->thread.debugreg7,7 ); - } - +load_debug_registers (); }static void __restore_processor_state (struct saved_context *ctxt)
arch/x86/power/cpu_64.c +3 -12
@@ -14,6 +14,7 @@#include <asm/page.h> #include <asm/pgtable.h> #include <asm/mtrr.h> +#include <asm/debugreg.h> static void fix_processor_context (void );
@@ -69,6 +70,7 @@ static void __save_processor_state(struc ctxt->cr3 =read_cr3 (); ctxt->cr4 =read_cr4 (); ctxt->cr8 =read_cr8 (); +disable_debug_registers (); }void save_processor_state (void )
@@ -151,16 +153,5 @@ static void fix_processor_context(void)load_TR_desc ();/* This does ltr */ load_LDT (¤t->active_mm->context);/* This does lldt */ -/* -* Now maybe reload the debug registers -*/ -if (current->thread.debugreg7){ -loaddebug (¤t->thread,0 ); -loaddebug (¤t->thread,1 ); -loaddebug (¤t->thread,2 ); -loaddebug (¤t->thread,3 ); -/* no 4 and 5 */ -loaddebug (¤t->thread,6 ); -loaddebug (¤t->thread,7 ); - } +load_debug_registers (); }
arch/x86/kernel/smpboot.c +3 -0
@@ -61,6 +61,7 @@#include <asm/mtrr.h> #include <asm/vmi.h> #include <asm/genapic.h> +#include <asm/debugreg.h> #include <linux/mc146818rtc.h> #include <mach_apic.h>
@@ -342,6 +343,7 @@ static void __cpuinit start_secondary(vosetup_secondary_clock ();wmb (); +load_debug_registers ();cpu_idle (); }
@@ -1382,6 +1384,7 @@ int __cpu_disable(void)remove_cpu_from_maps (cpu);unlock_vector_lock ();fixup_irqs (cpu_online_map); +disable_debug_registers ();return 0 ; }
include/asm-x86/debugreg.h +29 -0
@@ -49,6 +49,8 @@#define DR_LOCAL_ENABLE_SHIFT 0/* Extra shift to the local enable bit */ #define DR_GLOBAL_ENABLE_SHIFT 1/* Extra shift to the global enable bit */ +#define DR_LOCAL_ENABLE (0x1)/* Local enable for reg 0 */ +#define DR_GLOBAL_ENABLE (0x2)/* Global enable for reg 0 */ #define DR_ENABLE_SIZE 2/* 2 enable bits per register */ #define DR_LOCAL_ENABLE_MASK (0x55)/* Set local bits for all 4 regs */
@@ -67,4 +69,31 @@#define DR_LOCAL_SLOWDOWN (0x100)/* Local slow the pipeline */ #define DR_GLOBAL_SLOWDOWN (0x200)/* Global slow the pipeline */ +/* +* HW breakpoint additions +*/ +#ifdef __KERNEL__ + +#define HB_NUM 4/* Number of hardware breakpoints */ + +/* For process management */ +void flush_thread_hw_breakpoint (struct task_struct *tsk); +int copy_thread_hw_breakpoint (struct task_struct *tsk, +struct task_struct *child,unsigned long clone_flags); +void dump_thread_hw_breakpoint (struct task_struct *tsk,int u_debugreg[8 ]); +void switch_to_thread_hw_breakpoint (struct task_struct *tsk); + +/* For CPU management */ +void load_debug_registers (void ); +static inline void disable_debug_registers (void ) +{ +set_debugreg (0UL ,7 ); +} + +/* For use by ptrace */ +unsigned long thread_get_debugreg (struct task_struct *tsk,int n); +int thread_set_debugreg (struct task_struct *tsk,int n,unsigned long val); + +#endif /* __KERNEL__ */ +#endif
include/asm-x86/processor.h +3 -7
@@ -381,13 +381,9 @@ struct thread_struct {unsigned long ip;unsigned long fs;unsigned long gs; -/* Hardware debugging registers: */ -unsigned long debugreg0; -unsigned long debugreg1; -unsigned long debugreg2; -unsigned long debugreg3; -unsigned long debugreg6; -unsigned long debugreg7; +/* Hardware breakpoint info */ +unsigned long vdr6; +struct thread_hw_breakpoint *hw_breakpoint_info;/* Fault info: */ unsigned long cr2;unsigned long trap_no;
RFC PATCH 6/9 Use virtual debug registers in process/thread handling code
In reply to K.PrasadThis patch enables the use of abstract/virtual debug registers in process-handling routines.
--- arch/x86/kernel/process_32.c | 68 +++++++++++++++++++++++++------------------ arch/x86/kernel/process_64.c | 65 ++++++++++++++++++++++++----------------- 2 files changed, 79 insertions(+), 54 deletions(-)
arch/x86/kernel/process_32.c +40 -28
@@ -56,6 +56,8 @@#include <asm/cpu.h> #include <asm/kdebug.h> #include <asm/idle.h> +#include <asm/debugreg.h> +#include <asm/hw_breakpoint.h> asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
@@ -158,9 +160,11 @@ void cpu_idle(void)void __show_registers (struct pt_regs *regs,int all) {unsigned long cr0 =0L , cr2 =0L , cr3 =0L , cr4 =0L ; -unsigned long d0, d1, d2, d3, d6, d7; +unsigned long u_debugreg[8 ];unsigned long sp;unsigned short ss, gs; +struct thread_hw_breakpoint *thbi = current->thread.hw_breakpoint_info; +int i;if (user_mode_vm (regs)) { sp = regs->sp;
@@ -201,17 +205,18 @@ void __show_registers(struct pt_regs *reprintk ("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n" , cr0, cr2, cr3, cr4); -get_debugreg (d0,0 ); -get_debugreg (d1,1 ); -get_debugreg (d2,2 ); -get_debugreg (d3,3 ); +if (thbi) { +for (i =0 ; i <HB_NUM ; ++i) + u_debugreg[i] = thbi->vdr_bps[i].info.address; + u_debugreg[7 ] = thbi->vdr7; + } + u_debugreg[6 ] = current->thread.vdr6; +printk ("DR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n" , - d0, d1, d2, d3); + u_debugreg[0 ], u_debugreg[1 ], + u_debugreg[2 ], u_debugreg[3 ]); -get_debugreg (d6,6 ); -get_debugreg (d7,7 ); -printk ("DR6: %08lx DR7: %08lx\n" , - d6, d7); +printk ("DR6: %08lx DR7: %08lx\n" , u_debugreg[6 ], u_debugreg[7 ]); }void show_regs (struct pt_regs *regs)
@@ -257,6 +262,8 @@ EXPORT_SYMBOL(kernel_thread);*/ void exit_thread (void ) { +struct task_struct *tsk = current; +/* The process may have allocated an io port bitmap... nuke it. */ if (unlikely (test_thread_flag (TIF_IO_BITMAP ))) {struct task_struct *tsk = current;
@@ -277,20 +284,17 @@ void exit_thread(void) tss->x86_tss.io_bitmap_base =INVALID_IO_BITMAP_OFFSET ;put_cpu (); } +if (unlikely (tsk->thread.hw_breakpoint_info)) +flush_thread_hw_breakpoint (tsk); }void flush_thread (void ) {struct task_struct *tsk = current; - tsk->thread.debugreg0 =0 ; - tsk->thread.debugreg1 =0 ; - tsk->thread.debugreg2 =0 ; - tsk->thread.debugreg3 =0 ; - tsk->thread.debugreg6 =0 ; - tsk->thread.debugreg7 =0 ; -memset (tsk->thread.tls_array,0 ,sizeof (tsk->thread.tls_array)); -clear_tsk_thread_flag (tsk,TIF_DEBUG ); +memset (tsk->thread.tls_array,0 ,sizeof (tsk->thread.tls_array)); +if (unlikely (tsk->thread.hw_breakpoint_info)) +flush_thread_hw_breakpoint (tsk);/* * Forget coprocessor state.. */
@@ -334,7 +338,15 @@ int copy_thread(int nr, unsigned long clsavesegment (gs, p->thread.gs); + p->thread.hw_breakpoint_info =NULL ; + p->thread.io_bitmap_ptr =NULL ; + tsk = current; + err = -ENOMEM ; +if (unlikely (tsk->thread.hw_breakpoint_info)) { +if (copy_thread_hw_breakpoint (tsk, p, clone_flags)) + goto out; + }if (unlikely (test_tsk_thread_flag (tsk,TIF_IO_BITMAP ))) { p->thread.io_bitmap_ptr =kmemdup (tsk->thread.io_bitmap_ptr,IO_BITMAP_BYTES ,GFP_KERNEL );
@@ -354,10 +366,14 @@ int copy_thread(int nr, unsigned long cl err =do_set_thread_area (p,-1 , (struct user_desc __user *)childregs->si,0 ); +out:if (err && p->thread.io_bitmap_ptr) {kfree (p->thread.io_bitmap_ptr); p->thread.io_bitmap_max =0 ; } +if (err) +flush_thread_hw_breakpoint (p); +return err; }
@@ -460,16 +476,6 @@ __switch_to_xtra(struct task_struct *preif (next->debugctlmsr != debugctl)update_debugctlmsr (next->debugctlmsr); -if (test_tsk_thread_flag (next_p,TIF_DEBUG )) { -set_debugreg (next->debugreg0,0 ); -set_debugreg (next->debugreg1,1 ); -set_debugreg (next->debugreg2,2 ); -set_debugreg (next->debugreg3,3 ); -/* no 4 and 5 */ -set_debugreg (next->debugreg6,6 ); -set_debugreg (next->debugreg7,7 ); - } -if (test_tsk_thread_flag (prev_p,TIF_NOTSC ) ^test_tsk_thread_flag (next_p,TIF_NOTSC )) {/* prev and next are different */
@@ -625,6 +631,12 @@ struct task_struct * __switch_to(structloadsegment (gs, next->gs);x86_write_percpu (current_task, next_p); +/* +* Handle debug registers. This must be done _after_ current +* is updated. +*/ +if (unlikely (test_tsk_thread_flag (next_p,TIF_DEBUG ))) +switch_to_thread_hw_breakpoint (next_p);return prev_p; }
arch/x86/kernel/process_64.c +39 -26
128 lines not on this page · raw message
Re: [RFC Patch 6/9] Use virtual debug registers in process/thread handling code
Quoted 2 lines · K.Prasad
This patch enables the use of abstract/virtual debug registers in process-handling routines.
...
Quoted 39 lines
arch/x86/kernel/process_32.c +12
-5@@ -56,6 +56,8 @@#include <asm/cpu.h> #include <asm/kdebug.h> #include <asm/idle.h> +#include <asm/debugreg.h> +#include <asm/hw_breakpoint.h> asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");@@ -158,9 +160,11 @@ void cpu_idle(void)void __show_registers (struct pt_regs *regs,int all) {unsigned long cr0 =0L , cr2 =0L , cr3 =0L , cr4 =0L ; -unsigned long d0, d1, d2, d3, d6, d7; +unsigned long u_debugreg[8 ];unsigned long sp;unsigned short ss, gs; +struct thread_hw_breakpoint *thbi = current->thread.hw_breakpoint_info; +int i;if (user_mode_vm (regs)) { sp = regs->sp;@@ -201,17 +205,18 @@ void __show_registers(struct pt_regs *reprintk ("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n" , cr0, cr2, cr3, cr4); -get_debugreg (d0,0 ); -get_debugreg (d1,1 ); -get_debugreg (d2,2 ); -get_debugreg (d3,3 ); +if (thbi) { +for (i =0 ; i <HB_NUM ; ++i) + u_debugreg[i] = thbi->vdr_bps[i].info.address; + u_debugreg[7 ] = thbi->vdr7; + } + u_debugreg[6 ] = current->thread.vdr6; +
arch/x86/kernel/process_32.c void __show_registers(struct pt_regs *re · line 219
Quoted 10 lines
arch/x86/kernel/process_32.c +3
-5printk ("DR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n" , - d0, d1, d2, d3); + u_debugreg[0 ], u_debugreg[1 ], + u_debugreg[2 ], u_debugreg[3 ]); -get_debugreg (d6,6 ); -get_debugreg (d7,7 ); -printk ("DR6: %08lx DR7: %08lx\n" , - d6, d7); +printk ("DR6: %08lx DR7: %08lx\n" , u_debugreg[6 ], u_debugreg[7 ]);
I don't like this at all. show_registers() should display the physical register contents, not the virtualized values. Ditto for the 64-byte version of this routine.
Alan Stern
Re: [RFC Patch 6/9] Use virtual debug registers in process/thread handling code
Quoted 60 lines · Alan Stern
Quoted 2 lines · K.Prasad
This patch enables the use of abstract/virtual debug registers in process-handling routines.
...
Quoted 39 lines
arch/x86/kernel/process_32.c +12
-5@@ -56,6 +56,8 @@#include <asm/cpu.h> #include <asm/kdebug.h> #include <asm/idle.h> +#include <asm/debugreg.h> +#include <asm/hw_breakpoint.h> asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");@@ -158,9 +160,11 @@ void cpu_idle(void)void __show_registers (struct pt_regs *regs,int all) {unsigned long cr0 =0L , cr2 =0L , cr3 =0L , cr4 =0L ; -unsigned long d0, d1, d2, d3, d6, d7; +unsigned long u_debugreg[8 ];unsigned long sp;unsigned short ss, gs; +struct thread_hw_breakpoint *thbi = current->thread.hw_breakpoint_info; +int i;if (user_mode_vm (regs)) { sp = regs->sp;@@ -201,17 +205,18 @@ void __show_registers(struct pt_regs *reprintk ("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n" , cr0, cr2, cr3, cr4); -get_debugreg (d0,0 ); -get_debugreg (d1,1 ); -get_debugreg (d2,2 ); -get_debugreg (d3,3 ); +if (thbi) { +for (i =0 ; i <HB_NUM ; ++i) + u_debugreg[i] = thbi->vdr_bps[i].info.address; + u_debugreg[7 ] = thbi->vdr7; + } + u_debugreg[6 ] = current->thread.vdr6; +arch/x86/kernel/process_32.c void __show_registers(struct pt_regs *re · line 219
Quoted 10 lines
arch/x86/kernel/process_32.c +3
-5printk ("DR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n" , - d0, d1, d2, d3); + u_debugreg[0 ], u_debugreg[1 ], + u_debugreg[2 ], u_debugreg[3 ]); -get_debugreg (d6,6 ); -get_debugreg (d7,7 ); -printk ("DR6: %08lx DR7: %08lx\n" , - d6, d7); +printk ("DR6: %08lx DR7: %08lx\n" , u_debugreg[6 ], u_debugreg[7 ]);I don't like this at all.
show_registers()should display the physical register contents, not the virtualized values. Ditto for the 64-byte version of this routine.
Quoted 2 lines
Alan Stern
Agreed, will modify this!
Thanks,
K.Prasad
RFC PATCH 7/9 Modify signal handling code to refrain from re-enabling HW Breakpoints
In reply to K.PrasadThis patch disables re-enabling of Hardware Breakpoint registers through the signal handling code. This is now during hw_breakpoint_handler().
--- arch/x86/kernel/signal_32.c | 9 --------- arch/x86/kernel/signal_64.c | 8 -------- 2 files changed, 17 deletions(-)
arch/x86/kernel/signal_32.c +0 -9
@@ -600,15 +600,6 @@ static void do_signal(struct pt_regs *re signr =get_signal_to_deliver (&info, &ka, regs,NULL );if (signr >0 ) { -/* -* Re-enable any watchpoints before delivering the -* signal to user space. The processor register will -* have been cleared if the watchpoint triggered -* inside the kernel. -*/ -if (current->thread.debugreg7) -set_debugreg (current->thread.debugreg7,7 ); -/* Whee! Actually deliver the signal. */ if (handle_signal (signr, &info, &ka, oldset, regs) ==0 ) {/*
arch/x86/kernel/signal_64.c +0 -8
@@ -496,14 +496,6 @@ static void do_signal(struct pt_regs *re signr =get_signal_to_deliver (&info, &ka, regs,NULL );if (signr >0 ) { -/* Re-enable any watchpoints before delivering the -* signal to user space. The processor register will -* have been cleared if the watchpoint triggered -* inside the kernel. -*/ -if (current->thread.debugreg7) -set_debugreg (current->thread.debugreg7,7 ); -/* Whee! Actually deliver the signal. */ if (handle_signal (signr, &info, &ka, oldset, regs) ==0 ) {/*
RFC PATCH 8/9 Modify Ptrace to use wrapper routines to access breakpoint registers
In reply to K.PrasadThis patch modifies the ptrace code to use the new wrapper routines around the debug/breakpoint registers.
--- arch/x86/kernel/ptrace.c | 101 +---------------------------------------------- 1 file changed, 4 insertions(+), 97 deletions(-)
arch/x86/kernel/ptrace.c +4 -97
@@ -462,98 +462,6 @@ static int genregs_set(struct task_strucreturn ret; } -/* -* This function is trivial and will be inlined by the compiler. -* Having it separates the implementation details of debug -* registers from the interface details of ptrace. -*/ -static unsigned long ptrace_get_debugreg (struct task_struct *child,int n) -{ -switch (n) { -case 0 :return child->thread.debugreg0; -case 1 :return child->thread.debugreg1; -case 2 :return child->thread.debugreg2; -case 3 :return child->thread.debugreg3; -case 6 :return child->thread.debugreg6; -case 7 :return child->thread.debugreg7; - } -return 0 ; -} - -static int ptrace_set_debugreg (struct task_struct *child, -int n,unsigned long data) -{ -int i; - -if (unlikely (n ==4 || n ==5 )) -return -EIO ; - -if (n <4 &&unlikely (data >=debugreg_addr_limit (child))) -return -EIO ; - -switch (n) { -case 0 : child->thread.debugreg0 = data;break ; -case 1 : child->thread.debugreg1 = data;break ; -case 2 : child->thread.debugreg2 = data;break ; -case 3 : child->thread.debugreg3 = data;break ; - -case 6 : -if ((data & ~0xffffffffUL ) !=0 ) -return -EIO ; - child->thread.debugreg6 = data; -break ; - -case 7 : -/* -* Sanity-check data. Take one half-byte at once with -* check = (val >> (16 + 4*i)) & 0xf. It contains the -* R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits -* 2 and 3 are LENi. Given a list of invalid values, -* we do mask |= 1 << invalid_value, so that -* (mask >> check) & 1 is a correct test for invalid -* values. -* -* R/Wi contains the type of the breakpoint / -* watchpoint, LENi contains the length of the watched -* data in the watchpoint case. -* -* The invalid values are: -* - LENi == 0x10 (undefined), so mask |= 0x0f00. [32-bit] -* - R/Wi == 0x10 (break on I/O reads or writes), so -* mask |= 0x4444. -* - R/Wi == 0x00 && LENi != 0x00, so we have mask |= -* 0x1110. -* -* Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54. -* -* See the Intel Manual "System Programming Guide", -* 15.2.4 -* -* Note that LENi == 0x10 is defined on x86_64 in long -* mode (i.e. even for 32-bit userspace software, but -* 64-bit kernel), so the x86_64 mask value is 0x5454. -* See the AMD manual no. 24593 (AMD64 System Programming) -*/ -#ifdef CONFIG_X86_32 -#define DR7_MASK 0x5f54 -#else -#define DR7_MASK 0x5554 -#endif - data &= ~DR_CONTROL_RESERVED ; -for (i =0 ; i <4 ; i++) -if ((DR7_MASK >> ((data >> (16 +4 *i)) &0xf )) &1 ) -return -EIO ; - child->thread.debugreg7 = data; -if (data) -set_tsk_thread_flag (child,TIF_DEBUG ); -else -clear_tsk_thread_flag (child,TIF_DEBUG ); -break ; - } - -return 0 ; -} -#ifdef X86_BTS static int ptrace_bts_get_size (struct task_struct *child)
@@ -888,7 +796,7 @@ long arch_ptrace(struct task_struct *chi else if (addr >= offsetof(struct user, u_debugreg[0]) && addr <= offsetof(struct user, u_debugreg[7])) { addr -= offsetof(struct user, u_debugreg[0]); - tmp =ptrace_get_debugreg (child, addr /sizeof (data)); + tmp =thread_get_debugreg (child, addr/sizeof (data)); } ret =put_user (tmp, datap);break ;
@@ -905,8 +813,7 @@ long arch_ptrace(struct task_struct *chi else if (addr >= offsetof(struct user, u_debugreg[0]) && addr <= offsetof(struct user, u_debugreg[7])) { addr -= offsetof(struct user, u_debugreg[0]); - ret =ptrace_set_debugreg (child, - addr /sizeof (data), data); + ret =thread_set_debugreg (child, addr/sizeof (data), data); }break ;
@@ -1073,7 +980,7 @@ static int putreg32(struct task_struct * case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[7]): regno -= offsetof(struct user32, u_debugreg[0]); -return ptrace_set_debugreg (child, regno /4 , value); +return thread_set_debugreg (child, regno /4 , value);default :if (regno >sizeof (struct user32 ) || (regno &3 ))
@@ -1132,7 +1039,7 @@ static int getreg32(struct task_struct * case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[7]): regno -= offsetof(struct user32, u_debugreg[0]); - *val =ptrace_get_debugreg (child, regno /4 ); + *val =thread_get_debugreg (child, regno /4 );break ;default :
RFC PATCH 9/9 Cleanup HW Breakpoint registers before kexec
In reply to K.PrasadThis patch disables Hardware breakpoints before doing a 'kexec' on the machine.
--- arch/x86/kernel/machine_kexec_32.c | 2 ++ arch/x86/kernel/machine_kexec_64.c | 2 ++ 2 files changed, 4 insertions(+)
arch/x86/kernel/machine_kexec_32.c +2 -0
@@ -24,6 +24,7 @@#include <asm/desc.h> #include <asm/system.h> #include <asm/cacheflush.h> +#include <asm/debugreg.h> #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) static u32 kexec_pgd[1024] PAGE_ALIGNED;
@@ -131,6 +132,7 @@ void machine_kexec(struct kimage *image)/* Interrupts aren't acceptable while we reboot */ local_irq_disable (); +disable_debug_registers ();if (image->preserve_context) {#ifdef CONFIG_X86_IO_APIC
arch/x86/kernel/machine_kexec_64.c +2 -0
@@ -17,6 +17,7 @@#include <asm/tlbflush.h> #include <asm/mmu_context.h> #include <asm/io.h> +#include <asm/debugreg.h> #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) static u64 kexec_pgd[512] PAGE_ALIGNED;
@@ -190,6 +191,7 @@ void machine_kexec(struct kimage *image)/* Interrupts aren't acceptable while we reboot */ local_irq_disable (); +disable_debug_registers (); control_page =page_address (image->control_code_page) +PAGE_SIZE ;memcpy (control_page, relocate_kernel,PAGE_SIZE );
Quoted 4 lines · K.Prasad
- Enable KGDB and KVM to use the
register_kernel_hw_breakpoint()interface for their HW Breakpoint usage, in the absence of which they will be broken during simultaneous use.
KVM conceptually isn't a kernel use of the debug registers. KVM modifies the debug registers while the guest is running, and restores them after the guest returns.
Right now, as an optimization, KVM defers restoring the debug registers until after the next context switch out of the kvm task, or until the next exit to userspace (whichever comes earlier); we should change this to avoid the deferral if kernel breakpoints are in effect. This will allow simultaneous use of KVM breakpoints and kernel breakpoints.
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 10 lines · Avi Kivity
Quoted 4 lines · K.Prasad
- Enable KGDB and KVM to use the
register_kernel_hw_breakpoint()interface for their HW Breakpoint usage, in the absence of which they will be broken during simultaneous use.KVM conceptually isn't a kernel use of the debug registers. KVM modifies the debug registers while the guest is running, and restores them after the guest returns.
Quoted 6 lines
Right now, as an optimization, KVM defers restoring the debug registers until after the next context switch out of the kvm task, or until the next exit to userspace (whichever comes earlier); we should change this to avoid the deferral if kernel breakpoints are in effect. This will allow simultaneous use of KVM breakpoints and kernel breakpoints.
The patch posted provides interface for using HW breakpoints on both user- and kernel-space breakpoints. KVM's user-space address breakpoint requirements, after code-modification, should be made to use register_user_hw_breakpoint() interface (presently un-exported but will be changed subsequently) to help maintain a system-wide consistent view on the availability of HW Breakpoint registers.
Presently I find plenty of set_debugreg() calls from kvm/ which will modify the registers directly and break the breakpoint register management brought-in through the patch.
Thanks,
K.Prasad
Quoted 14 lines · K.Prasad
Quoted 12 lines · Avi Kivity
Quoted 5 lines · K.Prasad
- Enable KGDB and KVM to use the
register_kernel_hw_breakpoint()interface for their HW Breakpoint usage, in the absence of which they will be broken during simultaneous use.KVM conceptually isn't a kernel use of the debug registers. KVM modifies the debug registers while the guest is running, and restores them after the guest returns.
Right now, as an optimization, KVM defers restoring the debug registers until after the next context switch out of the kvm task, or until the next exit to userspace (whichever comes earlier); we should change this to avoid the deferral if kernel breakpoints are in effect. This will allow simultaneous use of KVM breakpoints and kernel breakpoints.
Quoted 8 lines
The patch posted provides interface for using HW breakpoints on both user- and kernel-space breakpoints. KVM's user-space address breakpoint requirements, after code-modification, should be made to use
register_user_hw_breakpoint()interface (presently un-exported but will be changed subsequently) to help maintain a system-wide consistent view on the availability of HW Breakpoint registers.
Correcting myself, actually kvm breakpoints are in a third namespace. You could have kernel breakpoints, user breakpoints, and guest breakpoints coexisting.
Quoted 4 lines
Presently I find plenty of
set_debugreg()calls from kvm/ which will modify the registers directly and break the breakpoint register management brought-in through the patch.
If kvm restores the registers, should there be any problem?
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 34 lines · Avi Kivity
Quoted 20 lines · K.Prasad
Quoted 10 lines · Avi Kivity
Quoted 4 lines · K.Prasad
- Enable KGDB and KVM to use the
register_kernel_hw_breakpoint()interface for their HW Breakpoint usage, in the absence of which they will be broken during simultaneous use.KVM conceptually isn't a kernel use of the debug registers. KVM modifies the debug registers while the guest is running, and restores
them after the guest returns.Quoted 8 lines
Right now, as an optimization, KVM defers restoring the debug registers until after the next context switch out of the kvm task, or until the next exit to userspace (whichever comes earlier); we should change this to avoid the deferral if kernel breakpoints are in effect. This will allow simultaneous use of KVM breakpoints and kernel breakpoints.
Quoted 8 lines
The patch posted provides interface for using HW breakpoints on both user- and kernel-space breakpoints. KVM's user-space address breakpoint requirements, after code-modification, should be made to use
register_user_hw_breakpoint()interface (presently un-exported but will be changed subsequently) to help maintain a system-wide consistent view on the availability of HW Breakpoint registers.Correcting myself, actually kvm breakpoints are in a third namespace. You could have kernel breakpoints, user breakpoints, and guest breakpoints coexisting.
Quoted 7 lines
Quoted 4 lines
Presently I find plenty of
set_debugreg()calls from kvm/ which will modify the registers directly and break the breakpoint register management brought-in through the patch.If kvm restores the registers, should there be any problem?
No, it should be fine although I don't understand how the exception handler is invoked in KVM without the use of notifier or a hook in die_debug (or have they replaced code at a layer much below that?).
Apart from the doubt I've stated above, if they operate by replacing the breakpoint register contents before a context switch from KVM to other processes, they might in fact help maximise its utilisation.
Thanks for the clarification!
Thanks,
K.Prasad
Quoted 6 lines · K.Prasad
Quoted 6 lines
Quoted 6 lines
Presently I find plenty of
set_debugreg()calls from kvm/ which will modify the registers directly and break the breakpoint register management brought-in through the patch.
Quoted 8 lines
Quoted 3 lines
If kvm restores the registers, should there be any problem?
No, it should be fine although I don't understand how the exception handler is invoked in KVM without the use of notifier or a hook in
die_debug(or have they replaced code at a layer much below that?).
kvm debug traps don't work by raising a debug exception; instead, the guest #VMEXITs into the host. The kernel debug handler will not be invoked.
Quoted 4 lines
Apart from the doubt I've stated above, if they operate by replacing the breakpoint register contents before a context switch from KVM to other processes, they might in fact help maximise its utilisation.
Yes. As I mentioned, right now we avoid swapping the debug registers if we aren't going to context switch or exit to userspace, but if you give us a predicate that tells us whether you are interested in the debug registers, we can swap them eagerly rather than lazily.
Signature · 1 line
error compiling committee.c: too many arguments to function