aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Blanchard <anton@samba.org>2005-09-10 02:01:11 -0400
committerPaul Mackerras <paulus@samba.org>2005-09-12 03:19:12 -0400
commitfd9648dff6f9797ecc509bcd181706a274dc074d (patch)
tree845b99905dc4f54171d554c14e45b55f1cfe48e2
parenta94d308513bdb2b926b45c11d7ce7fac6d6ca865 (diff)
[PATCH] ppc64: Add ptrace data breakpoint support
Add hardware data breakpoint support. Signed-off-by: Anton Blanchard <anton@samba.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/ppc64/kernel/process.c34
-rw-r--r--arch/ppc64/kernel/ptrace.c13
-rw-r--r--arch/ppc64/kernel/ptrace32.c13
-rw-r--r--arch/ppc64/kernel/ras.c2
-rw-r--r--arch/ppc64/kernel/signal.c9
-rw-r--r--arch/ppc64/kernel/signal32.c8
-rw-r--r--arch/ppc64/mm/fault.c31
-rw-r--r--arch/ppc64/xmon/privinst.h1
-rw-r--r--arch/ppc64/xmon/xmon.c20
-rw-r--r--include/asm-ppc64/hvcall.h6
-rw-r--r--include/asm-ppc64/plpar_wrappers.h9
-rw-r--r--include/asm-ppc64/processor.h1
-rw-r--r--include/asm-ppc64/ptrace-common.h20
-rw-r--r--include/asm-ppc64/system.h3
14 files changed, 145 insertions, 25 deletions
diff --git a/arch/ppc64/kernel/process.c b/arch/ppc64/kernel/process.c
index 7a7e027653ad..887005358eb1 100644
--- a/arch/ppc64/kernel/process.c
+++ b/arch/ppc64/kernel/process.c
@@ -54,6 +54,7 @@
54#include <asm/sections.h> 54#include <asm/sections.h>
55#include <asm/tlbflush.h> 55#include <asm/tlbflush.h>
56#include <asm/time.h> 56#include <asm/time.h>
57#include <asm/plpar_wrappers.h>
57 58
58#ifndef CONFIG_SMP 59#ifndef CONFIG_SMP
59struct task_struct *last_task_used_math = NULL; 60struct task_struct *last_task_used_math = NULL;
@@ -163,7 +164,30 @@ int dump_task_altivec(struct pt_regs *regs, elf_vrregset_t *vrregs)
163 164
164#endif /* CONFIG_ALTIVEC */ 165#endif /* CONFIG_ALTIVEC */
165 166
167static void set_dabr_spr(unsigned long val)
168{
169 mtspr(SPRN_DABR, val);
170}
171
172int set_dabr(unsigned long dabr)
173{
174 int ret = 0;
175
176 if (firmware_has_feature(FW_FEATURE_XDABR)) {
177 /* We want to catch accesses from kernel and userspace */
178 unsigned long flags = H_DABRX_KERNEL|H_DABRX_USER;
179 ret = plpar_set_xdabr(dabr, flags);
180 } else if (firmware_has_feature(FW_FEATURE_DABR)) {
181 ret = plpar_set_dabr(dabr);
182 } else {
183 set_dabr_spr(dabr);
184 }
185
186 return ret;
187}
188
166DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array); 189DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
190static DEFINE_PER_CPU(unsigned long, current_dabr);
167 191
168struct task_struct *__switch_to(struct task_struct *prev, 192struct task_struct *__switch_to(struct task_struct *prev,
169 struct task_struct *new) 193 struct task_struct *new)
@@ -198,6 +222,11 @@ struct task_struct *__switch_to(struct task_struct *prev,
198 new->thread.regs->msr |= MSR_VEC; 222 new->thread.regs->msr |= MSR_VEC;
199#endif /* CONFIG_ALTIVEC */ 223#endif /* CONFIG_ALTIVEC */
200 224
225 if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) {
226 set_dabr(new->thread.dabr);
227 __get_cpu_var(current_dabr) = new->thread.dabr;
228 }
229
201 flush_tlb_pending(); 230 flush_tlb_pending();
202 231
203 new_thread = &new->thread; 232 new_thread = &new->thread;
@@ -334,6 +363,11 @@ void flush_thread(void)
334 last_task_used_altivec = NULL; 363 last_task_used_altivec = NULL;
335#endif /* CONFIG_ALTIVEC */ 364#endif /* CONFIG_ALTIVEC */
336#endif /* CONFIG_SMP */ 365#endif /* CONFIG_SMP */
366
367 if (current->thread.dabr) {
368 current->thread.dabr = 0;
369 set_dabr(0);
370 }
337} 371}
338 372
339void 373void
diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c
index bf7116d4c4c2..85ed3188a91d 100644
--- a/arch/ppc64/kernel/ptrace.c
+++ b/arch/ppc64/kernel/ptrace.c
@@ -207,6 +207,19 @@ int sys_ptrace(long request, long pid, long addr, long data)
207 break; 207 break;
208 } 208 }
209 209
210 case PTRACE_GET_DEBUGREG: {
211 ret = -EINVAL;
212 /* We only support one DABR and no IABRS at the moment */
213 if (addr > 0)
214 break;
215 ret = put_user(child->thread.dabr,
216 (unsigned long __user *)data);
217 break;
218 }
219
220 case PTRACE_SET_DEBUGREG:
221 ret = ptrace_set_debugreg(child, addr, data);
222
210 case PTRACE_DETACH: 223 case PTRACE_DETACH:
211 ret = ptrace_detach(child, data); 224 ret = ptrace_detach(child, data);
212 break; 225 break;
diff --git a/arch/ppc64/kernel/ptrace32.c b/arch/ppc64/kernel/ptrace32.c
index cbb1e0fb8813..fb8c22d6084a 100644
--- a/arch/ppc64/kernel/ptrace32.c
+++ b/arch/ppc64/kernel/ptrace32.c
@@ -338,6 +338,19 @@ int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data)
338 break; 338 break;
339 } 339 }
340 340
341 case PTRACE_GET_DEBUGREG: {
342 ret = -EINVAL;
343 /* We only support one DABR and no IABRS at the moment */
344 if (addr > 0)
345 break;
346 ret = put_user(child->thread.dabr, (u32 __user *)data);
347 break;
348 }
349
350 case PTRACE_SET_DEBUGREG:
351 ret = ptrace_set_debugreg(child, addr, data);
352 break;
353
341 case PTRACE_DETACH: 354 case PTRACE_DETACH:
342 ret = ptrace_detach(child, data); 355 ret = ptrace_detach(child, data);
343 break; 356 break;
diff --git a/arch/ppc64/kernel/ras.c b/arch/ppc64/kernel/ras.c
index 3c00f7bfc1b5..41b97dc9cc0a 100644
--- a/arch/ppc64/kernel/ras.c
+++ b/arch/ppc64/kernel/ras.c
@@ -59,8 +59,6 @@ char mce_data_buf[RTAS_ERROR_LOG_MAX]
59/* This is true if we are using the firmware NMI handler (typically LPAR) */ 59/* This is true if we are using the firmware NMI handler (typically LPAR) */
60extern int fwnmi_active; 60extern int fwnmi_active;
61 61
62extern void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
63
64static int ras_get_sensor_state_token; 62static int ras_get_sensor_state_token;
65static int ras_check_exception_token; 63static int ras_check_exception_token;
66 64
diff --git a/arch/ppc64/kernel/signal.c b/arch/ppc64/kernel/signal.c
index 49a79a55c32d..347112cca3c0 100644
--- a/arch/ppc64/kernel/signal.c
+++ b/arch/ppc64/kernel/signal.c
@@ -550,6 +550,15 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
550 /* Whee! Actually deliver the signal. */ 550 /* Whee! Actually deliver the signal. */
551 if (TRAP(regs) == 0x0C00) 551 if (TRAP(regs) == 0x0C00)
552 syscall_restart(regs, &ka); 552 syscall_restart(regs, &ka);
553
554 /*
555 * Reenable the DABR before delivering the signal to
556 * user space. The DABR will have been cleared if it
557 * triggered inside the kernel.
558 */
559 if (current->thread.dabr)
560 set_dabr(current->thread.dabr);
561
553 return handle_signal(signr, &ka, &info, oldset, regs); 562 return handle_signal(signr, &ka, &info, oldset, regs);
554 } 563 }
555 564
diff --git a/arch/ppc64/kernel/signal32.c b/arch/ppc64/kernel/signal32.c
index 46f4d6cc7fc9..a8b7a5a56bb4 100644
--- a/arch/ppc64/kernel/signal32.c
+++ b/arch/ppc64/kernel/signal32.c
@@ -970,6 +970,14 @@ int do_signal32(sigset_t *oldset, struct pt_regs *regs)
970 newsp = regs->gpr[1]; 970 newsp = regs->gpr[1];
971 newsp &= ~0xfUL; 971 newsp &= ~0xfUL;
972 972
973 /*
974 * Reenable the DABR before delivering the signal to
975 * user space. The DABR will have been cleared if it
976 * triggered inside the kernel.
977 */
978 if (current->thread.dabr)
979 set_dabr(current->thread.dabr);
980
973 /* Whee! Actually deliver the signal. */ 981 /* Whee! Actually deliver the signal. */
974 if (ka.sa.sa_flags & SA_SIGINFO) 982 if (ka.sa.sa_flags & SA_SIGINFO)
975 ret = handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp); 983 ret = handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp);
diff --git a/arch/ppc64/mm/fault.c b/arch/ppc64/mm/fault.c
index 772f0714a5b7..7fbc68bbb739 100644
--- a/arch/ppc64/mm/fault.c
+++ b/arch/ppc64/mm/fault.c
@@ -77,6 +77,28 @@ static int store_updates_sp(struct pt_regs *regs)
77 return 0; 77 return 0;
78} 78}
79 79
80static void do_dabr(struct pt_regs *regs, unsigned long error_code)
81{
82 siginfo_t info;
83
84 if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
85 11, SIGSEGV) == NOTIFY_STOP)
86 return;
87
88 if (debugger_dabr_match(regs))
89 return;
90
91 /* Clear the DABR */
92 set_dabr(0);
93
94 /* Deliver the signal to userspace */
95 info.si_signo = SIGTRAP;
96 info.si_errno = 0;
97 info.si_code = TRAP_HWBKPT;
98 info.si_addr = (void __user *)regs->nip;
99 force_sig_info(SIGTRAP, &info, current);
100}
101
80/* 102/*
81 * The error_code parameter is 103 * The error_code parameter is
82 * - DSISR for a non-SLB data access fault, 104 * - DSISR for a non-SLB data access fault,
@@ -111,12 +133,9 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
111 if (!user_mode(regs) && (address >= TASK_SIZE)) 133 if (!user_mode(regs) && (address >= TASK_SIZE))
112 return SIGSEGV; 134 return SIGSEGV;
113 135
114 if (error_code & DSISR_DABRMATCH) { 136 if (error_code & DSISR_DABRMATCH) {
115 if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code, 137 do_dabr(regs, error_code);
116 11, SIGSEGV) == NOTIFY_STOP) 138 return 0;
117 return 0;
118 if (debugger_dabr_match(regs))
119 return 0;
120 } 139 }
121 140
122 if (in_atomic() || mm == NULL) { 141 if (in_atomic() || mm == NULL) {
diff --git a/arch/ppc64/xmon/privinst.h b/arch/ppc64/xmon/privinst.h
index 183c3e400258..02eb40dac0b3 100644
--- a/arch/ppc64/xmon/privinst.h
+++ b/arch/ppc64/xmon/privinst.h
@@ -46,7 +46,6 @@ GSETSPR(287, pvr)
46GSETSPR(1008, hid0) 46GSETSPR(1008, hid0)
47GSETSPR(1009, hid1) 47GSETSPR(1009, hid1)
48GSETSPR(1010, iabr) 48GSETSPR(1010, iabr)
49GSETSPR(1013, dabr)
50GSETSPR(1023, pir) 49GSETSPR(1023, pir)
51 50
52static inline void store_inst(void *p) 51static inline void store_inst(void *p)
diff --git a/arch/ppc64/xmon/xmon.c b/arch/ppc64/xmon/xmon.c
index 45908b10acd3..74e63a886a69 100644
--- a/arch/ppc64/xmon/xmon.c
+++ b/arch/ppc64/xmon/xmon.c
@@ -586,6 +586,8 @@ int xmon_dabr_match(struct pt_regs *regs)
586{ 586{
587 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF)) 587 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
588 return 0; 588 return 0;
589 if (dabr.enabled == 0)
590 return 0;
589 xmon_core(regs, 0); 591 xmon_core(regs, 0);
590 return 1; 592 return 1;
591} 593}
@@ -628,20 +630,6 @@ int xmon_fault_handler(struct pt_regs *regs)
628 return 0; 630 return 0;
629} 631}
630 632
631/* On systems with a hypervisor, we can't set the DABR
632 (data address breakpoint register) directly. */
633static void set_controlled_dabr(unsigned long val)
634{
635#ifdef CONFIG_PPC_PSERIES
636 if (systemcfg->platform == PLATFORM_PSERIES_LPAR) {
637 int rc = plpar_hcall_norets(H_SET_DABR, val);
638 if (rc != H_Success)
639 xmon_printf("Warning: setting DABR failed (%d)\n", rc);
640 } else
641#endif
642 set_dabr(val);
643}
644
645static struct bpt *at_breakpoint(unsigned long pc) 633static struct bpt *at_breakpoint(unsigned long pc)
646{ 634{
647 int i; 635 int i;
@@ -728,7 +716,7 @@ static void insert_bpts(void)
728static void insert_cpu_bpts(void) 716static void insert_cpu_bpts(void)
729{ 717{
730 if (dabr.enabled) 718 if (dabr.enabled)
731 set_controlled_dabr(dabr.address | (dabr.enabled & 7)); 719 set_dabr(dabr.address | (dabr.enabled & 7));
732 if (iabr && cpu_has_feature(CPU_FTR_IABR)) 720 if (iabr && cpu_has_feature(CPU_FTR_IABR))
733 set_iabr(iabr->address 721 set_iabr(iabr->address
734 | (iabr->enabled & (BP_IABR|BP_IABR_TE))); 722 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
@@ -756,7 +744,7 @@ static void remove_bpts(void)
756 744
757static void remove_cpu_bpts(void) 745static void remove_cpu_bpts(void)
758{ 746{
759 set_controlled_dabr(0); 747 set_dabr(0);
760 if (cpu_has_feature(CPU_FTR_IABR)) 748 if (cpu_has_feature(CPU_FTR_IABR))
761 set_iabr(0); 749 set_iabr(0);
762} 750}
diff --git a/include/asm-ppc64/hvcall.h b/include/asm-ppc64/hvcall.h
index 4f668a4baff0..ab7c3cf24888 100644
--- a/include/asm-ppc64/hvcall.h
+++ b/include/asm-ppc64/hvcall.h
@@ -56,6 +56,11 @@
56#define H_PP1 (1UL<<(63-62)) 56#define H_PP1 (1UL<<(63-62))
57#define H_PP2 (1UL<<(63-63)) 57#define H_PP2 (1UL<<(63-63))
58 58
59/* DABRX flags */
60#define H_DABRX_HYPERVISOR (1UL<<(63-61))
61#define H_DABRX_KERNEL (1UL<<(63-62))
62#define H_DABRX_USER (1UL<<(63-63))
63
59/* pSeries hypervisor opcodes */ 64/* pSeries hypervisor opcodes */
60#define H_REMOVE 0x04 65#define H_REMOVE 0x04
61#define H_ENTER 0x08 66#define H_ENTER 0x08
@@ -101,6 +106,7 @@
101#define H_VIO_SIGNAL 0x104 106#define H_VIO_SIGNAL 0x104
102#define H_SEND_CRQ 0x108 107#define H_SEND_CRQ 0x108
103#define H_COPY_RDMA 0x110 108#define H_COPY_RDMA 0x110
109#define H_SET_XDABR 0x134
104#define H_STUFF_TCE 0x138 110#define H_STUFF_TCE 0x138
105#define H_PUT_TCE_INDIRECT 0x13C 111#define H_PUT_TCE_INDIRECT 0x13C
106#define H_VTERM_PARTNER_INFO 0x150 112#define H_VTERM_PARTNER_INFO 0x150
diff --git a/include/asm-ppc64/plpar_wrappers.h b/include/asm-ppc64/plpar_wrappers.h
index f4a5fb7d67c7..72dd2449ee76 100644
--- a/include/asm-ppc64/plpar_wrappers.h
+++ b/include/asm-ppc64/plpar_wrappers.h
@@ -107,5 +107,14 @@ static inline long plpar_put_term_char(unsigned long termno,
107 lbuf[1]); 107 lbuf[1]);
108} 108}
109 109
110static inline long plpar_set_xdabr(unsigned long address, unsigned long flags)
111{
112 return plpar_hcall_norets(H_SET_XDABR, address, flags);
113}
114
115static inline long plpar_set_dabr(unsigned long val)
116{
117 return plpar_hcall_norets(H_SET_DABR, val);
118}
110 119
111#endif /* _PPC64_PLPAR_WRAPPERS_H */ 120#endif /* _PPC64_PLPAR_WRAPPERS_H */
diff --git a/include/asm-ppc64/processor.h b/include/asm-ppc64/processor.h
index 8bd7aa959385..4146189006e3 100644
--- a/include/asm-ppc64/processor.h
+++ b/include/asm-ppc64/processor.h
@@ -433,6 +433,7 @@ struct thread_struct {
433 unsigned long start_tb; /* Start purr when proc switched in */ 433 unsigned long start_tb; /* Start purr when proc switched in */
434 unsigned long accum_tb; /* Total accumilated purr for process */ 434 unsigned long accum_tb; /* Total accumilated purr for process */
435 unsigned long vdso_base; /* base of the vDSO library */ 435 unsigned long vdso_base; /* base of the vDSO library */
436 unsigned long dabr; /* Data address breakpoint register */
436#ifdef CONFIG_ALTIVEC 437#ifdef CONFIG_ALTIVEC
437 /* Complete AltiVec register set */ 438 /* Complete AltiVec register set */
438 vector128 vr[32] __attribute((aligned(16))); 439 vector128 vr[32] __attribute((aligned(16)));
diff --git a/include/asm-ppc64/ptrace-common.h b/include/asm-ppc64/ptrace-common.h
index bd0f84c27bd0..b1babb729673 100644
--- a/include/asm-ppc64/ptrace-common.h
+++ b/include/asm-ppc64/ptrace-common.h
@@ -13,6 +13,7 @@
13#define _PPC64_PTRACE_COMMON_H 13#define _PPC64_PTRACE_COMMON_H
14 14
15#include <linux/config.h> 15#include <linux/config.h>
16#include <asm/system.h>
16 17
17/* 18/*
18 * Set of msr bits that gdb can change on behalf of a process. 19 * Set of msr bits that gdb can change on behalf of a process.
@@ -141,4 +142,23 @@ static inline int set_vrregs(struct task_struct *task,
141} 142}
142#endif 143#endif
143 144
145static inline int ptrace_set_debugreg(struct task_struct *task,
146 unsigned long addr, unsigned long data)
147{
148 /* We only support one DABR and no IABRS at the moment */
149 if (addr > 0)
150 return -EINVAL;
151
152 /* The bottom 3 bits are flags */
153 if ((data & ~0x7UL) >= TASK_SIZE)
154 return -EIO;
155
156 /* Ensure translation is on */
157 if (data && !(data & DABR_TRANSLATION))
158 return -EIO;
159
160 task->thread.dabr = data;
161 return 0;
162}
163
144#endif /* _PPC64_PTRACE_COMMON_H */ 164#endif /* _PPC64_PTRACE_COMMON_H */
diff --git a/include/asm-ppc64/system.h b/include/asm-ppc64/system.h
index c0396428cc3c..375015c62f20 100644
--- a/include/asm-ppc64/system.h
+++ b/include/asm-ppc64/system.h
@@ -101,6 +101,9 @@ static inline int debugger_dabr_match(struct pt_regs *regs) { return 0; }
101static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; } 101static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
102#endif 102#endif
103 103
104extern int set_dabr(unsigned long dabr);
105extern void _exception(int signr, struct pt_regs *regs, int code,
106 unsigned long addr);
104extern int fix_alignment(struct pt_regs *regs); 107extern int fix_alignment(struct pt_regs *regs);
105extern void bad_page_fault(struct pt_regs *regs, unsigned long address, 108extern void bad_page_fault(struct pt_regs *regs, unsigned long address,
106 int sig); 109 int sig);