diff options
author | K.Prasad <prasad@linux.vnet.ibm.com> | 2009-06-01 14:15:48 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-06-02 16:46:59 -0400 |
commit | 72f674d203cd230426437cdcf7dd6f681dad8b0d (patch) | |
tree | eae0f2ad35b579d84232f71415ffb3e4f6fb8c05 /arch/x86/kernel/ptrace.c | |
parent | da0cdc14f5f7e0faee6b2393fefed056cdb17146 (diff) |
hw-breakpoints: modify Ptrace routines to access breakpoint registers
This patch modifies the ptrace code to use the new wrapper routines around the
debug/breakpoint registers.
[ Impact: adapt x86 ptrace to the new breakpoint Api ]
Original-patch-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com>
Signed-off-by: Maneesh Soni <maneesh@linux.vnet.ibm.com>
Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 231 |
1 files changed, 141 insertions, 90 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 313be40be55a..b457f78b7dbf 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <asm/prctl.h> | 34 | #include <asm/prctl.h> |
35 | #include <asm/proto.h> | 35 | #include <asm/proto.h> |
36 | #include <asm/ds.h> | 36 | #include <asm/ds.h> |
37 | #include <asm/hw_breakpoint.h> | ||
37 | 38 | ||
38 | #include <trace/syscall.h> | 39 | #include <trace/syscall.h> |
39 | 40 | ||
@@ -136,11 +137,6 @@ static int set_segment_reg(struct task_struct *task, | |||
136 | return 0; | 137 | return 0; |
137 | } | 138 | } |
138 | 139 | ||
139 | static unsigned long debugreg_addr_limit(struct task_struct *task) | ||
140 | { | ||
141 | return TASK_SIZE - 3; | ||
142 | } | ||
143 | |||
144 | #else /* CONFIG_X86_64 */ | 140 | #else /* CONFIG_X86_64 */ |
145 | 141 | ||
146 | #define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT) | 142 | #define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT) |
@@ -265,15 +261,6 @@ static int set_segment_reg(struct task_struct *task, | |||
265 | return 0; | 261 | return 0; |
266 | } | 262 | } |
267 | 263 | ||
268 | static unsigned long debugreg_addr_limit(struct task_struct *task) | ||
269 | { | ||
270 | #ifdef CONFIG_IA32_EMULATION | ||
271 | if (test_tsk_thread_flag(task, TIF_IA32)) | ||
272 | return IA32_PAGE_OFFSET - 3; | ||
273 | #endif | ||
274 | return TASK_SIZE_MAX - 7; | ||
275 | } | ||
276 | |||
277 | #endif /* CONFIG_X86_32 */ | 264 | #endif /* CONFIG_X86_32 */ |
278 | 265 | ||
279 | static unsigned long get_flags(struct task_struct *task) | 266 | static unsigned long get_flags(struct task_struct *task) |
@@ -464,95 +451,159 @@ static int genregs_set(struct task_struct *target, | |||
464 | } | 451 | } |
465 | 452 | ||
466 | /* | 453 | /* |
467 | * This function is trivial and will be inlined by the compiler. | 454 | * Decode the length and type bits for a particular breakpoint as |
468 | * Having it separates the implementation details of debug | 455 | * stored in debug register 7. Return the "enabled" status. |
469 | * registers from the interface details of ptrace. | ||
470 | */ | 456 | */ |
471 | static unsigned long ptrace_get_debugreg(struct task_struct *child, int n) | 457 | static int decode_dr7(unsigned long dr7, int bpnum, unsigned *len, |
458 | unsigned *type) | ||
472 | { | 459 | { |
473 | switch (n) { | 460 | int bp_info = dr7 >> (DR_CONTROL_SHIFT + bpnum * DR_CONTROL_SIZE); |
474 | case 0: return child->thread.debugreg[0]; | 461 | |
475 | case 1: return child->thread.debugreg[1]; | 462 | *len = (bp_info & 0xc) | 0x40; |
476 | case 2: return child->thread.debugreg[2]; | 463 | *type = (bp_info & 0x3) | 0x80; |
477 | case 3: return child->thread.debugreg[3]; | 464 | return (dr7 >> (bpnum * DR_ENABLE_SIZE)) & 0x3; |
478 | case 6: return child->thread.debugreg6; | ||
479 | case 7: return child->thread.debugreg7; | ||
480 | } | ||
481 | return 0; | ||
482 | } | 465 | } |
483 | 466 | ||
484 | static int ptrace_set_debugreg(struct task_struct *child, | 467 | static void ptrace_triggered(struct hw_breakpoint *bp, struct pt_regs *regs) |
485 | int n, unsigned long data) | ||
486 | { | 468 | { |
469 | struct thread_struct *thread = &(current->thread); | ||
487 | int i; | 470 | int i; |
488 | 471 | ||
489 | if (unlikely(n == 4 || n == 5)) | 472 | /* |
490 | return -EIO; | 473 | * Store in the virtual DR6 register the fact that the breakpoint |
474 | * was hit so the thread's debugger will see it. | ||
475 | */ | ||
476 | for (i = 0; i < hbp_kernel_pos; i++) | ||
477 | /* | ||
478 | * We will check bp->info.address against the address stored in | ||
479 | * thread's hbp structure and not debugreg[i]. This is to ensure | ||
480 | * that the corresponding bit for 'i' in DR7 register is enabled | ||
481 | */ | ||
482 | if (bp->info.address == thread->hbp[i]->info.address) | ||
483 | break; | ||
491 | 484 | ||
492 | if (n < 4 && unlikely(data >= debugreg_addr_limit(child))) | 485 | thread->debugreg6 |= (DR_TRAP0 << i); |
493 | return -EIO; | 486 | } |
494 | 487 | ||
495 | switch (n) { | 488 | /* |
496 | case 0: child->thread.debugreg[0] = data; break; | 489 | * Handle ptrace writes to debug register 7. |
497 | case 1: child->thread.debugreg[1] = data; break; | 490 | */ |
498 | case 2: child->thread.debugreg[2] = data; break; | 491 | static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) |
499 | case 3: child->thread.debugreg[3] = data; break; | 492 | { |
493 | struct thread_struct *thread = &(tsk->thread); | ||
494 | unsigned long old_dr7 = thread->debugreg7; | ||
495 | int i, orig_ret = 0, rc = 0; | ||
496 | int enabled, second_pass = 0; | ||
497 | unsigned len, type; | ||
498 | struct hw_breakpoint *bp; | ||
499 | |||
500 | data &= ~DR_CONTROL_RESERVED; | ||
501 | restore: | ||
502 | /* | ||
503 | * Loop through all the hardware breakpoints, making the | ||
504 | * appropriate changes to each. | ||
505 | */ | ||
506 | for (i = 0; i < HBP_NUM; i++) { | ||
507 | enabled = decode_dr7(data, i, &len, &type); | ||
508 | bp = thread->hbp[i]; | ||
509 | |||
510 | if (!enabled) { | ||
511 | if (bp) { | ||
512 | /* Don't unregister the breakpoints right-away, | ||
513 | * unless all register_user_hw_breakpoint() | ||
514 | * requests have succeeded. This prevents | ||
515 | * any window of opportunity for debug | ||
516 | * register grabbing by other users. | ||
517 | */ | ||
518 | if (!second_pass) | ||
519 | continue; | ||
520 | unregister_user_hw_breakpoint(tsk, bp); | ||
521 | kfree(bp); | ||
522 | } | ||
523 | continue; | ||
524 | } | ||
525 | if (!bp) { | ||
526 | rc = -ENOMEM; | ||
527 | bp = kzalloc(sizeof(struct hw_breakpoint), GFP_KERNEL); | ||
528 | if (bp) { | ||
529 | bp->info.address = thread->debugreg[i]; | ||
530 | bp->triggered = ptrace_triggered; | ||
531 | bp->info.len = len; | ||
532 | bp->info.type = type; | ||
533 | rc = register_user_hw_breakpoint(tsk, bp); | ||
534 | if (rc) | ||
535 | kfree(bp); | ||
536 | } | ||
537 | } else | ||
538 | rc = modify_user_hw_breakpoint(tsk, bp); | ||
539 | if (rc) | ||
540 | break; | ||
541 | } | ||
542 | /* | ||
543 | * Make a second pass to free the remaining unused breakpoints | ||
544 | * or to restore the original breakpoints if an error occurred. | ||
545 | */ | ||
546 | if (!second_pass) { | ||
547 | second_pass = 1; | ||
548 | if (rc < 0) { | ||
549 | orig_ret = rc; | ||
550 | data = old_dr7; | ||
551 | } | ||
552 | goto restore; | ||
553 | } | ||
554 | return ((orig_ret < 0) ? orig_ret : rc); | ||
555 | } | ||
500 | 556 | ||
501 | case 6: | 557 | /* |
502 | if ((data & ~0xffffffffUL) != 0) | 558 | * Handle PTRACE_PEEKUSR calls for the debug register area. |
503 | return -EIO; | 559 | */ |
504 | child->thread.debugreg6 = data; | 560 | unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n) |
505 | break; | 561 | { |
562 | struct thread_struct *thread = &(tsk->thread); | ||
563 | unsigned long val = 0; | ||
564 | |||
565 | if (n < HBP_NUM) | ||
566 | val = thread->debugreg[n]; | ||
567 | else if (n == 6) | ||
568 | val = thread->debugreg6; | ||
569 | else if (n == 7) | ||
570 | val = thread->debugreg7; | ||
571 | return val; | ||
572 | } | ||
506 | 573 | ||
507 | case 7: | 574 | /* |
508 | /* | 575 | * Handle PTRACE_POKEUSR calls for the debug register area. |
509 | * Sanity-check data. Take one half-byte at once with | 576 | */ |
510 | * check = (val >> (16 + 4*i)) & 0xf. It contains the | 577 | int ptrace_set_debugreg(struct task_struct *tsk, int n, unsigned long val) |
511 | * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits | 578 | { |
512 | * 2 and 3 are LENi. Given a list of invalid values, | 579 | struct thread_struct *thread = &(tsk->thread); |
513 | * we do mask |= 1 << invalid_value, so that | 580 | int rc = 0; |
514 | * (mask >> check) & 1 is a correct test for invalid | 581 | |
515 | * values. | 582 | /* There are no DR4 or DR5 registers */ |
516 | * | 583 | if (n == 4 || n == 5) |
517 | * R/Wi contains the type of the breakpoint / | 584 | return -EIO; |
518 | * watchpoint, LENi contains the length of the watched | 585 | |
519 | * data in the watchpoint case. | 586 | if (n == 6) { |
520 | * | 587 | tsk->thread.debugreg6 = val; |
521 | * The invalid values are: | 588 | goto ret_path; |
522 | * - LENi == 0x10 (undefined), so mask |= 0x0f00. [32-bit] | ||
523 | * - R/Wi == 0x10 (break on I/O reads or writes), so | ||
524 | * mask |= 0x4444. | ||
525 | * - R/Wi == 0x00 && LENi != 0x00, so we have mask |= | ||
526 | * 0x1110. | ||
527 | * | ||
528 | * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54. | ||
529 | * | ||
530 | * See the Intel Manual "System Programming Guide", | ||
531 | * 15.2.4 | ||
532 | * | ||
533 | * Note that LENi == 0x10 is defined on x86_64 in long | ||
534 | * mode (i.e. even for 32-bit userspace software, but | ||
535 | * 64-bit kernel), so the x86_64 mask value is 0x5454. | ||
536 | * See the AMD manual no. 24593 (AMD64 System Programming) | ||
537 | */ | ||
538 | #ifdef CONFIG_X86_32 | ||
539 | #define DR7_MASK 0x5f54 | ||
540 | #else | ||
541 | #define DR7_MASK 0x5554 | ||
542 | #endif | ||
543 | data &= ~DR_CONTROL_RESERVED; | ||
544 | for (i = 0; i < 4; i++) | ||
545 | if ((DR7_MASK >> ((data >> (16 + 4*i)) & 0xf)) & 1) | ||
546 | return -EIO; | ||
547 | child->thread.debugreg7 = data; | ||
548 | if (data) | ||
549 | set_tsk_thread_flag(child, TIF_DEBUG); | ||
550 | else | ||
551 | clear_tsk_thread_flag(child, TIF_DEBUG); | ||
552 | break; | ||
553 | } | 589 | } |
590 | if (n < HBP_NUM) { | ||
591 | if (thread->hbp[n]) { | ||
592 | if (arch_check_va_in_userspace(val, | ||
593 | thread->hbp[n]->info.len) == 0) { | ||
594 | rc = -EIO; | ||
595 | goto ret_path; | ||
596 | } | ||
597 | thread->hbp[n]->info.address = val; | ||
598 | } | ||
599 | thread->debugreg[n] = val; | ||
600 | } | ||
601 | /* All that's left is DR7 */ | ||
602 | if (n == 7) | ||
603 | rc = ptrace_write_dr7(tsk, val); | ||
554 | 604 | ||
555 | return 0; | 605 | ret_path: |
606 | return rc; | ||
556 | } | 607 | } |
557 | 608 | ||
558 | /* | 609 | /* |