powerpc: Rework set_dabr so it can take a DABRX value as well
authorMichael Neuling <mikey@neuling.org>
Thu, 6 Sep 2012 21:24:56 +0000 (21:24 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 9 Sep 2012 23:59:10 +0000 (09:59 +1000)
Rework set_dabr to take a DABRX value as well.

Both the pseries and PS3 hypervisors do some checks on the DABRX
values that are passed in the hcall.  This patch stops bogus values
from being passed to hypervisor.  Also, in the case where we are
clearing the breakpoint, where DABR and DABRX are zero, we modify the
DABRX value to make it valid so that the hcall won't fail.

Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
14 files changed:
arch/powerpc/include/asm/debug.h
arch/powerpc/include/asm/hw_breakpoint.h
arch/powerpc/include/asm/machdep.h
arch/powerpc/include/asm/processor.h
arch/powerpc/include/asm/reg.h
arch/powerpc/kernel/hw_breakpoint.c
arch/powerpc/kernel/process.c
arch/powerpc/kernel/ptrace.c
arch/powerpc/kernel/signal.c
arch/powerpc/platforms/cell/beat.c
arch/powerpc/platforms/cell/beat.h
arch/powerpc/platforms/ps3/setup.c
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/xmon/xmon.c

index 716d2f089eb61966e18f20af66e893cb0543cb49..32de2577bb6d6630ff77e58254df6d1218f0230d 100644 (file)
@@ -44,7 +44,7 @@ static inline int debugger_dabr_match(struct pt_regs *regs) { return 0; }
 static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
 #endif
 
-extern int set_dabr(unsigned long dabr);
+extern int set_dabr(unsigned long dabr, unsigned long dabrx);
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
 extern void do_send_trap(struct pt_regs *regs, unsigned long address,
                         unsigned long error_code, int signal_code, int brkpt);
index 39b323e80c30d2241794c74f0a23f60d5fec47fa..c6f48eb5299cfca23ce4a3db5fae6cab629226aa 100644 (file)
@@ -61,7 +61,7 @@ extern void ptrace_triggered(struct perf_event *bp,
                        struct perf_sample_data *data, struct pt_regs *regs);
 static inline void hw_breakpoint_disable(void)
 {
-       set_dabr(0);
+       set_dabr(0, 0);
 }
 extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
 
index 42ce570812c1629afb75ed7a5a472d059570305c..236b4779ec4f0dde1651686c4d40e55088661c9f 100644 (file)
@@ -180,7 +180,8 @@ struct machdep_calls {
        void            (*enable_pmcs)(void);
 
        /* Set DABR for this platform, leave empty for default implemenation */
-       int             (*set_dabr)(unsigned long dabr);
+       int             (*set_dabr)(unsigned long dabr,
+                                   unsigned long dabrx);
 
 #ifdef CONFIG_PPC32    /* XXX for now */
        /* A general init function, called by ppc_init in init/main.c.
index 7e7fa13e4667293991e1586cb7b520e936f61da6..83efc6e81543b3785ad422fc87835acba869f422 100644 (file)
@@ -219,6 +219,7 @@ struct thread_struct {
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #endif
        unsigned long   dabr;           /* Data address breakpoint register */
+       unsigned long   dabrx;          /*      ... extension  */
        unsigned long   trap_nr;        /* last trap # on this thread */
 #ifdef CONFIG_ALTIVEC
        /* Complete AltiVec register set */
index ad400a535d23161ff2c0a3bb3b4817d44dba2c3e..121a90bbf7780c4d99d891fa9482c71e39aac51f 100644 (file)
 #define SPRN_DABRX     0x3F7   /* Data Address Breakpoint Register Extension */
 #define   DABRX_USER   (1UL << 0)
 #define   DABRX_KERNEL (1UL << 1)
+#define   DABRX_HYP    (1UL << 2)
+#define   DABRX_BTI    (1UL << 3)
+#define   DABRX_ALL     (DABRX_BTI | DABRX_HYP | DABRX_KERNEL | DABRX_USER)
 #define SPRN_DAR       0x013   /* Data Address Register */
 #define SPRN_DBCR      0x136   /* e300 Data Breakpoint Control Reg */
 #define SPRN_DSISR     0x012   /* Data Storage Interrupt Status Register */
index 6767445ecb45757ba97e6c06f68aad3dc808861c..6891d79ecef6ba0bd88b0c04607ce9ff1aecc51d 100644 (file)
@@ -73,7 +73,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
         * If so, DABR will be populated in single_step_dabr_instruction().
         */
        if (current->thread.last_hit_ubp != bp)
-               set_dabr(info->address | info->type | DABR_TRANSLATION);
+               set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
 
        return 0;
 }
@@ -97,7 +97,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
        }
 
        *slot = NULL;
