diff options
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/kernel/hw_breakpoint.c | 382 |
3 files changed, 384 insertions, 1 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index df9e885eee14..3033375ed6bc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -46,6 +46,7 @@ config X86 | |||
46 | select HAVE_KERNEL_GZIP | 46 | select HAVE_KERNEL_GZIP |
47 | select HAVE_KERNEL_BZIP2 | 47 | select HAVE_KERNEL_BZIP2 |
48 | select HAVE_KERNEL_LZMA | 48 | select HAVE_KERNEL_LZMA |
49 | select HAVE_HW_BREAKPOINT | ||
49 | 50 | ||
50 | config ARCH_DEFCONFIG | 51 | config ARCH_DEFCONFIG |
51 | string | 52 | string |
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 77df4d654ff9..cbc781829173 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -36,7 +36,7 @@ obj-$(CONFIG_X86_64) += sys_x86_64.o x8664_ksyms_64.o | |||
36 | obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o | 36 | obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o |
37 | obj-y += bootflag.o e820.o | 37 | obj-y += bootflag.o e820.o |
38 | obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o | 38 | obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o |
39 | obj-y += alternative.o i8253.o pci-nommu.o | 39 | obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o |
40 | obj-y += tsc.o io_delay.o rtc.o | 40 | obj-y += tsc.o io_delay.o rtc.o |
41 | 41 | ||
42 | obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o | 42 | obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o |
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c new file mode 100644 index 000000000000..4867c9f3b5fb --- /dev/null +++ b/arch/x86/kernel/hw_breakpoint.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU General Public License | ||
13 | * along with this program; if not, write to the Free Software | ||
14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
15 | * | ||
16 | * Copyright (C) 2007 Alan Stern | ||
17 | * Copyright (C) 2009 IBM Corporation | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * HW_breakpoint: a unified kernel/user-space hardware breakpoint facility, | ||
22 | * using the CPU's debug registers. | ||
23 | */ | ||
24 | |||
25 | #include <linux/irqflags.h> | ||
26 | #include <linux/notifier.h> | ||
27 | #include <linux/kallsyms.h> | ||
28 | #include <linux/kprobes.h> | ||
29 | #include <linux/percpu.h> | ||
30 | #include <linux/kdebug.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/smp.h> | ||
36 | |||
37 | #include <asm/hw_breakpoint.h> | ||
38 | #include <asm/processor.h> | ||
39 | #include <asm/debugreg.h> | ||
40 | |||
41 | /* Unmasked kernel DR7 value */ | ||
42 | static unsigned long kdr7; | ||
43 | |||
44 | /* | ||
45 | * Masks for the bits corresponding to registers DR0 - DR3 in DR7 register. | ||
46 | * Used to clear and verify the status of bits corresponding to DR0 - DR3 | ||
47 | */ | ||
48 | static const unsigned long dr7_masks[HBP_NUM] = { | ||
49 | 0x000f0003, /* LEN0, R/W0, G0, L0 */ | ||
50 | 0x00f0000c, /* LEN1, R/W1, G1, L1 */ | ||
51 | 0x0f000030, /* LEN2, R/W2, G2, L2 */ | ||
52 | 0xf00000c0 /* LEN3, R/W3, G3, L3 */ | ||
53 | }; | ||
54 | |||
55 | |||
56 | /* | ||
57 | * Encode the length, type, Exact, and Enable bits for a particular breakpoint | ||
58 | * as stored in debug register 7. | ||
59 | */ | ||
60 | static unsigned long encode_dr7(int drnum, unsigned int len, unsigned int type) | ||
61 | { | ||
62 | unsigned long bp_info; | ||
63 | |||
64 | bp_info = (len | type) & 0xf; | ||
65 | bp_info <<= (DR_CONTROL_SHIFT + drnum * DR_CONTROL_SIZE); | ||
66 | bp_info |= (DR_GLOBAL_ENABLE << (drnum * DR_ENABLE_SIZE)) | | ||
67 | DR_GLOBAL_SLOWDOWN; | ||
68 | return bp_info; | ||
69 | } | ||
70 | |||
71 | void arch_update_kernel_hw_breakpoint(void *unused) | ||
72 | { | ||
73 | struct hw_breakpoint *bp; | ||
74 | int i, cpu = get_cpu(); | ||
75 | unsigned long temp_kdr7 = 0; | ||
76 | |||
77 | /* Don't allow debug exceptions while we update the registers */ | ||
78 | set_debugreg(0UL, 7); | ||
79 | |||
80 | for (i = hbp_kernel_pos; i < HBP_NUM; i++) { | ||
81 | per_cpu(this_hbp_kernel[i], cpu) = bp = hbp_kernel[i]; | ||
82 | if (bp) { | ||
83 | temp_kdr7 |= encode_dr7(i, bp->info.len, bp->info.type); | ||
84 | set_debugreg(bp->info.address, i); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | /* No need to set DR6. Update the debug registers with kernel-space | ||
89 | * breakpoint values from kdr7 and user-space requests from the | ||
90 | * current process | ||
91 | */ | ||
92 | kdr7 = temp_kdr7; | ||
93 | set_debugreg(kdr7 | current->thread.debugreg7, 7); | ||
94 | put_cpu_no_resched(); | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * Install the thread breakpoints in their debug registers. | ||
99 | */ | ||
100 | void arch_install_thread_hw_breakpoint(struct task_struct *tsk) | ||
101 | { | ||
102 | struct thread_struct *thread = &(tsk->thread); | ||
103 | |||
104 | switch (hbp_kernel_pos) { | ||
105 | case 4: | ||
106 | set_debugreg(thread->debugreg[3], 3); | ||
107 | case 3: | ||
108 | set_debugreg(thread->debugreg[2], 2); | ||
109 | case 2: | ||
110 | set_debugreg(thread->debugreg[1], 1); | ||
111 | case 1: | ||
112 | set_debugreg(thread->debugreg[0], 0); | ||
113 | default: | ||
114 | break; | ||
115 | } | ||
116 | |||
117 | /* No need to set DR6 */ | ||
118 | set_debugreg((kdr7 | thread->debugreg7), 7); | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Install the debug register values for just the kernel, no thread. | ||
123 | */ | ||
124 | void arch_uninstall_thread_hw_breakpoint() | ||
125 | { | ||
126 | /* Clear the user-space portion of debugreg7 by setting only kdr7 */ | ||
127 | set_debugreg(kdr7, 7); | ||
128 | |||
129 | } | ||
130 | |||
131 | static int get_hbp_len(u8 hbp_len) | ||
132 | { | ||
133 | unsigned int len_in_bytes = 0; | ||
134 | |||
135 | switch (hbp_len) { | ||
136 | case HW_BREAKPOINT_LEN_1: | ||
137 | len_in_bytes = 1; | ||
138 | break; | ||
139 | case HW_BREAKPOINT_LEN_2: | ||
140 | len_in_bytes = 2; | ||
141 | break; | ||
142 | case HW_BREAKPOINT_LEN_4: | ||
143 | len_in_bytes = 4; | ||
144 | break; | ||
145 | #ifdef CONFIG_X86_64 | ||
146 | case HW_BREAKPOINT_LEN_8: | ||
147 | len_in_bytes = 8; | ||
148 | break; | ||
149 | #endif | ||
150 | } | ||
151 | return len_in_bytes; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Check for virtual address in user space. | ||
156 | */ | ||
157 | int arch_check_va_in_userspace(unsigned long va, u8 hbp_len) | ||
158 | { | ||
159 | unsigned int len; | ||
160 | |||
161 | len = get_hbp_len(hbp_len); | ||
162 | |||
163 | return (va <= TASK_SIZE - len); | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * Check for virtual address in kernel space. | ||
168 | */ | ||
169 | int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) | ||
170 | { | ||
171 | unsigned int len; | ||
172 | |||
173 | len = get_hbp_len(hbp_len); | ||
174 | |||
175 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Store a breakpoint's encoded address, length, and type. | ||
180 | */ | ||
181 | static int arch_store_info(struct hw_breakpoint *bp, struct task_struct *tsk) | ||
182 | { | ||
183 | /* | ||
184 | * User-space requests will always have the address field populated | ||
185 | * Symbol names from user-space are rejected | ||
186 | */ | ||
187 | if (tsk && bp->info.name) | ||
188 | return -EINVAL; | ||
189 | /* | ||
190 | * For kernel-addresses, either the address or symbol name can be | ||
191 | * specified. | ||
192 | */ | ||
193 | if (bp->info.name) | ||
194 | bp->info.address = (unsigned long) | ||
195 | kallsyms_lookup_name(bp->info.name); | ||
196 | if (bp->info.address) | ||
197 | return 0; | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Validate the arch-specific HW Breakpoint register settings | ||
203 | */ | ||
204 | int arch_validate_hwbkpt_settings(struct hw_breakpoint *bp, | ||
205 | struct task_struct *tsk) | ||
206 | { | ||
207 | unsigned int align; | ||
208 | int ret = -EINVAL; | ||
209 | |||
210 | switch (bp->info.type) { | ||
211 | /* | ||
212 | * Ptrace-refactoring code | ||
213 | * For now, we'll allow instruction breakpoint only for user-space | ||
214 | * addresses | ||
215 | */ | ||
216 | case HW_BREAKPOINT_EXECUTE: | ||
217 | if ((!arch_check_va_in_userspace(bp->info.address, | ||
218 | bp->info.len)) && | ||
219 | bp->info.len != HW_BREAKPOINT_LEN_EXECUTE) | ||
220 | return ret; | ||
221 | break; | ||
222 | case HW_BREAKPOINT_WRITE: | ||
223 | break; | ||
224 | case HW_BREAKPOINT_RW: | ||
225 | break; | ||
226 | default: | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | switch (bp->info.len) { | ||
231 | case HW_BREAKPOINT_LEN_1: | ||
232 | align = 0; | ||
233 | break; | ||
234 | case HW_BREAKPOINT_LEN_2: | ||
235 | align = 1; | ||
236 | break; | ||
237 | case HW_BREAKPOINT_LEN_4: | ||
238 | align = 3; | ||
239 | break; | ||
240 | #ifdef CONFIG_X86_64 | ||
241 | case HW_BREAKPOINT_LEN_8: | ||
242 | align = 7; | ||
243 | break; | ||
244 | #endif | ||
245 | default: | ||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | if (bp->triggered) | ||
250 | ret = arch_store_info(bp, tsk); | ||
251 | |||
252 | if (ret < 0) | ||
253 | return ret; | ||
254 | /* | ||
255 | * Check that the low-order bits of the address are appropriate | ||
256 | * for the alignment implied by len. | ||
257 | */ | ||
258 | if (bp->info.address & align) | ||
259 | return -EINVAL; | ||
260 | |||
261 | /* Check that the virtual address is in the proper range */ | ||
262 | if (tsk) { | ||
263 | if (!arch_check_va_in_userspace(bp->info.address, bp->info.len)) | ||
264 | return -EFAULT; | ||
265 | } else { | ||
266 | if (!arch_check_va_in_kernelspace(bp->info.address, | ||
267 | bp->info.len)) | ||
268 | return -EFAULT; | ||
269 | } | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | void arch_update_user_hw_breakpoint(int pos, struct task_struct *tsk) | ||
274 | { | ||
275 | struct thread_struct *thread = &(tsk->thread); | ||
276 | struct hw_breakpoint *bp = thread->hbp[pos]; | ||
277 | |||
278 | thread->debugreg7 &= ~dr7_masks[pos]; | ||
279 | if (bp) { | ||
280 | thread->debugreg[pos] = bp->info.address; | ||
281 | thread->debugreg7 |= encode_dr7(pos, bp->info.len, | ||
282 | bp->info.type); | ||
283 | } else | ||
284 | thread->debugreg[pos] = 0; | ||
285 | } | ||
286 | |||
287 | void arch_flush_thread_hw_breakpoint(struct task_struct *tsk) | ||
288 | { | ||
289 | int i; | ||
290 | struct thread_struct *thread = &(tsk->thread); | ||
291 | |||
292 | thread->debugreg7 = 0; | ||
293 | for (i = 0; i < HBP_NUM; i++) | ||
294 | thread->debugreg[i] = 0; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * Handle debug exception notifications. | ||
299 | * | ||
300 | * Return value is either NOTIFY_STOP or NOTIFY_DONE as explained below. | ||
301 | * | ||
302 | * NOTIFY_DONE returned if one of the following conditions is true. | ||
303 | * i) When the causative address is from user-space and the exception | ||
304 | * is a valid one, i.e. not triggered as a result of lazy debug register | ||
305 | * switching | ||
306 | * ii) When there are more bits than trap<n> set in DR6 register (such | ||
307 | * as BD, BS or BT) indicating that more than one debug condition is | ||
308 | * met and requires some more action in do_debug(). | ||
309 | * | ||
310 | * NOTIFY_STOP returned for all other cases | ||
311 | * | ||
312 | */ | ||
313 | int __kprobes hw_breakpoint_handler(struct die_args *args) | ||
314 | { | ||
315 | int i, cpu, rc = NOTIFY_STOP; | ||
316 | struct hw_breakpoint *bp; | ||
317 | /* The DR6 value is stored in args->err */ | ||
318 | unsigned long dr7, dr6 = args->err; | ||
319 | |||
320 | /* Do an early return if no trap bits are set in DR6 */ | ||
321 | if ((dr6 & DR_TRAP_BITS) == 0) | ||
322 | return NOTIFY_DONE; | ||
323 | |||
324 | /* Lazy debug register switching */ | ||
325 | if (!test_tsk_thread_flag(current, TIF_DEBUG)) | ||
326 | arch_uninstall_thread_hw_breakpoint(); | ||
327 | |||
328 | get_debugreg(dr7, 7); | ||
329 | /* Disable breakpoints during exception handling */ | ||
330 | set_debugreg(0UL, 7); | ||
331 | /* | ||
332 | * Assert that local interrupts are disabled | ||
333 | * Reset the DRn bits in the virtualized register value. | ||
334 | * The ptrace trigger routine will add in whatever is needed. | ||
335 | */ | ||
336 | current->thread.debugreg6 &= ~DR_TRAP_BITS; | ||
337 | cpu = get_cpu(); | ||
338 | |||
339 | /* Handle all the breakpoints that were triggered */ | ||
340 | for (i = 0; i < HBP_NUM; ++i) { | ||
341 | if (likely(!(dr6 & (DR_TRAP0 << i)))) | ||
342 | continue; | ||
343 | /* | ||
344 | * Find the corresponding hw_breakpoint structure and | ||
345 | * invoke its triggered callback. | ||
346 | */ | ||
347 | if (i >= hbp_kernel_pos) | ||
348 | bp = per_cpu(this_hbp_kernel[i], cpu); | ||
349 | else { | ||
350 | bp = current->thread.hbp[i]; | ||
351 | if (bp) | ||
352 | rc = NOTIFY_DONE; | ||
353 | } | ||
354 | /* | ||
355 | * bp can be NULL due to lazy debug register switching | ||
356 | * or due to the delay between updates of hbp_kernel_pos | ||
357 | * and this_hbp_kernel. | ||
358 | */ | ||
359 | if (!bp) | ||
360 | continue; | ||
361 | |||
362 | (bp->triggered)(bp, args->regs); | ||
363 | } | ||
364 | if (dr6 & (~DR_TRAP_BITS)) | ||
365 | rc = NOTIFY_DONE; | ||
366 | |||
367 | set_debugreg(dr7, 7); | ||
368 | put_cpu_no_resched(); | ||
369 | return rc; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * Handle debug exception notifications. | ||
374 | */ | ||
375 | int __kprobes hw_breakpoint_exceptions_notify( | ||
376 | struct notifier_block *unused, unsigned long val, void *data) | ||
377 | { | ||
378 | if (val != DIE_DEBUG) | ||
379 | return NOTIFY_DONE; | ||
380 | |||
381 | return hw_breakpoint_handler(data); | ||
382 | } | ||