diff options
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/kernel/kgdb.c | 171 |
1 files changed, 114 insertions, 57 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index dd74fe7273b1..62bea7307eaa 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
43 | #include <linux/smp.h> | 43 | #include <linux/smp.h> |
44 | #include <linux/nmi.h> | 44 | #include <linux/nmi.h> |
45 | #include <linux/hw_breakpoint.h> | ||
45 | 46 | ||
46 | #include <asm/debugreg.h> | 47 | #include <asm/debugreg.h> |
47 | #include <asm/apicdef.h> | 48 | #include <asm/apicdef.h> |
@@ -204,40 +205,38 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | |||
204 | 205 | ||
205 | static struct hw_breakpoint { | 206 | static struct hw_breakpoint { |
206 | unsigned enabled; | 207 | unsigned enabled; |
207 | unsigned type; | ||
208 | unsigned len; | ||
209 | unsigned long addr; | 208 | unsigned long addr; |
209 | int len; | ||
210 | int type; | ||
211 | struct perf_event **pev; | ||
210 | } breakinfo[4]; | 212 | } breakinfo[4]; |
211 | 213 | ||
212 | static void kgdb_correct_hw_break(void) | 214 | static void kgdb_correct_hw_break(void) |
213 | { | 215 | { |
214 | unsigned long dr7; | ||
215 | int correctit = 0; | ||
216 | int breakbit; | ||
217 | int breakno; | 216 | int breakno; |
218 | 217 | ||
219 | get_debugreg(dr7, 7); | ||
220 | for (breakno = 0; breakno < 4; breakno++) { | 218 | for (breakno = 0; breakno < 4; breakno++) { |
221 | breakbit = 2 << (breakno << 1); | 219 | struct perf_event *bp; |
222 | if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { | 220 | struct arch_hw_breakpoint *info; |
223 | correctit = 1; | 221 | int val; |
224 | dr7 |= breakbit; | 222 | int cpu = raw_smp_processor_id(); |
225 | dr7 &= ~(0xf0000 << (breakno << 2)); | 223 | if (!breakinfo[breakno].enabled) |
226 | dr7 |= ((breakinfo[breakno].len << 2) | | 224 | continue; |
227 | breakinfo[breakno].type) << | 225 | bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu); |
228 | ((breakno << 2) + 16); | 226 | info = counter_arch_bp(bp); |
229 | set_debugreg(breakinfo[breakno].addr, breakno); | 227 | if (bp->attr.disabled != 1) |
230 | 228 | continue; | |
231 | } else { | 229 | bp->attr.bp_addr = breakinfo[breakno].addr; |
232 | if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { | 230 | bp->attr.bp_len = breakinfo[breakno].len; |
233 | correctit = 1; | 231 | bp->attr.bp_type = breakinfo[breakno].type; |
234 | dr7 &= ~breakbit; | 232 | info->address = breakinfo[breakno].addr; |
235 | dr7 &= ~(0xf0000 << (breakno << 2)); | 233 | info->len = breakinfo[breakno].len; |
236 | } | 234 | info->type = breakinfo[breakno].type; |
237 | } | 235 | val = arch_install_hw_breakpoint(bp); |
236 | if (!val) | ||
237 | bp->attr.disabled = 0; | ||
238 | } | 238 | } |
239 | if (correctit) | 239 | hw_breakpoint_restore(); |
240 | set_debugreg(dr7, 7); | ||
241 | } | 240 | } |
242 | 241 | ||
243 | static int | 242 | static int |
@@ -259,15 +258,23 @@ kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) | |||
259 | static void kgdb_remove_all_hw_break(void) | 258 | static void kgdb_remove_all_hw_break(void) |
260 | { | 259 | { |
261 | int i; | 260 | int i; |
261 | int cpu = raw_smp_processor_id(); | ||
262 | struct perf_event *bp; | ||
262 | 263 | ||
263 | for (i = 0; i < 4; i++) | 264 | for (i = 0; i < 4; i++) { |
264 | memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); | 265 | if (!breakinfo[i].enabled) |
266 | continue; | ||
267 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); | ||
268 | if (bp->attr.disabled == 1) | ||
269 | continue; | ||
270 | arch_uninstall_hw_breakpoint(bp); | ||
271 | bp->attr.disabled = 1; | ||
272 | } | ||
265 | } | 273 | } |
266 | 274 | ||
267 | static int | 275 | static int |
268 | kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) | 276 | kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) |
269 | { | 277 | { |
270 | unsigned type; | ||
271 | int i; | 278 | int i; |
272 | 279 | ||
273 | for (i = 0; i < 4; i++) | 280 | for (i = 0; i < 4; i++) |
@@ -278,27 +285,38 @@ kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) | |||
278 | 285 | ||
279 | switch (bptype) { | 286 | switch (bptype) { |
280 | case BP_HARDWARE_BREAKPOINT: | 287 | case BP_HARDWARE_BREAKPOINT: |
281 | type = 0; | 288 | len = 1; |
282 | len = 1; | 289 | breakinfo[i].type = X86_BREAKPOINT_EXECUTE; |
283 | break; | 290 | break; |
284 | case BP_WRITE_WATCHPOINT: | 291 | case BP_WRITE_WATCHPOINT: |
285 | type = 1; | 292 | breakinfo[i].type = X86_BREAKPOINT_WRITE; |
286 | break; | 293 | break; |
287 | case BP_ACCESS_WATCHPOINT: | 294 | case BP_ACCESS_WATCHPOINT: |
288 | type = 3; | 295 | breakinfo[i].type = X86_BREAKPOINT_RW; |
289 | break; | 296 | break; |
290 | default: | 297 | default: |
291 | return -1; | 298 | return -1; |
292 | } | 299 | } |
293 | 300 | switch (len) { | |
294 | if (len == 1 || len == 2 || len == 4) | 301 | case 1: |
295 | breakinfo[i].len = len - 1; | 302 | breakinfo[i].len = X86_BREAKPOINT_LEN_1; |
296 | else | 303 | break; |
304 | case 2: | ||
305 | breakinfo[i].len = X86_BREAKPOINT_LEN_2; | ||
306 | break; | ||
307 | case 4: | ||
308 | breakinfo[i].len = X86_BREAKPOINT_LEN_4; | ||
309 | break; | ||
310 | #ifdef CONFIG_X86_64 | ||
311 | case 8: | ||
312 | breakinfo[i].len = X86_BREAKPOINT_LEN_8; | ||
313 | break; | ||
314 | #endif | ||
315 | default: | ||
297 | return -1; | 316 | return -1; |
298 | 317 | } | |
299 | breakinfo[i].enabled = 1; | ||
300 | breakinfo[i].addr = addr; | 318 | breakinfo[i].addr = addr; |
301 | breakinfo[i].type = type; | 319 | breakinfo[i].enabled = 1; |
302 | 320 | ||
303 | return 0; | 321 | return 0; |
304 | } | 322 | } |
@@ -313,8 +331,21 @@ kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) | |||
313 | */ | 331 | */ |
314 | void kgdb_disable_hw_debug(struct pt_regs *regs) | 332 | void kgdb_disable_hw_debug(struct pt_regs *regs) |
315 | { | 333 | { |
334 | int i; | ||
335 | int cpu = raw_smp_processor_id(); | ||
336 | struct perf_event *bp; | ||
337 | |||
316 | /* Disable hardware debugging while we are in kgdb: */ | 338 | /* Disable hardware debugging while we are in kgdb: */ |
317 | set_debugreg(0UL, 7); | 339 | set_debugreg(0UL, 7); |
340 | for (i = 0; i < 4; i++) { | ||
341 | if (!breakinfo[i].enabled) | ||
342 | continue; | ||
343 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); | ||
344 | if (bp->attr.disabled == 1) | ||
345 | continue; | ||
346 | arch_uninstall_hw_breakpoint(bp); | ||
347 | bp->attr.disabled = 1; | ||
348 | } | ||
318 | } | 349 | } |
319 | 350 | ||
320 | /** | 351 | /** |
@@ -378,7 +409,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, | |||
378 | struct pt_regs *linux_regs) | 409 | struct pt_regs *linux_regs) |
379 | { | 410 | { |
380 | unsigned long addr; | 411 | unsigned long addr; |
381 | unsigned long dr6; | ||
382 | char *ptr; | 412 | char *ptr; |
383 | int newPC; | 413 | int newPC; |
384 | 414 | ||
@@ -404,20 +434,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, | |||
404 | raw_smp_processor_id()); | 434 | raw_smp_processor_id()); |
405 | } | 435 | } |
406 | 436 | ||
407 | get_debugreg(dr6, 6); | ||
408 | if (!(dr6 & 0x4000)) { | ||
409 | int breakno; | ||
410 | |||
411 | for (breakno = 0; breakno < 4; breakno++) { | ||
412 | if (dr6 & (1 << breakno) && | ||
413 | breakinfo[breakno].type == 0) { | ||
414 | /* Set restore flag: */ | ||
415 | linux_regs->flags |= X86_EFLAGS_RF; | ||
416 | break; | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | set_debugreg(0UL, 6); | ||
421 | kgdb_correct_hw_break(); | 437 | kgdb_correct_hw_break(); |
422 | 438 | ||
423 | return 0; | 439 | return 0; |
@@ -485,8 +501,7 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd) | |||
485 | break; | 501 | break; |
486 | 502 | ||
487 | case DIE_DEBUG: | 503 | case DIE_DEBUG: |
488 | if (atomic_read(&kgdb_cpu_doing_single_step) == | 504 | if (atomic_read(&kgdb_cpu_doing_single_step) != -1) { |
489 | raw_smp_processor_id()) { | ||
490 | if (user_mode(regs)) | 505 | if (user_mode(regs)) |
491 | return single_step_cont(regs, args); | 506 | return single_step_cont(regs, args); |
492 | break; | 507 | break; |
@@ -539,7 +554,42 @@ static struct notifier_block kgdb_notifier = { | |||
539 | */ | 554 | */ |
540 | int kgdb_arch_init(void) | 555 | int kgdb_arch_init(void) |
541 | { | 556 | { |
542 | return register_die_notifier(&kgdb_notifier); | 557 | int i, cpu; |
558 | int ret; | ||
559 | struct perf_event_attr attr; | ||
560 | struct perf_event **pevent; | ||
561 | |||
562 | ret = register_die_notifier(&kgdb_notifier); | ||
563 | if (ret != 0) | ||
564 | return ret; | ||
565 | /* | ||
566 | * Pre-allocate the hw breakpoint structions in the non-atomic | ||
567 | * portion of kgdb because this operation requires mutexs to | ||
568 | * complete. | ||
569 | */ | ||
570 | attr.bp_addr = (unsigned long)kgdb_arch_init; | ||
571 | attr.type = PERF_TYPE_BREAKPOINT; | ||
572 | attr.bp_len = HW_BREAKPOINT_LEN_1; | ||
573 | attr.bp_type = HW_BREAKPOINT_W; | ||
574 | attr.disabled = 1; | ||
575 | for (i = 0; i < 4; i++) { | ||
576 | breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL); | ||
577 | if (IS_ERR(breakinfo[i].pev)) { | ||
578 | printk(KERN_ERR "kgdb: Could not allocate hw breakpoints\n"); | ||
579 | breakinfo[i].pev = NULL; | ||
580 | kgdb_arch_exit(); | ||
581 | return -1; | ||
582 | } | ||
583 | for_each_online_cpu(cpu) { | ||
584 | pevent = per_cpu_ptr(breakinfo[i].pev, cpu); | ||
585 | pevent[0]->hw.sample_period = 1; | ||
586 | if (pevent[0]->destroy != NULL) { | ||
587 | pevent[0]->destroy = NULL; | ||
588 | release_bp_slot(*pevent); | ||
589 | } | ||
590 | } | ||
591 | } | ||
592 | return ret; | ||
543 | } | 593 | } |
544 | 594 | ||
545 | /** | 595 | /** |
@@ -550,6 +600,13 @@ int kgdb_arch_init(void) | |||
550 | */ | 600 | */ |
551 | void kgdb_arch_exit(void) | 601 | void kgdb_arch_exit(void) |
552 | { | 602 | { |
603 | int i; | ||
604 | for (i = 0; i < 4; i++) { | ||
605 | if (breakinfo[i].pev) { | ||
606 | unregister_wide_hw_breakpoint(breakinfo[i].pev); | ||
607 | breakinfo[i].pev = NULL; | ||
608 | } | ||
609 | } | ||
553 | unregister_die_notifier(&kgdb_notifier); | 610 | unregister_die_notifier(&kgdb_notifier); |
554 | } | 611 | } |
555 | 612 | ||