aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorK.Prasad <prasad@linux.vnet.ibm.com>2009-06-01 14:13:10 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2009-06-02 16:46:57 -0400
commitb332828c39326b1dca617f387dd15d12e81cd5f0 (patch)
treeb8cff4bd7a5b42a723e26d69cce4f3030bf68e62
parent43bd1236234cacbc18d1476a9b57e7a306efddf5 (diff)
hw-breakpoints: prepare the code for Hardware Breakpoint interfaces
The generic hardware breakpoint interface provides an abstraction of hardware breakpoints in front of specific arch implementations for both kernel and user side breakpoints. This includes execution breakpoints and read/write breakpoints, also known as "watchpoints". This patch introduces header files containing constants, structure definitions and declaration of functions used by the hardware breakpoint core and x86 specific code. It also introduces an array based storage for the debug-register values in 'struct thread_struct', while modifying all users of debugreg<n> member in the structure. [ Impact: add headers for new hardware breakpoint interface ] Original-patch-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com> Reviewed-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
-rw-r--r--arch/x86/include/asm/a.out-core.h8
-rw-r--r--arch/x86/include/asm/debugreg.h29
-rw-r--r--arch/x86/include/asm/hw_breakpoint.h55
-rw-r--r--arch/x86/include/asm/processor.h8
-rw-r--r--arch/x86/kernel/process.c16
-rw-r--r--arch/x86/kernel/ptrace.c16
-rw-r--r--arch/x86/power/cpu_32.c8
-rw-r--r--arch/x86/power/cpu_64.c8
-rw-r--r--include/asm-generic/hw_breakpoint.h139
9 files changed, 255 insertions, 32 deletions
diff --git a/arch/x86/include/asm/a.out-core.h b/arch/x86/include/asm/a.out-core.h
index bb70e397aa84..fc4685dd6e4d 100644
--- a/arch/x86/include/asm/a.out-core.h
+++ b/arch/x86/include/asm/a.out-core.h
@@ -32,10 +32,10 @@ static inline void aout_dump_thread(struct pt_regs *regs, struct user *dump)
32 >> PAGE_SHIFT; 32 >> PAGE_SHIFT;
33 dump->u_dsize -= dump->u_tsize; 33 dump->u_dsize -= dump->u_tsize;
34 dump->u_ssize = 0; 34 dump->u_ssize = 0;
35 dump->u_debugreg[0] = current->thread.debugreg0; 35 dump->u_debugreg[0] = current->thread.debugreg[0];
36 dump->u_debugreg[1] = current->thread.debugreg1; 36 dump->u_debugreg[1] = current->thread.debugreg[1];
37 dump->u_debugreg[2] = current->thread.debugreg2; 37 dump->u_debugreg[2] = current->thread.debugreg[2];
38 dump->u_debugreg[3] = current->thread.debugreg3; 38 dump->u_debugreg[3] = current->thread.debugreg[3];
39 dump->u_debugreg[4] = 0; 39 dump->u_debugreg[4] = 0;
40 dump->u_debugreg[5] = 0; 40 dump->u_debugreg[5] = 0;
41 dump->u_debugreg[6] = current->thread.debugreg6; 41 dump->u_debugreg[6] = current->thread.debugreg6;
diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h
index 3ea6f37be9e2..23439fbb1d0e 100644
--- a/arch/x86/include/asm/debugreg.h
+++ b/arch/x86/include/asm/debugreg.h
@@ -18,6 +18,7 @@
18#define DR_TRAP1 (0x2) /* db1 */ 18#define DR_TRAP1 (0x2) /* db1 */
19#define DR_TRAP2 (0x4) /* db2 */ 19#define DR_TRAP2 (0x4) /* db2 */
20#define DR_TRAP3 (0x8) /* db3 */ 20#define DR_TRAP3 (0x8) /* db3 */
21#define DR_TRAP_BITS (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)
21 22
22#define DR_STEP (0x4000) /* single-step */ 23#define DR_STEP (0x4000) /* single-step */
23#define DR_SWITCH (0x8000) /* task switch */ 24#define DR_SWITCH (0x8000) /* task switch */
@@ -49,6 +50,8 @@
49 50
50#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit */ 51#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit */
51#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit */ 52#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit */
53#define DR_LOCAL_ENABLE (0x1) /* Local enable for reg 0 */
54#define DR_GLOBAL_ENABLE (0x2) /* Global enable for reg 0 */
52#define DR_ENABLE_SIZE 2 /* 2 enable bits per register */ 55#define DR_ENABLE_SIZE 2 /* 2 enable bits per register */
53 56
54#define DR_LOCAL_ENABLE_MASK (0x55) /* Set local bits for all 4 regs */ 57#define DR_LOCAL_ENABLE_MASK (0x55) /* Set local bits for all 4 regs */
@@ -67,4 +70,30 @@
67#define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */ 70#define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */
68#define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */ 71#define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */
69 72
73/*
74 * HW breakpoint additions
75 */
76#ifdef __KERNEL__
77
78/* For process management */
79extern void flush_thread_hw_breakpoint(struct task_struct *tsk);
80extern int copy_thread_hw_breakpoint(struct task_struct *tsk,
81 struct task_struct *child, unsigned long clone_flags);
82
83/* For CPU management */
84extern void load_debug_registers(void);
85static inline void hw_breakpoint_disable(void)
86{
87 /* Zero the control register for HW Breakpoint */
88 set_debugreg(0UL, 7);
89
90 /* Zero-out the individual HW breakpoint address registers */
91 set_debugreg(0UL, 0);
92 set_debugreg(0UL, 1);
93 set_debugreg(0UL, 2);
94 set_debugreg(0UL, 3);
95}
96
97#endif /* __KERNEL__ */
98
70#endif /* _ASM_X86_DEBUGREG_H */ 99#endif /* _ASM_X86_DEBUGREG_H */
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h
new file mode 100644
index 000000000000..1acb4d45de70
--- /dev/null
+++ b/arch/x86/include/asm/hw_breakpoint.h
@@ -0,0 +1,55 @@
1#ifndef _I386_HW_BREAKPOINT_H
2#define _I386_HW_BREAKPOINT_H
3
4#ifdef __KERNEL__
5#define __ARCH_HW_BREAKPOINT_H
6
7struct arch_hw_breakpoint {
8 char *name; /* Contains name of the symbol to set bkpt */
9 unsigned long address;
10 u8 len;
11 u8 type;
12};
13
14#include <linux/kdebug.h>
15#include <asm-generic/hw_breakpoint.h>
16
17/* Available HW breakpoint length encodings */
18#define HW_BREAKPOINT_LEN_1 0x40
19#define HW_BREAKPOINT_LEN_2 0x44
20#define HW_BREAKPOINT_LEN_4 0x4c
21#define HW_BREAKPOINT_LEN_EXECUTE 0x40
22
23#ifdef CONFIG_X86_64
24#define HW_BREAKPOINT_LEN_8 0x48
25#endif
26
27/* Available HW breakpoint type encodings */
28
29/* trigger on instruction execute */
30#define HW_BREAKPOINT_EXECUTE 0x80
31/* trigger on memory write */
32#define HW_BREAKPOINT_WRITE 0x81
33/* trigger on memory read or write */
34#define HW_BREAKPOINT_RW 0x83
35
36/* Total number of available HW breakpoint registers */
37#define HBP_NUM 4
38
39extern struct hw_breakpoint *hbp_kernel[HBP_NUM];
40DECLARE_PER_CPU(struct hw_breakpoint*, this_hbp_kernel[HBP_NUM]);
41extern unsigned int hbp_user_refcount[HBP_NUM];
42
43extern void arch_install_thread_hw_breakpoint(struct task_struct *tsk);
44extern void arch_uninstall_thread_hw_breakpoint(void);
45extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len);
46extern int arch_validate_hwbkpt_settings(struct hw_breakpoint *bp,
47 struct task_struct *tsk);
48extern void arch_update_user_hw_breakpoint(int pos, struct task_struct *tsk);
49extern void arch_flush_thread_hw_breakpoint(struct task_struct *tsk);
50extern void arch_update_kernel_hw_breakpoint(void *);
51extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
52 unsigned long val, void *data);
53#endif /* __KERNEL__ */
54#endif /* _I386_HW_BREAKPOINT_H */
55
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 0b2fab0051e0..448b34a8e393 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -29,6 +29,7 @@ struct mm_struct;
29#include <linux/threads.h> 29#include <linux/threads.h>
30#include <linux/init.h> 30#include <linux/init.h>
31 31
32#define HBP_NUM 4
32/* 33/*
33 * Default implementation of macro that returns current 34 * Default implementation of macro that returns current
34 * instruction pointer ("program counter"). 35 * instruction pointer ("program counter").
@@ -431,12 +432,11 @@ struct thread_struct {
431 unsigned long fs; 432 unsigned long fs;
432 unsigned long gs; 433 unsigned long gs;
433 /* Hardware debugging registers: */ 434 /* Hardware debugging registers: */
434 unsigned long debugreg0; 435 unsigned long debugreg[HBP_NUM];
435 unsigned long debugreg1;
436 unsigned long debugreg2;
437 unsigned long debugreg3;
438 unsigned long debugreg6; 436 unsigned long debugreg6;
439 unsigned long debugreg7; 437 unsigned long debugreg7;
438 /* Hardware breakpoint info */
439 struct hw_breakpoint *hbp[HBP_NUM];
440 /* Fault info: */ 440 /* Fault info: */
441 unsigned long cr2; 441 unsigned long cr2;
442 unsigned long trap_no; 442 unsigned long trap_no;
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index fb5dfb891f0f..291527cb438a 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -106,10 +106,10 @@ void flush_thread(void)
106 106
107 clear_tsk_thread_flag(tsk, TIF_DEBUG); 107 clear_tsk_thread_flag(tsk, TIF_DEBUG);
108 108
109 tsk->thread.debugreg0 = 0; 109 tsk->thread.debugreg[0] = 0;
110 tsk->thread.debugreg1 = 0; 110 tsk->thread.debugreg[1] = 0;
111 tsk->thread.debugreg2 = 0; 111 tsk->thread.debugreg[2] = 0;
112 tsk->thread.debugreg3 = 0; 112 tsk->thread.debugreg[3] = 0;
113 tsk->thread.debugreg6 = 0; 113 tsk->thread.debugreg6 = 0;
114 tsk->thread.debugreg7 = 0; 114 tsk->thread.debugreg7 = 0;
115 memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); 115 memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
@@ -194,10 +194,10 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
194 update_debugctlmsr(next->debugctlmsr); 194 update_debugctlmsr(next->debugctlmsr);
195 195
196 if (test_tsk_thread_flag(next_p, TIF_DEBUG)) { 196 if (test_tsk_thread_flag(next_p, TIF_DEBUG)) {
197 set_debugreg(next->debugreg0, 0); 197 set_debugreg(next->debugreg[0], 0);
198 set_debugreg(next->debugreg1, 1); 198 set_debugreg(next->debugreg[1], 1);
199 set_debugreg(next->debugreg2, 2); 199 set_debugreg(next->debugreg[2], 2);
200 set_debugreg(next->debugreg3, 3); 200 set_debugreg(next->debugreg[3], 3);
201 /* no 4 and 5 */ 201 /* no 4 and 5 */
202 set_debugreg(next->debugreg6, 6); 202 set_debugreg(next->debugreg6, 6);
203 set_debugreg(next->debugreg7, 7); 203 set_debugreg(next->debugreg7, 7);
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 09ecbde91c13..313be40be55a 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -471,10 +471,10 @@ static int genregs_set(struct task_struct *target,
471static unsigned long ptrace_get_debugreg(struct task_struct *child, int n) 471static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
472{ 472{
473 switch (n) { 473 switch (n) {
474 case 0: return child->thread.debugreg0; 474 case 0: return child->thread.debugreg[0];
475 case 1: return child->thread.debugreg1; 475 case 1: return child->thread.debugreg[1];
476 case 2: return child->thread.debugreg2; 476 case 2: return child->thread.debugreg[2];
477 case 3: return child->thread.debugreg3; 477 case 3: return child->thread.debugreg[3];
478 case 6: return child->thread.debugreg6; 478 case 6: return child->thread.debugreg6;
479 case 7: return child->thread.debugreg7; 479 case 7: return child->thread.debugreg7;
480 } 480 }
@@ -493,10 +493,10 @@ static int ptrace_set_debugreg(struct task_struct *child,
493 return -EIO; 493 return -EIO;
494 494
495 switch (n) { 495 switch (n) {
496 case 0: child->thread.debugreg0 = data; break; 496 case 0: child->thread.debugreg[0] = data; break;
497 case 1: child->thread.debugreg1 = data; break; 497 case 1: child->thread.debugreg[1] = data; break;
498 case 2: child->thread.debugreg2 = data; break; 498 case 2: child->thread.debugreg[2] = data; break;
499 case 3: child->thread.debugreg3 = data; break; 499 case 3: child->thread.debugreg[3] = data; break;
500 500
501 case 6: 501 case 6:
502 if ((data & ~0xffffffffUL) != 0) 502 if ((data & ~0xffffffffUL) != 0)
diff --git a/arch/x86/power/cpu_32.c b/arch/x86/power/cpu_32.c
index ce702c5b3a2c..519913948003 100644
--- a/arch/x86/power/cpu_32.c
+++ b/arch/x86/power/cpu_32.c
@@ -84,10 +84,10 @@ static void fix_processor_context(void)
84 * Now maybe reload the debug registers 84 * Now maybe reload the debug registers
85 */ 85 */
86 if (current->thread.debugreg7) { 86 if (current->thread.debugreg7) {
87 set_debugreg(current->thread.debugreg0, 0); 87 set_debugreg(current->thread.debugreg[0], 0);
88 set_debugreg(current->thread.debugreg1, 1); 88 set_debugreg(current->thread.debugreg[1], 1);
89 set_debugreg(current->thread.debugreg2, 2); 89 set_debugreg(current->thread.debugreg[2], 2);
90 set_debugreg(current->thread.debugreg3, 3); 90 set_debugreg(current->thread.debugreg[3], 3);
91 /* no 4 and 5 */ 91 /* no 4 and 5 */
92 set_debugreg(current->thread.debugreg6, 6); 92 set_debugreg(current->thread.debugreg6, 6);
93 set_debugreg(current->thread.debugreg7, 7); 93 set_debugreg(current->thread.debugreg7, 7);
diff --git a/arch/x86/power/cpu_64.c b/arch/x86/power/cpu_64.c
index 5343540f2607..1e3bdcc959ff 100644
--- a/arch/x86/power/cpu_64.c
+++ b/arch/x86/power/cpu_64.c
@@ -163,10 +163,10 @@ static void fix_processor_context(void)
163 * Now maybe reload the debug registers 163 * Now maybe reload the debug registers
164 */ 164 */
165 if (current->thread.debugreg7){ 165 if (current->thread.debugreg7){
166 loaddebug(&current->thread, 0); 166 set_debugreg(current->thread.debugreg[0], 0);
167 loaddebug(&current->thread, 1); 167 set_debugreg(current->thread.debugreg[1], 1);
168 loaddebug(&current->thread, 2); 168 set_debugreg(current->thread.debugreg[2], 2);
169 loaddebug(&current->thread, 3); 169 set_debugreg(current->thread.debugreg[3], 3);
170 /* no 4 and 5 */ 170 /* no 4 and 5 */
171 loaddebug(&current->thread, 6); 171 loaddebug(&current->thread, 6);
172 loaddebug(&current->thread, 7); 172 loaddebug(&current->thread, 7);
diff --git a/include/asm-generic/hw_breakpoint.h b/include/asm-generic/hw_breakpoint.h
new file mode 100644
index 000000000000..9bf2d12eb74a
--- /dev/null
+++ b/include/asm-generic/hw_breakpoint.h
@@ -0,0 +1,139 @@
1#ifndef _ASM_GENERIC_HW_BREAKPOINT_H
2#define _ASM_GENERIC_HW_BREAKPOINT_H
3
4#ifndef __ARCH_HW_BREAKPOINT_H
5#error "Please don't include this file directly"
6#endif
7
8#ifdef __KERNEL__
9#include <linux/list.h>
10#include <linux/types.h>
11#include <linux/kallsyms.h>
12
13/**
14 * struct hw_breakpoint - unified kernel/user-space hardware breakpoint
15 * @triggered: callback invoked after target address access
16 * @info: arch-specific breakpoint info (address, length, and type)
17 *
18 * %hw_breakpoint structures are the kernel's way of representing
19 * hardware breakpoints. These are data breakpoints
20 * (also known as "watchpoints", triggered on data access), and the breakpoint's
21 * target address can be located in either kernel space or user space.
22 *
23 * The breakpoint's address, length, and type are highly
24 * architecture-specific. The values are encoded in the @info field; you
25 * specify them when registering the breakpoint. To examine the encoded
26 * values use hw_breakpoint_get_{kaddress,uaddress,len,type}(), declared
27 * below.
28 *
29 * The address is specified as a regular kernel pointer (for kernel-space
30 * breakponts) or as an %__user pointer (for user-space breakpoints).
31 * With register_user_hw_breakpoint(), the address must refer to a
32 * location in user space. The breakpoint will be active only while the
33 * requested task is running. Conversely with
34 * register_kernel_hw_breakpoint(), the address must refer to a location
35 * in kernel space, and the breakpoint will be active on all CPUs
36 * regardless of the current task.
37 *
38 * The length is the breakpoint's extent in bytes, which is subject to
39 * certain limitations. include/asm/hw_breakpoint.h contains macros
40 * defining the available lengths for a specific architecture. Note that
41 * the address's alignment must match the length. The breakpoint will
42 * catch accesses to any byte in the range from address to address +
43 * (length - 1).
44 *
45 * The breakpoint's type indicates the sort of access that will cause it
46 * to trigger. Possible values may include:
47 *
48 * %HW_BREAKPOINT_RW (triggered on read or write access),
49 * %HW_BREAKPOINT_WRITE (triggered on write access), and
50 * %HW_BREAKPOINT_READ (triggered on read access).
51 *
52 * Appropriate macros are defined in include/asm/hw_breakpoint.h; not all
53 * possibilities are available on all architectures. Execute breakpoints
54 * must have length equal to the special value %HW_BREAKPOINT_LEN_EXECUTE.
55 *
56 * When a breakpoint gets hit, the @triggered callback is
57 * invoked in_interrupt with a pointer to the %hw_breakpoint structure and the
58 * processor registers.
59 * Data breakpoints occur after the memory access has taken place.
60 * Breakpoints are disabled during execution @triggered, to avoid
61 * recursive traps and allow unhindered access to breakpointed memory.
62 *
63 * This sample code sets a breakpoint on pid_max and registers a callback
64 * function for writes to that variable. Note that it is not portable
65 * as written, because not all architectures support HW_BREAKPOINT_LEN_4.
66 *
67 * ----------------------------------------------------------------------
68 *
69 * #include <asm/hw_breakpoint.h>
70 *
71 * struct hw_breakpoint my_bp;
72 *
73 * static void my_triggered(struct hw_breakpoint *bp, struct pt_regs *regs)
74 * {
75 * printk(KERN_DEBUG "Inside triggered routine of breakpoint exception\n");
76 * dump_stack();
77 * .......<more debugging output>........
78 * }
79 *
80 * static struct hw_breakpoint my_bp;
81 *
82 * static int init_module(void)
83 * {
84 * ..........<do anything>............
85 * my_bp.info.type = HW_BREAKPOINT_WRITE;
86 * my_bp.info.len = HW_BREAKPOINT_LEN_4;
87 *
88 * my_bp.installed = (void *)my_bp_installed;
89 *
90 * rc = register_kernel_hw_breakpoint(&my_bp);
91 * ..........<do anything>............
92 * }
93 *
94 * static void cleanup_module(void)
95 * {
96 * ..........<do anything>............
97 * unregister_kernel_hw_breakpoint(&my_bp);
98 * ..........<do anything>............
99 * }
100 *
101 * ----------------------------------------------------------------------
102 */
103struct hw_breakpoint {
104 void (*triggered)(struct hw_breakpoint *, struct pt_regs *);
105 struct arch_hw_breakpoint info;
106};
107
108/*
109 * len and type values are defined in include/asm/hw_breakpoint.h.
110 * Available values vary according to the architecture. On i386 the
111 * possibilities are:
112 *
113 * HW_BREAKPOINT_LEN_1
114 * HW_BREAKPOINT_LEN_2
115 * HW_BREAKPOINT_LEN_4
116 * HW_BREAKPOINT_RW
117 * HW_BREAKPOINT_READ
118 *
119 * On other architectures HW_BREAKPOINT_LEN_8 may be available, and the
120 * 1-, 2-, and 4-byte lengths may be unavailable. There also may be
121 * HW_BREAKPOINT_WRITE. You can use #ifdef to check at compile time.
122 */
123
124extern int register_user_hw_breakpoint(struct task_struct *tsk,
125 struct hw_breakpoint *bp);
126extern int modify_user_hw_breakpoint(struct task_struct *tsk,
127 struct hw_breakpoint *bp);
128extern void unregister_user_hw_breakpoint(struct task_struct *tsk,
129 struct hw_breakpoint *bp);
130/*
131 * Kernel breakpoints are not associated with any particular thread.
132 */
133extern int register_kernel_hw_breakpoint(struct hw_breakpoint *bp);
134extern void unregister_kernel_hw_breakpoint(struct hw_breakpoint *bp);
135
136extern unsigned int hbp_kernel_pos;
137
138#endif /* __KERNEL__ */
139#endif /* _ASM_GENERIC_HW_BREAKPOINT_H */