Commit 3162d92dfb79a0b5fc03380b8819fa5f870ebf1e
Committed by
Benjamin Herrenschmidt
1 parent
172ae2e7f8
Exists in
master
and in
4 other branches
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>
Showing 3 changed files with 301 additions and 0 deletions Side-by-side Diff
Documentation/powerpc/ptrace.txt
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.. |
arch/powerpc/include/asm/ptrace.h
... | ... | @@ -24,6 +24,12 @@ |
24 | 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 | 33 | #ifndef __ASSEMBLY__ |
28 | 34 | |
29 | 35 | struct pt_regs { |
... | ... | @@ -293,6 +299,77 @@ |
293 | 299 | #define PPC_PTRACE_POKEUSR_3264 0x90 |
294 | 300 | |
295 | 301 | #define PTRACE_SINGLEBLOCK 0x100 /* resume execution until next branch */ |
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)) | |
296 | 373 | |
297 | 374 | #endif /* _ASM_POWERPC_PTRACE_H */ |
arch/powerpc/kernel/ptrace.c
... | ... | @@ -835,6 +835,52 @@ |
835 | 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 | 885 | * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, |
840 | 886 | * we mark them as obsolete now, they will be removed in a future version |
... | ... | @@ -925,6 +971,50 @@ |
925 | 971 | [TS_FPRWIDTH * (index - PT_FPR0)] = data; |
926 | 972 | ret = 0; |
927 | 973 | } |
974 | + break; | |
975 | + } | |
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); | |
928 | 1018 | break; |
929 | 1019 | } |
930 | 1020 |