aboutsummaryrefslogtreecommitdiff
path: root/arch/ppc64
diff options
context:
space:
mode:
authorAnton Blanchard <anton@samba.org>2005-09-10 16:01:11 +1000
committerPaul Mackerras <paulus@samba.org>2005-09-12 17:19:12 +1000
commitfd9648dff6f9797ecc509bcd181706a274dc074d (patch)
tree845b99905dc4f54171d554c14e45b55f1cfe48e2 /arch/ppc64
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>
Diffstat (limited to 'arch/ppc64')
-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
9 files changed, 106 insertions, 25 deletions
diff --git a/arch/ppc64/kernel/process.c b/arch/ppc64/kernel/process.c
index 7a7e027653a..887005358eb 100644
--- a/arch/ppc64/kernel/process.c
+++ b/arch/ppc64/kernel/process.c
@@ -54,6 +54,7 @@
#include <asm/sections.h>
#include <asm/tlbflush.h>
#include <asm/time.h>
+#include <asm/plpar_wrappers.h>
#ifndef CONFIG_SMP
struct task_struct *last_task_used_math = NULL;
@@ -163,7 +164,30 @@ int dump_task_altivec(struct pt_regs *regs, elf_vrregset_t *vrregs)
#endif /* CONFIG_ALTIVEC */
+static void set_dabr_spr(unsigned long val)
+{
+ mtspr(SPRN_DABR, val);
+}
+
+int set_dabr(unsigned long dabr)
+{
+ int ret = 0;
+
+ if (firmware_has_feature(FW_FEATURE_XDABR)) {
+ /* We want to catch accesses from kernel and userspace */
+ unsigned long flags = H_DABRX_KERNEL|H_DABRX_USER;
+ ret = plpar_set_xdabr(dabr, flags);
+ } else if (firmware_has_feature(FW_FEATURE_DABR)) {
+ ret = plpar_set_dabr(dabr);
+ } else {
+ set_dabr_spr(dabr);
+ }
+
+ return ret;
+}
+
DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
+static DEFINE_PER_CPU(unsigned long, current_dabr);
struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *new)
@@ -198,6 +222,11 @@ struct task_struct *__switch_to(struct task_struct *prev,
new->thread.regs->msr |= MSR_VEC;
#endif /* CONFIG_ALTIVEC */
+ if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) {
+ set_dabr(new->thread.dabr);
+ __get_cpu_var(current_dabr) = new->thread.dabr;
+ }
+
flush_tlb_pending();
new_thread = &new->thread;
@@ -334,6 +363,11 @@ void flush_thread(void)
last_task_used_altivec = NULL;
#endif /* CONFIG_ALTIVEC */
#endif /* CONFIG_SMP */
+
+ if (current->thread.dabr) {
+ current->thread.dabr = 0;
+ set_dabr(0);
+ }
}
void
diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c
index bf7116d4c4c..85ed3188a91 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)
break;
}
+ case PTRACE_GET_DEBUGREG: {
+ ret = -EINVAL;
+ /* We only support one DABR and no IABRS at the moment */
+ if (addr > 0)
+ break;
+ ret = put_user(child->thread.dabr,
+ (unsigned long __user *)data);
+ break;
+ }
+
+ case PTRACE_SET_DEBUGREG:
+ ret = ptrace_set_debugreg(child, addr, data);
+
case PTRACE_DETACH:
ret = ptrace_detach(child, data);
break;
diff --git a/arch/ppc64/kernel/ptrace32.c b/arch/ppc64/kernel/ptrace32.c
index cbb1e0fb881..fb8c22d6084 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)
break;
}
+ case PTRACE_GET_DEBUGREG: {
+ ret = -EINVAL;
+ /* We only support one DABR and no IABRS at the moment */
+ if (addr > 0)
+ break;
+ ret = put_user(child->thread.dabr, (u32 __user *)data);
+ break;
+ }
+
+ case PTRACE_SET_DEBUGREG:
+ ret = ptrace_set_debugreg(child, addr, data);
+ break;
+
case PTRACE_DETACH:
ret = ptrace_detach(child, data);
break;
diff --git a/arch/ppc64/kernel/ras.c b/arch/ppc64/kernel/ras.c
index 3c00f7bfc1b..41b97dc9cc0 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]
/* This is true if we are using the firmware NMI handler (typically LPAR) */
extern int fwnmi_active;
-extern void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
-
static int ras_get_sensor_state_token;
static int ras_check_exception_token;
diff --git a/arch/ppc64/kernel/signal.c b/arch/ppc64/kernel/signal.c
index 49a79a55c32..347112cca3c 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)
/* Whee! Actually deliver the signal. */
if (TRAP(regs) == 0x0C00)
syscall_restart(regs, &ka);
+
+ /*
+ * Reenable the DABR before delivering the signal to
+ * user space. The DABR will have been cleared if it
+ * triggered inside the kernel.
+ */
+ if (current->thread.dabr)
+ set_dabr(current->thread.dabr);
+
return handle_signal(signr, &ka, &info, oldset, regs);
}
diff --git a/arch/ppc64/kernel/signal32.c b/arch/ppc64/kernel/signal32.c
index 46f4d6cc7fc..a8b7a5a56bb 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)
newsp = regs->gpr[1];
newsp &= ~0xfUL;
+ /*
+ * Reenable the DABR before delivering the signal to
+ * user space. The DABR will have been cleared if it
+ * triggered inside the kernel.
+ */
+ if (current->thread.dabr)
+ set_dabr(current->thread.dabr);
+
/* Whee! Actually deliver the signal. */
if (ka.sa.sa_flags & SA_SIGINFO)
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 772f0714a5b..7fbc68bbb73 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)
return 0;
}
+static void do_dabr(struct pt_regs *regs, unsigned long error_code)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
+ 11, SIGSEGV) == NOTIFY_STOP)
+ return;
+
+ if (debugger_dabr_match(regs))
+ return;
+
+ /* Clear the DABR */
+ set_dabr(0);
+
+ /* Deliver the signal to userspace */
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_HWBKPT;
+ info.si_addr = (void __user *)regs->nip;
+ force_sig_info(SIGTRAP, &info, current);
+}
+
/*
* The error_code parameter is
* - DSISR for a non-SLB data access fault,
@@ -111,12 +133,9 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
if (!user_mode(regs) && (address >= TASK_SIZE))
return SIGSEGV;
- if (error_code & DSISR_DABRMATCH) {
- if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
- 11, SIGSEGV) == NOTIFY_STOP)
- return 0;
- if (debugger_dabr_match(regs))
- return 0;
+ if (error_code & DSISR_DABRMATCH) {
+ do_dabr(regs, error_code);
+ return 0;
}
if (in_atomic() || mm == NULL) {
diff --git a/arch/ppc64/xmon/privinst.h b/arch/ppc64/xmon/privinst.h
index 183c3e40025..02eb40dac0b 100644
--- a/arch/ppc64/xmon/privinst.h
+++ b/arch/ppc64/xmon/privinst.h
@@ -46,7 +46,6 @@ GSETSPR(287, pvr)
GSETSPR(1008, hid0)
GSETSPR(1009, hid1)
GSETSPR(1010, iabr)
-GSETSPR(1013, dabr)
GSETSPR(1023, pir)
static inline void store_inst(void *p)
diff --git a/arch/ppc64/xmon/xmon.c b/arch/ppc64/xmon/xmon.c
index 45908b10acd..74e63a886a6 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)
{
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
return 0;
+ if (dabr.enabled == 0)
+ return 0;
xmon_core(regs, 0);
return 1;
}
@@ -628,20 +630,6 @@ int xmon_fault_handler(struct pt_regs *regs)
return 0;
}
-/* On systems with a hypervisor, we can't set the DABR
- (data address breakpoint register) directly. */
-static void set_controlled_dabr(unsigned long val)
-{
-#ifdef CONFIG_PPC_PSERIES
- if (systemcfg->platform == PLATFORM_PSERIES_LPAR) {
- int rc = plpar_hcall_norets(H_SET_DABR, val);
- if (rc != H_Success)
- xmon_printf("Warning: setting DABR failed (%d)\n", rc);
- } else
-#endif
- set_dabr(val);
-}
-
static struct bpt *at_breakpoint(unsigned long pc)
{
int i;
@@ -728,7 +716,7 @@ static void insert_bpts(void)
static void insert_cpu_bpts(void)
{
if (dabr.enabled)
- set_controlled_dabr(dabr.address | (dabr.enabled & 7));
+ set_dabr(dabr.address | (dabr.enabled & 7));
if (iabr && cpu_has_feature(CPU_FTR_IABR))
set_iabr(iabr->address
| (iabr->enabled & (BP_IABR|BP_IABR_TE)));
@@ -756,7 +744,7 @@ static void remove_bpts(void)
static void remove_cpu_bpts(void)
{
- set_controlled_dabr(0);
+ set_dabr(0);
if (cpu_has_feature(CPU_FTR_IABR))
set_iabr(0);
}