-       set_dabr(0);
+       set_dabr(0, 0);
 }
 
 /*
@@ -197,7 +197,7 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
 
        info = counter_arch_bp(tsk->thread.last_hit_ubp);
        regs->msr &= ~MSR_SE;
-       set_dabr(info->address | info->type | DABR_TRANSLATION);
+       set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
        tsk->thread.last_hit_ubp = NULL;
 }
 
@@ -215,7 +215,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
        unsigned long dar = regs->dar;
 
        /* Disable breakpoints during exception handling */
-       set_dabr(0);
+       set_dabr(0, 0);
 
        /*
         * The counter may be concurrently released but that can only
@@ -281,7 +281,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
        if (!info->extraneous_interrupt)
                perf_bp_event(bp, regs);
 
-       set_dabr(info->address | info->type | DABR_TRANSLATION);
+       set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
 out:
        rcu_read_unlock();
        return rc;
@@ -313,7 +313,7 @@ int __kprobes single_step_dabr_instruction(struct die_args *args)
        if (!info->extraneous_interrupt)
                perf_bp_event(bp, regs);
 
-       set_dabr(info->address | info->type | DABR_TRANSLATION);
+       set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
        current->thread.last_hit_ubp = NULL;
 
        /*
index 2e743de545d06ec5669c0fbf67fe6d4e47ef63e1..50e504c29bb95294baa35700b74ce7ee44396e60 100644 (file)
@@ -285,7 +285,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address,
                return;
 
        /* Clear the DABR */
-       set_dabr(0);
+       set_dabr(0, 0);
 
        /* Deliver the signal to userspace */
        info.si_signo = SIGTRAP;
@@ -366,18 +366,19 @@ static void set_debug_reg_defaults(struct thread_struct *thread)
 {
        if (thread->dabr) {
                thread->dabr = 0;
-               set_dabr(0);
+               thread->dabrx = 0;
+               set_dabr(0, 0);
        }
 }
 #endif /* !CONFIG_HAVE_HW_BREAKPOINT */
 #endif /* CONFIG_PPC_ADV_DEBUG_REGS */
 
-int set_dabr(unsigned long dabr)
+int set_dabr(unsigned long dabr, unsigned long dabrx)
 {
        __get_cpu_var(current_dabr) = dabr;
 
        if (ppc_md.set_dabr)
-               return ppc_md.set_dabr(dabr);
+               return ppc_md.set_dabr(dabr, dabrx);
 
        /* XXX should we have a CPU_FTR_HAS_DABR ? */
 #ifdef CONFIG_PPC_ADV_DEBUG_REGS
@@ -387,9 +388,8 @@ int set_dabr(unsigned long dabr)
 #endif
 #elif defined(CONFIG_PPC_BOOK3S)
        mtspr(SPRN_DABR, dabr);
+       mtspr(SPRN_DABRX, dabrx);
 #endif
-
-
        return 0;
 }
 
@@ -482,7 +482,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
  */
 #ifndef CONFIG_HAVE_HW_BREAKPOINT
        if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr))
-               set_dabr(new->thread.dabr);
+               set_dabr(new->thread.dabr, new->thread.dabrx);
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #endif
 
index c10fc28b90920120a12c4bc6cc5c39dbf2780e6a..79d8e56470df8105c9119aee7f01f4938101acc8 100644 (file)
@@ -960,6 +960,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
                thread->ptrace_bps[0] = bp;
                ptrace_put_breakpoints(task);
                thread->dabr = data;
+               thread->dabrx = DABRX_ALL;
                return 0;
        }
 
@@ -983,6 +984,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 
        /* Move contents to the DABR register */
        task->thread.dabr = data;
+       task->thread.dabrx = DABRX_ALL;
 #else /* CONFIG_PPC_ADV_DEBUG_REGS */
        /* As described above, it was assumed 3 bits were passed with the data
         *  address, but we will assume only the mode bits will be passed
@@ -1397,6 +1399,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
                dabr |= DABR_DATA_WRITE;
 
        child->thread.dabr = dabr;
+       child->thread.dabrx = DABRX_ALL;
 
        return 1;
 #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
index 29be2712e5609a192656d6303b2f06078591e7fb..a2dc75793bd56b2d09786f9cbf3400b0f6051e51 100644 (file)
@@ -131,7 +131,7 @@ static int do_signal(struct pt_regs *regs)
         * triggered inside the kernel.
         */
        if (current->thread.dabr)
-               set_dabr(current->thread.dabr);
+               set_dabr(current->thread.dabr, current->thread.dabrx);
 #endif
        /* Re-enable the breakpoints for the signal stack */
        thread_change_pc(current, regs);
