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 /arch/powerpc | |
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>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/ptrace.h | 77 | ||||
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 90 |
2 files changed, 167 insertions, 0 deletions
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 */ |