mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 17:12:06 +00:00
[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>
This commit is contained in:
parent
a94d308513
commit
fd9648dff6
@ -54,6 +54,7 @@
|
|||||||
#include <asm/sections.h>
|
#include <asm/sections.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/time.h>
|
#include <asm/time.h>
|
||||||
|
#include <asm/plpar_wrappers.h>
|
||||||
|
|
||||||
#ifndef CONFIG_SMP
|
#ifndef CONFIG_SMP
|
||||||
struct task_struct *last_task_used_math = NULL;
|
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 */
|
#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);
|
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 *__switch_to(struct task_struct *prev,
|
||||||
struct task_struct *new)
|
struct task_struct *new)
|
||||||
@ -198,6 +222,11 @@ struct task_struct *__switch_to(struct task_struct *prev,
|
|||||||
new->thread.regs->msr |= MSR_VEC;
|
new->thread.regs->msr |= MSR_VEC;
|
||||||
#endif /* CONFIG_ALTIVEC */
|
#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();
|
flush_tlb_pending();
|
||||||
|
|
||||||
new_thread = &new->thread;
|
new_thread = &new->thread;
|
||||||
@ -334,6 +363,11 @@ void flush_thread(void)
|
|||||||
last_task_used_altivec = NULL;
|
last_task_used_altivec = NULL;
|
||||||
#endif /* CONFIG_ALTIVEC */
|
#endif /* CONFIG_ALTIVEC */
|
||||||
#endif /* CONFIG_SMP */
|
#endif /* CONFIG_SMP */
|
||||||
|
|
||||||
|
if (current->thread.dabr) {
|
||||||
|
current->thread.dabr = 0;
|
||||||
|
set_dabr(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -207,6 +207,19 @@ int sys_ptrace(long request, long pid, long addr, long data)
|
|||||||
break;
|
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:
|
case PTRACE_DETACH:
|
||||||
ret = ptrace_detach(child, data);
|
ret = ptrace_detach(child, data);
|
||||||
break;
|
break;
|
||||||
|
@ -338,6 +338,19 @@ int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data)
|
|||||||
break;
|
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:
|
case PTRACE_DETACH:
|
||||||
ret = ptrace_detach(child, data);
|
ret = ptrace_detach(child, data);
|
||||||
break;
|
break;
|
||||||
|
@ -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) */
|
/* This is true if we are using the firmware NMI handler (typically LPAR) */
|
||||||
extern int fwnmi_active;
|
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_get_sensor_state_token;
|
||||||
static int ras_check_exception_token;
|
static int ras_check_exception_token;
|
||||||
|
|
||||||
|
@ -550,6 +550,15 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
|
|||||||
/* Whee! Actually deliver the signal. */
|
/* Whee! Actually deliver the signal. */
|
||||||
if (TRAP(regs) == 0x0C00)
|
if (TRAP(regs) == 0x0C00)
|
||||||
syscall_restart(regs, &ka);
|
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);
|
return handle_signal(signr, &ka, &info, oldset, regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,6 +970,14 @@ int do_signal32(sigset_t *oldset, struct pt_regs *regs)
|
|||||||
newsp = regs->gpr[1];
|
newsp = regs->gpr[1];
|
||||||
newsp &= ~0xfUL;
|
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. */
|
/* Whee! Actually deliver the signal. */
|
||||||
if (ka.sa.sa_flags & SA_SIGINFO)
|
if (ka.sa.sa_flags & SA_SIGINFO)
|
||||||
ret = handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp);
|
ret = handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp);
|
||||||
|
@ -77,6 +77,28 @@ static int store_updates_sp(struct pt_regs *regs)
|
|||||||
return 0;
|
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
|
* The error_code parameter is
|
||||||
* - DSISR for a non-SLB data access fault,
|
* - DSISR for a non-SLB data access fault,
|
||||||
@ -112,10 +134,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
|||||||
return SIGSEGV;
|
return SIGSEGV;
|
||||||
|
|
||||||
if (error_code & DSISR_DABRMATCH) {
|
if (error_code & DSISR_DABRMATCH) {
|
||||||
if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
|
do_dabr(regs, error_code);
|
||||||
11, SIGSEGV) == NOTIFY_STOP)
|
|
||||||
return 0;
|
|
||||||
if (debugger_dabr_match(regs))
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ GSETSPR(287, pvr)
|
|||||||
GSETSPR(1008, hid0)
|
GSETSPR(1008, hid0)
|
||||||
GSETSPR(1009, hid1)
|
GSETSPR(1009, hid1)
|
||||||
GSETSPR(1010, iabr)
|
GSETSPR(1010, iabr)
|
||||||
GSETSPR(1013, dabr)
|
|
||||||
GSETSPR(1023, pir)
|
GSETSPR(1023, pir)
|
||||||
|
|
||||||
static inline void store_inst(void *p)
|
static inline void store_inst(void *p)
|
||||||
|
@ -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))
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (dabr.enabled == 0)
|
||||||
|
return 0;
|
||||||
xmon_core(regs, 0);
|
xmon_core(regs, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -628,20 +630,6 @@ int xmon_fault_handler(struct pt_regs *regs)
|
|||||||
return 0;
|
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)
|
static struct bpt *at_breakpoint(unsigned long pc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -728,7 +716,7 @@ static void insert_bpts(void)
|
|||||||
static void insert_cpu_bpts(void)
|
static void insert_cpu_bpts(void)
|
||||||
{
|
{
|
||||||
if (dabr.enabled)
|
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))
|
if (iabr && cpu_has_feature(CPU_FTR_IABR))
|
||||||
set_iabr(iabr->address
|
set_iabr(iabr->address
|
||||||
| (iabr->enabled & (BP_IABR|BP_IABR_TE)));
|
| (iabr->enabled & (BP_IABR|BP_IABR_TE)));
|
||||||
@ -756,7 +744,7 @@ static void remove_bpts(void)
|
|||||||
|
|
||||||
static void remove_cpu_bpts(void)
|
static void remove_cpu_bpts(void)
|
||||||
{
|
{
|
||||||
set_controlled_dabr(0);
|
set_dabr(0);
|
||||||
if (cpu_has_feature(CPU_FTR_IABR))
|
if (cpu_has_feature(CPU_FTR_IABR))
|
||||||
set_iabr(0);
|
set_iabr(0);
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,11 @@
|
|||||||
#define H_PP1 (1UL<<(63-62))
|
#define H_PP1 (1UL<<(63-62))
|
||||||
#define H_PP2 (1UL<<(63-63))
|
#define H_PP2 (1UL<<(63-63))
|
||||||
|
|
||||||
|
/* DABRX flags */
|
||||||
|
#define H_DABRX_HYPERVISOR (1UL<<(63-61))
|
||||||
|
#define H_DABRX_KERNEL (1UL<<(63-62))
|
||||||
|
#define H_DABRX_USER (1UL<<(63-63))
|
||||||
|
|
||||||
/* pSeries hypervisor opcodes */
|
/* pSeries hypervisor opcodes */
|
||||||
#define H_REMOVE 0x04
|
#define H_REMOVE 0x04
|
||||||
#define H_ENTER 0x08
|
#define H_ENTER 0x08
|
||||||
@ -101,6 +106,7 @@
|
|||||||
#define H_VIO_SIGNAL 0x104
|
#define H_VIO_SIGNAL 0x104
|
||||||
#define H_SEND_CRQ 0x108
|
#define H_SEND_CRQ 0x108
|
||||||
#define H_COPY_RDMA 0x110
|
#define H_COPY_RDMA 0x110
|
||||||
|
#define H_SET_XDABR 0x134
|
||||||
#define H_STUFF_TCE 0x138
|
#define H_STUFF_TCE 0x138
|
||||||
#define H_PUT_TCE_INDIRECT 0x13C
|
#define H_PUT_TCE_INDIRECT 0x13C
|
||||||
#define H_VTERM_PARTNER_INFO 0x150
|
#define H_VTERM_PARTNER_INFO 0x150
|
||||||
|
@ -107,5 +107,14 @@ static inline long plpar_put_term_char(unsigned long termno,
|
|||||||
lbuf[1]);
|
lbuf[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline long plpar_set_xdabr(unsigned long address, unsigned long flags)
|
||||||
|
{
|
||||||
|
return plpar_hcall_norets(H_SET_XDABR, address, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long plpar_set_dabr(unsigned long val)
|
||||||
|
{
|
||||||
|
return plpar_hcall_norets(H_SET_DABR, val);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _PPC64_PLPAR_WRAPPERS_H */
|
#endif /* _PPC64_PLPAR_WRAPPERS_H */
|
||||||
|
@ -433,6 +433,7 @@ struct thread_struct {
|
|||||||
unsigned long start_tb; /* Start purr when proc switched in */
|
unsigned long start_tb; /* Start purr when proc switched in */
|
||||||
unsigned long accum_tb; /* Total accumilated purr for process */
|
unsigned long accum_tb; /* Total accumilated purr for process */
|
||||||
unsigned long vdso_base; /* base of the vDSO library */
|
unsigned long vdso_base; /* base of the vDSO library */
|
||||||
|
unsigned long dabr; /* Data address breakpoint register */
|
||||||
#ifdef CONFIG_ALTIVEC
|
#ifdef CONFIG_ALTIVEC
|
||||||
/* Complete AltiVec register set */
|
/* Complete AltiVec register set */
|
||||||
vector128 vr[32] __attribute((aligned(16)));
|
vector128 vr[32] __attribute((aligned(16)));
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#define _PPC64_PTRACE_COMMON_H
|
#define _PPC64_PTRACE_COMMON_H
|
||||||
|
|
||||||
#include <linux/config.h>
|
#include <linux/config.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set of msr bits that gdb can change on behalf of a process.
|
* 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,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline int ptrace_set_debugreg(struct task_struct *task,
|
||||||
|
unsigned long addr, unsigned long data)
|
||||||
|
{
|
||||||
|
/* We only support one DABR and no IABRS at the moment */
|
||||||
|
if (addr > 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* The bottom 3 bits are flags */
|
||||||
|
if ((data & ~0x7UL) >= TASK_SIZE)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
/* Ensure translation is on */
|
||||||
|
if (data && !(data & DABR_TRANSLATION))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
task->thread.dabr = data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _PPC64_PTRACE_COMMON_H */
|
#endif /* _PPC64_PTRACE_COMMON_H */
|
||||||
|
@ -101,6 +101,9 @@ static inline int debugger_dabr_match(struct pt_regs *regs) { return 0; }
|
|||||||
static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
|
static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern int set_dabr(unsigned long dabr);
|
||||||
|
extern void _exception(int signr, struct pt_regs *regs, int code,
|
||||||
|
unsigned long addr);
|
||||||
extern int fix_alignment(struct pt_regs *regs);
|
extern int fix_alignment(struct pt_regs *regs);
|
||||||
extern void bad_page_fault(struct pt_regs *regs, unsigned long address,
|
extern void bad_page_fault(struct pt_regs *regs, unsigned long address,
|
||||||
int sig);
|
int sig);
|
||||||
|
Loading…
Reference in New Issue
Block a user