diff options
| author | Dave Kleikamp <shaggy@linux.vnet.ibm.com> | 2010-02-08 06:51:05 -0500 |
|---|---|---|
| committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2010-02-16 22:03:17 -0500 |
| commit | 3162d92dfb79a0b5fc03380b8819fa5f870ebf1e (patch) | |
| tree | 490c54c78630be6052200432d249596bf2f3ae62 | |
| parent | 172ae2e7f8ff9053905a36672453a6d2ff95b182 (diff) | |
powerpc: Extended ptrace interface
powerpc: Extended ptrace interface
From: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
Based on patches originally written by Torez Smith.
Add a new extended ptrace interface so that user-space has a single
interface for powerpc, without having to know the specific layout
of the debug registers.
Implement:
PPC_PTRACE_GETHWDEBUGINFO
PPC_PTRACE_SETHWDEBUG
PPC_PTRACE_DELHWDEBUG
Signed-off-by: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
Acked-by: David Gibson <dwg@au1.ibm.com>
Cc: Torez Smith <lnxtorez@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Sergio Durigan Junior <sergiodj@br.ibm.com>
Cc: Thiago Jung Bauermann <bauerman@br.ibm.com>
Cc: linuxppc-dev list <Linuxppc-dev@ozlabs.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
| -rw-r--r-- | Documentation/powerpc/ptrace.txt | 134 | ||||
| -rw-r--r-- | arch/powerpc/include/asm/ptrace.h | 77 | ||||
| -rw-r--r-- | arch/powerpc/kernel/ptrace.c | 90 |
3 files changed, 301 insertions, 0 deletions
diff --git a/Documentation/powerpc/ptrace.txt b/Documentation/powerpc/ptrace.txt new file mode 100644 index 000000000000..f4a5499b7bc6 --- /dev/null +++ b/Documentation/powerpc/ptrace.txt | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | GDB intends to support the following hardware debug features of BookE | ||
| 2 | processors: | ||
| 3 | |||
| 4 | 4 hardware breakpoints (IAC) | ||
| 5 | 2 hardware watchpoints (read, write and read-write) (DAC) | ||
| 6 | 2 value conditions for the hardware watchpoints (DVC) | ||
| 7 | |||
| 8 | For that, we need to extend ptrace so that GDB can query and set these | ||
| 9 | resources. Since we're extending, we're trying to create an interface | ||
| 10 | that's extendable and that covers both BookE and server processors, so | ||
| 11 | that GDB doesn't need to special-case each of them. We added the | ||
| 12 | following 3 new ptrace requests. | ||
| 13 | |||
| 14 | 1. PTRACE_PPC_GETHWDEBUGINFO | ||
| 15 | |||
| 16 | Query for GDB to discover the hardware debug features. The main info to | ||
| 17 | be returned here is the minimum alignment for the hardware watchpoints. | ||
| 18 | BookE processors don't have restrictions here, but server processors have | ||
| 19 | an 8-byte alignment restriction for hardware watchpoints. We'd like to avoid | ||
| 20 | adding special cases to GDB based on what it sees in AUXV. | ||
| 21 | |||
| 22 | Since we're at it, we added other useful info that the kernel can return to | ||
| 23 | GDB: this query will return the number of hardware breakpoints, hardware | ||
| 24 | watchpoints and whether it supports a range of addresses and a condition. | ||
| 25 | The query will fill the following structure provided by the requesting process: | ||
| 26 | |||
| 27 | struct ppc_debug_info { | ||
| 28 | unit32_t version; | ||
| 29 | unit32_t num_instruction_bps; | ||
| 30 | unit32_t num_data_bps; | ||
| 31 | unit32_t num_condition_regs; | ||
| 32 | unit32_t data_bp_alignment; | ||
| 33 | unit32_t sizeof_condition; /* size of the DVC register */ | ||
| 34 | uint64_t features; /* bitmask of the individual flags */ | ||
| 35 | }; | ||
| 36 | |||
| 37 | features will have bits indicating whether there is support for: | ||
| 38 | |||
| 39 | #define PPC_DEBUG_FEATURE_INSN_BP_RANGE 0x1 | ||
| 40 | #define PPC_DEBUG_FEATURE_INSN_BP_MASK 0x2 | ||
| 41 | #define PPC_DEBUG_FEATURE_DATA_BP_RANGE 0x4 | ||
| 42 | #define PPC_DEBUG_FEATURE_DATA_BP_MASK 0x8 | ||
| 43 | |||
| 44 | 2. PTRACE_SETHWDEBUG | ||
| 45 | |||
| 46 | Sets a hardware breakpoint or watchpoint, according to the provided structure: | ||
| 47 | |||
| 48 | struct ppc_hw_breakpoint { | ||
| 49 | uint32_t version; | ||
| 50 | #define PPC_BREAKPOINT_TRIGGER_EXECUTE 0x1 | ||
| 51 | #define PPC_BREAKPOINT_TRIGGER_READ 0x2 | ||
| 52 | #define PPC_BREAKPOINT_TRIGGER_WRITE 0x4 | ||
| 53 | uint32_t trigger_type; /* only some combinations allowed */ | ||
| 54 | #define PPC_BREAKPOINT_MODE_EXACT 0x0 | ||
| 55 | #define PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE 0x1 | ||
| 56 | #define PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE 0x2 | ||
| 57 | #define PPC_BREAKPOINT_MODE_MASK 0x3 | ||
| 58 | uint32_t addr_mode; /* address match mode */ | ||
| 59 | |||
| 60 | #define PPC_BREAKPOINT_CONDITION_MODE 0x3 | ||
| 61 | #define PPC_BREAKPOINT_CONDITION_NONE 0x0 | ||
| 62 | #define PPC_BREAKPOINT_CONDITION_AND 0x1 | ||
| 63 | #define PPC_BREAKPOINT_CONDITION_EXACT 0x1 /* different name for the same thing as above */ | ||
| 64 | #define PPC_BREAKPOINT_CONDITION_OR 0x2 | ||
| 65 | #define PPC_BREAKPOINT_CONDITION_AND_OR 0x3 | ||
| 66 | #define PPC_BREAKPOINT_CONDITION_BE_ALL 0x00ff0000 /* byte enable bits */ | ||
| 67 | #define PPC_BREAKPOINT_CONDITION_BE(n) (1<<((n)+16)) | ||
| 68 | uint32_t condition_mode; /* break/watchpoint condition flags */ | ||
| 69 | |||
| 70 | uint64_t addr; | ||
| 71 | uint64_t addr2; | ||
| 72 | uint64_t condition_value; | ||
| 73 | }; | ||
| 74 | |||
| 75 | A request specifies one event, not necessarily just one register to be set. | ||
| 76 | For instance, if the request is for a watchpoint with a condition, both the | ||
| 77 | DAC and DVC registers will be set in the same request. | ||
| 78 | |||
| 79 | With this GDB can ask for all kinds of hardware breakpoints and watchpoints | ||
| 80 | that the BookE supports. COMEFROM breakpoints available in server processors | ||
| 81 | are not contemplated, but that is out of the scope of this work. | ||
| 82 | |||
| 83 | ptrace will return an integer (handle) uniquely identifying the breakpoint or | ||
| 84 | watchpoint just created. This integer will be used in the PTRACE_DELHWDEBUG | ||
| 85 | request to ask for its removal. Return -ENOSPC if the requested breakpoint | ||
| 86 | can't be allocated on the registers. | ||
| 87 | |||
| 88 | Some examples of using the structure to: | ||
| 89 | |||
| 90 | - set a breakpoint in the first breakpoint register | ||
| 91 | |||
| 92 | p.version = PPC_DEBUG_CURRENT_VERSION; | ||
| 93 | p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE; | ||
| 94 | p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; | ||
| 95 | p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; | ||
| 96 | p.addr = (uint64_t) address; | ||
| 97 | p.addr2 = 0; | ||
| 98 | p.condition_value = 0; | ||
| 99 | |||
| 100 | - set a watchpoint which triggers on reads in the second watchpoint register | ||
| 101 | |||
| 102 | p.version = PPC_DEBUG_CURRENT_VERSION; | ||
| 103 | p.trigger_type = PPC_BREAKPOINT_TRIGGER_READ; | ||
| 104 | p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; | ||
| 105 | p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; | ||
| 106 | p.addr = (uint64_t) address; | ||
| 107 | p.addr2 = 0; | ||
| 108 | p.condition_value = 0; | ||
| 109 | |||
| 110 | - set a watchpoint which triggers only with a specific value | ||
| 111 | |||
| 112 | p.version = PPC_DEBUG_CURRENT_VERSION; | ||
| 113 | p.trigger_type = PPC_BREAKPOINT_TRIGGER_READ; | ||
| 114 | p.addr_mode = PPC_BREAKPOINT_MODE_EXACT; | ||
| 115 | p.condition_mode = PPC_BREAKPOINT_CONDITION_AND | PPC_BREAKPOINT_CONDITION_BE_ALL; | ||
| 116 | p.addr = (uint64_t) address; | ||
| 117 | p.addr2 = 0; | ||
| 118 | p.condition_value = (uint64_t) condition; | ||
| 119 | |||
| 120 | - set a ranged hardware breakpoint | ||
| 121 | |||
| 122 | p.version = PPC_DEBUG_CURRENT_VERSION; | ||
| 123 | p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE; | ||
| 124 | p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; | ||
| 125 | p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; | ||
| 126 | p.addr = (uint64_t) begin_range; | ||
| 127 | p.addr2 = (uint64_t) end_range; | ||
| 128 | p.condition_value = 0; | ||
| 129 | |||
| 130 | 3. PTRACE_DELHWDEBUG | ||
| 131 | |||
| 132 | Takes an integer which identifies an existing breakpoint or watchpoint | ||
| 133 | (i.e., the value returned from PTRACE_SETHWDEBUG), and deletes the | ||
| 134 | corresponding breakpoint or watchpoint.. | ||
diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index cbd759e3cd78..b45108126562 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h | |||
| @@ -24,6 +24,12 @@ | |||
| 24 | * 2 of the License, or (at your option) any later version. | 24 | * 2 of the License, or (at your option) any later version. |
| 25 | */ | 25 | */ |
| 26 | 26 | ||
| 27 | #ifdef __KERNEL__ | ||
| 28 | #include <linux/types.h> | ||
| 29 | #else | ||
| 30 | #include <stdint.h> | ||
| 31 | #endif | ||
| 32 | |||
| 27 | #ifndef __ASSEMBLY__ | 33 | #ifndef __ASSEMBLY__ |
| 28 | 34 | ||
| 29 | struct pt_regs { | 35 | struct pt_regs { |
| @@ -294,4 +300,75 @@ extern void user_disable_single_step(struct task_struct *); | |||
| 294 | 300 | ||
| 295 | #define PTRACE_SINGLEBLOCK 0x100 /* resume execution until next branch */ | 301 | #define PTRACE_SINGLEBLOCK 0x100 /* resume execution until next branch */ |
| 296 | 302 | ||
| 303 | #define PPC_PTRACE_GETHWDBGINFO 0x89 | ||
| 304 | #define PPC_PTRACE_SETHWDEBUG 0x88 | ||
| 305 | #define PPC_PTRACE_DELHWDEBUG 0x87 | ||
| 306 | |||
| 307 | #ifndef __ASSEMBLY__ | ||
| 308 | |||
| 309 | struct ppc_debug_info { | ||
| 310 | uint32_t version; /* Only version 1 exists to date */ | ||
| 311 | uint32_t num_instruction_bps; | ||
| 312 | uint32_t num_data_bps; | ||
| 313 | uint32_t num_condition_regs; | ||
| 314 | uint32_t data_bp_alignment; | ||
| 315 | uint32_t sizeof_condition; /* size of the DVC register */ | ||
| 316 | uint64_t features; | ||
| 317 | }; | ||
| 318 | |||
| 319 | #endif /* __ASSEMBLY__ */ | ||
| 320 | |||
| 321 | /* | ||
| 322 | * features will have bits indication whether there is support for: | ||
| 323 | */ | ||
| 324 | #define PPC_DEBUG_FEATURE_INSN_BP_RANGE 0x0000000000000001 | ||
| 325 | #define PPC_DEBUG_FEATURE_INSN_BP_MASK 0x0000000000000002 | ||
| 326 | #define PPC_DEBUG_FEATURE_DATA_BP_RANGE 0x0000000000000004 | ||
| 327 | #define PPC_DEBUG_FEATURE_DATA_BP_MASK 0x0000000000000008 | ||
| 328 | |||
| 329 | #ifndef __ASSEMBLY__ | ||
| 330 | |||
| 331 | struct ppc_hw_breakpoint { | ||
| 332 | uint32_t version; /* currently, version must be 1 */ | ||
| 333 | uint32_t trigger_type; /* only some combinations allowed */ | ||
| 334 | uint32_t addr_mode; /* address match mode */ | ||
| 335 | uint32_t condition_mode; /* break/watchpoint condition flags */ | ||
| 336 | uint64_t addr; /* break/watchpoint address */ | ||
| 337 | uint64_t addr2; /* range end or mask */ | ||
| 338 | uint64_t condition_value; /* contents of the DVC register */ | ||
| 339 | }; | ||
| 340 | |||
| 341 | #endif /* __ASSEMBLY__ */ | ||
| 342 | |||
| 343 | /* | ||
| 344 | * Trigger Type | ||
| 345 | */ | ||
| 346 | #define PPC_BREAKPOINT_TRIGGER_EXECUTE 0x00000001 | ||
| 347 | #define PPC_BREAKPOINT_TRIGGER_READ 0x00000002 | ||
| 348 | #define PPC_BREAKPOINT_TRIGGER_WRITE 0x00000004 | ||
| 349 | #define PPC_BREAKPOINT_TRIGGER_RW \ | ||
| 350 | (PPC_BREAKPOINT_TRIGGER_READ | PPC_BREAKPOINT_TRIGGER_WRITE) | ||
| 351 | |||
| 352 | /* | ||
| 353 | * Address Mode | ||
| 354 | */ | ||
| 355 | #define PPC_BREAKPOINT_MODE_EXACT 0x00000000 | ||
| 356 | #define PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE 0x00000001 | ||
| 357 | #define PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE 0x00000002 | ||
| 358 | #define PPC_BREAKPOINT_MODE_MASK 0x00000003 | ||
| 359 | |||
| 360 | /* | ||
| 361 | * Condition Mode | ||
| 362 | */ | ||
| 363 | #define PPC_BREAKPOINT_CONDITION_MODE 0x00000003 | ||
| 364 | #define PPC_BREAKPOINT_CONDITION_NONE 0x00000000 | ||
| 365 | #define PPC_BREAKPOINT_CONDITION_AND 0x00000001 | ||
| 366 | #define PPC_BREAKPOINT_CONDITION_EXACT PPC_BREAKPOINT_CONDITION_AND | ||
| 367 | #define PPC_BREAKPOINT_CONDITION_OR 0x00000002 | ||
| 368 | #define PPC_BREAKPOINT_CONDITION_AND_OR 0x00000003 | ||
| 369 | #define PPC_BREAKPOINT_CONDITION_BE_ALL 0x00ff0000 | ||
| 370 | #define PPC_BREAKPOINT_CONDITION_BE_SHIFT 16 | ||
| 371 | #define PPC_BREAKPOINT_CONDITION_BE(n) \ | ||
| 372 | (1<<((n)+PPC_BREAKPOINT_CONDITION_BE_SHIFT)) | ||
| 373 | |||
| 297 | #endif /* _ASM_POWERPC_PTRACE_H */ | 374 | #endif /* _ASM_POWERPC_PTRACE_H */ |
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 292c81432014..8847bd618cec 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c | |||
| @@ -835,6 +835,52 @@ void ptrace_disable(struct task_struct *child) | |||
| 835 | user_disable_single_step(child); | 835 | user_disable_single_step(child); |
| 836 | } | 836 | } |
| 837 | 837 | ||
| 838 | static long ppc_set_hwdebug(struct task_struct *child, | ||
| 839 | struct ppc_hw_breakpoint *bp_info) | ||
| 840 | { | ||
| 841 | /* | ||
| 842 | * We currently support one data breakpoint | ||
| 843 | */ | ||
| 844 | if (((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0) || | ||
| 845 | ((bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0) || | ||
| 846 | (bp_info->trigger_type != PPC_BREAKPOINT_TRIGGER_WRITE) || | ||
| 847 | (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) || | ||
| 848 | (bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)) | ||
| 849 | return -EINVAL; | ||
| 850 | |||
| 851 | if (child->thread.dabr) | ||
| 852 | return -ENOSPC; | ||
| 853 | |||
| 854 | if ((unsigned long)bp_info->addr >= TASK_SIZE) | ||
| 855 | return -EIO; | ||
| 856 | |||
| 857 | child->thread.dabr = (unsigned long)bp_info->addr; | ||
| 858 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | ||
| 859 | child->thread.dbcr0 = DBCR0_IDM; | ||
| 860 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ) | ||
| 861 | child->thread.dbcr0 |= DBSR_DAC1R; | ||
| 862 | if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) | ||
| 863 | child->thread.dbcr0 |= DBSR_DAC1W; | ||
| 864 | child->thread.regs->msr |= MSR_DE; | ||
| 865 | #endif | ||
| 866 | return 1; | ||
| 867 | } | ||
| 868 | |||
| 869 | static long ppc_del_hwdebug(struct task_struct *child, long addr, long data) | ||
| 870 | { | ||
| 871 | if (data != 1) | ||
| 872 | return -EINVAL; | ||
| 873 | if (child->thread.dabr == 0) | ||
| 874 | return -ENOENT; | ||
| 875 | |||
| 876 | child->thread.dabr = 0; | ||
| 877 | #ifdef CONFIG_PPC_ADV_DEBUG_REGS | ||
| 878 | child->thread.dbcr0 &= ~(DBSR_DAC1R | DBSR_DAC1W | DBCR0_IDM); | ||
| 879 | child->thread.regs->msr &= ~MSR_DE; | ||
| 880 | #endif | ||
| 881 | return 0; | ||
| 882 | } | ||
| 883 | |||
| 838 | /* | 884 | /* |
| 839 | * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, | 885 | * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, |
| 840 | * we mark them as obsolete now, they will be removed in a future version | 886 | * we mark them as obsolete now, they will be removed in a future version |
| @@ -928,6 +974,50 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
| 928 | break; | 974 | break; |
| 929 | } | 975 | } |
| 930 | 976 | ||
| 977 | case PPC_PTRACE_GETHWDBGINFO: { | ||
| 978 | struct ppc_debug_info dbginfo; | ||
| 979 | |||
| 980 | dbginfo.version = 1; | ||
| 981 | dbginfo.num_instruction_bps = 0; | ||
| 982 | dbginfo.num_data_bps = 1; | ||
| 983 | dbginfo.num_condition_regs = 0; | ||
| 984 | #ifdef CONFIG_PPC64 | ||
| 985 | dbginfo.data_bp_alignment = 8; | ||
| 986 | #else | ||
| 987 | dbginfo.data_bp_alignment = 4; | ||
| 988 | #endif | ||
| 989 | dbginfo.sizeof_condition = 0; | ||
| 990 | dbginfo.features = 0; | ||
| 991 | |||
| 992 | if (!access_ok(VERIFY_WRITE, data, | ||
| 993 | sizeof(struct ppc_debug_info))) | ||
| 994 | return -EFAULT; | ||
| 995 | ret = __copy_to_user((struct ppc_debug_info __user *)data, | ||
| 996 | &dbginfo, sizeof(struct ppc_debug_info)) ? | ||
| 997 | -EFAULT : 0; | ||
| 998 | break; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | case PPC_PTRACE_SETHWDEBUG: { | ||
| 1002 | struct ppc_hw_breakpoint bp_info; | ||
| 1003 | |||
| 1004 | if (!access_ok(VERIFY_READ, data, | ||
| 1005 | sizeof(struct ppc_hw_breakpoint))) | ||
| 1006 | return -EFAULT; | ||
| 1007 | ret = __copy_from_user(&bp_info, | ||
| 1008 | (struct ppc_hw_breakpoint __user *)data, | ||
| 1009 | sizeof(struct ppc_hw_breakpoint)) ? | ||
| 1010 | -EFAULT : 0; | ||
| 1011 | if (!ret) | ||
| 1012 | ret = ppc_set_hwdebug(child, &bp_info); | ||
| 1013 | break; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | case PPC_PTRACE_DELHWDEBUG: { | ||
| 1017 | ret = ppc_del_hwdebug(child, addr, data); | ||
| 1018 | break; | ||
| 1019 | } | ||
| 1020 | |||
| 931 | case PTRACE_GET_DEBUGREG: { | 1021 | case PTRACE_GET_DEBUGREG: { |
| 932 | ret = -EINVAL; | 1022 | ret = -EINVAL; |
| 933 | /* We only support one DABR and no IABRS at the moment */ | 1023 | /* We only support one DABR and no IABRS at the moment */ |