index 852592b2b7128e0fd72b41dca54f123e827240df..affcf566d460039ba0fd416ffad7f6ca77427ec3 100644 (file)
@@ -136,9 +136,9 @@ ssize_t beat_nvram_get_size(void)
        return BEAT_NVRAM_SIZE;
 }
 
-int beat_set_xdabr(unsigned long dabr)
+int beat_set_xdabr(unsigned long dabr, unsigned long dabrx)
 {
-       if (beat_set_dabr(dabr, DABRX_KERNEL | DABRX_USER))
+       if (beat_set_dabr(dabr, dabrx))
                return -1;
        return 0;
 }
index 32c8efcedc8091ed673592456a2249d73dbd3c83..bfcb8e351ae5e944207e99f920a12839aa40970c 100644 (file)
@@ -32,7 +32,7 @@ void beat_get_rtc_time(struct rtc_time *);
 ssize_t beat_nvram_get_size(void);
 ssize_t beat_nvram_read(char *, size_t, loff_t *);
 ssize_t beat_nvram_write(char *, size_t, loff_t *);
-int beat_set_xdabr(unsigned long);
+int beat_set_xdabr(unsigned long, unsigned long);
 void beat_power_save(void);
 void beat_kexec_cpu_down(int, int);
 
index 2d664c5a83b0230918979585af4eb06adea17ab9..3f509f86432c1b28df128b984793df50789a06f3 100644 (file)
@@ -184,11 +184,15 @@ early_param("ps3flash", early_parse_ps3flash);
 #define prealloc_ps3flash_bounce_buffer()      do { } while (0)
 #endif
 
-static int ps3_set_dabr(unsigned long dabr)
+static int ps3_set_dabr(unsigned long dabr, unsigned long dabrx)
 {
-       enum {DABR_USER = 1, DABR_KERNEL = 2,};
+       /* Have to set at least one bit in the DABRX */
+       if (dabrx == 0 && dabr == 0)
+               dabrx = DABRX_USER;
+       /* hypervisor only allows us to set BTI, Kernel and user */
+       dabrx &= DABRX_BTI | DABRX_KERNEL | DABRX_USER;
 
-       return lv1_set_dabr(dabr, DABR_KERNEL | DABR_USER) ? -1 : 0;
+       return lv1_set_dabr(dabr, dabrx) ? -1 : 0;
 }
 
 static void __init ps3_setup_arch(void)
index 4a2cd48b21f6ad19333cfe51ccca61f1cb7b0a12..a3a69658a6ec4e0beac66e2edae9e594ae319c54 100644 (file)
@@ -414,16 +414,20 @@ static int __init pSeries_init_panel(void)
 }
 machine_arch_initcall(pseries, pSeries_init_panel);
 
-static int pseries_set_dabr(unsigned long dabr)
+static int pseries_set_dabr(unsigned long dabr, unsigned long dabrx)
 {
        return plpar_hcall_norets(H_SET_DABR, dabr);
 }
 
-static int pseries_set_xdabr(unsigned long dabr)
+static int pseries_set_xdabr(unsigned long dabr, unsigned long dabrx)
 {
-       /* We want to catch accesses from kernel and userspace */
-       return plpar_hcall_norets(H_SET_XDABR, dabr,
-                       H_DABRX_KERNEL | H_DABRX_USER);
+       /* Have to set at least one bit in the DABRX according to PAPR */
+       if (dabrx == 0 && dabr == 0)
+               dabrx = DABRX_USER;
+       /* PAPR says we can only set kernel and user bits */
+       dabrx &= H_DABRX_KERNEL | H_DABRX_USER;
+
+       return plpar_hcall_norets(H_SET_XDABR, dabr, dabrx);
 }
 
 #define CMO_CHARACTERISTICS_TOKEN 44
index 9b49c65ee7a42f6f9d0b8dc436628c34bafb262b..987f441525cb7c646e3424e87403e5ee9ec5af1e 100644 (file)
@@ -740,7 +740,7 @@ static void insert_bpts(void)
 static void insert_cpu_bpts(void)
 {
        if (dabr.enabled)
-               set_dabr(dabr.address | (dabr.enabled & 7));
+               set_dabr(dabr.address | (dabr.enabled & 7), DABRX_ALL);
        if (iabr && cpu_has_feature(CPU_FTR_IABR))
                mtspr(SPRN_IABR, iabr->address
                         | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
@@ -768,7 +768,7 @@ static void remove_bpts(void)
 
 static void remove_cpu_bpts(void)
 {
-       set_dabr(0);
+       set_dabr(0, 0);
        if (cpu_has_feature(CPU_FTR_IABR))
                mtspr(SPRN_IABR, 0);
 }