diff options
| -rw-r--r-- | arch/x86/kernel/kgdb.c | 171 | ||||
| -rw-r--r-- | kernel/kgdb.c | 3 |
2 files changed, 117 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 | ||
diff --git a/kernel/kgdb.c b/kernel/kgdb.c index 2eb517e23514..c7ade62e4ef0 100644 --- a/kernel/kgdb.c +++ b/kernel/kgdb.c | |||
| @@ -583,6 +583,9 @@ static void kgdb_wait(struct pt_regs *regs) | |||
| 583 | smp_wmb(); | 583 | smp_wmb(); |
| 584 | atomic_set(&cpu_in_kgdb[cpu], 1); | 584 | atomic_set(&cpu_in_kgdb[cpu], 1); |
| 585 | 585 | ||
| 586 | /* Disable any cpu specific hw breakpoints */ | ||
| 587 | kgdb_disable_hw_debug(regs); | ||
| 588 | |||
| 586 | /* Wait till primary CPU is done with debugging */ | 589 | /* Wait till primary CPU is done with debugging */ |
| 587 | while (atomic_read(&passive_cpu_wait[cpu])) | 590 | while (atomic_read(&passive_cpu_wait[cpu])) |
| 588 | cpu_relax(); | 591 | cpu_relax(); |
