powerpc: Add force enable of DAWR on P9 option
authorMichael Neuling <mikey@neuling.org>
Mon, 1 Apr 2019 06:03:12 +0000 (17:03 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Sat, 20 Apr 2019 12:20:45 +0000 (22:20 +1000)
This adds a flag so that the DAWR can be enabled on P9 via:
  echo Y > /sys/kernel/debug/powerpc/dawr_enable_dangerous

The DAWR was previously force disabled on POWER9 in:
  9654153158 powerpc: Disable DAWR in the base POWER9 CPU features
Also see Documentation/powerpc/DAWR-POWER9.txt

This is a dangerous setting, USE AT YOUR OWN RISK.

Some users may not care about a bad user crashing their box
(ie. single user/desktop systems) and really want the DAWR.  This
allows them to force enable DAWR.

This flag can also be used to disable DAWR access. Once this is
cleared, all DAWR access should be cleared immediately and your
machine once again safe from crashing.

Userspace may get confused by toggling this. If DAWR is force
enabled/disabled between getting the number of breakpoints (via
PTRACE_GETHWDBGINFO) and setting the breakpoint, userspace will get an
inconsistent view of what's available. Similarly for guests.

For the DAWR to be enabled in a KVM guest, the DAWR needs to be force
enabled in the host AND the guest. For this reason, this won't work on
POWERVM as it doesn't allow the HCALL to work. Writes of 'Y' to the
dawr_enable_dangerous file will fail if the hypervisor doesn't support
writing the DAWR.

To double check the DAWR is working, run this kernel selftest:
  tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c
Any errors/failures/skips mean something is wrong.

Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Documentation/powerpc/DAWR-POWER9.txt
arch/powerpc/include/asm/hw_breakpoint.h
arch/powerpc/kernel/hw_breakpoint.c
arch/powerpc/kernel/process.c
arch/powerpc/kernel/ptrace.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S

index 2feaa66196585bdb10043f048800ad9fba6a31c7..bdec03650941e40363d4ea46aab2ee2f76d6821a 100644 (file)
@@ -56,3 +56,35 @@ POWER9. Loads and stores to the watchpoint locations will not be
 trapped in GDB. The watchpoint is remembered, so if the guest is
 migrated back to the POWER8 host, it will start working again.
 
+Force enabling the DAWR
+=============================
+Kernels (since ~v5.2) have an option to force enable the DAWR via:
+
+  echo Y > /sys/kernel/debug/powerpc/dawr_enable_dangerous
+
+This enables the DAWR even on POWER9.
+
+This is a dangerous setting, USE AT YOUR OWN RISK.
+
+Some users may not care about a bad user crashing their box
+(ie. single user/desktop systems) and really want the DAWR.  This
+allows them to force enable DAWR.
+
+This flag can also be used to disable DAWR access. Once this is
+cleared, all DAWR access should be cleared immediately and your
+machine once again safe from crashing.
+
+Userspace may get confused by toggling this. If DAWR is force
+enabled/disabled between getting the number of breakpoints (via
+PTRACE_GETHWDBGINFO) and setting the breakpoint, userspace will get an
+inconsistent view of what's available. Similarly for guests.
+
+For the DAWR to be enabled in a KVM guest, the DAWR needs to be force
+enabled in the host AND the guest. For this reason, this won't work on
+POWERVM as it doesn't allow the HCALL to work. Writes of 'Y' to the
+dawr_enable_dangerous file will fail if the hypervisor doesn't support
+writing the DAWR.
+
+To double check the DAWR is working, run this kernel selftest:
+  tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c
+Any errors/failures/skips mean something is wrong.
index ece4dc89c90becb2accebc74f2747fe4d0580121..0fe8c1e46bbcb2d87223101f13794de64618e292 100644 (file)
@@ -90,10 +90,18 @@ static inline void hw_breakpoint_disable(void)
 extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
 int hw_breakpoint_handler(struct die_args *args);
 
+extern int set_dawr(struct arch_hw_breakpoint *brk);
+extern bool dawr_force_enable;
+static inline bool dawr_enabled(void)
+{
+       return dawr_force_enable;
+}
+
 #else  /* CONFIG_HAVE_HW_BREAKPOINT */
 static inline void hw_breakpoint_disable(void) { }
 static inline void thread_change_pc(struct task_struct *tsk,
                                        struct pt_regs *regs) { }
+static inline bool dawr_enabled(void) { return false; }
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #endif /* __KERNEL__ */
 #endif /* _PPC_BOOK3S_64_HW_BREAKPOINT_H */
index fec8a6773119ff809524ba0119ef079189f7442a..da307dd93ee38f522ee01121a1cfd919db85fc45 100644 (file)
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
+#include <linux/debugfs.h>
+#include <linux/init.h>
 
 #include <asm/hw_breakpoint.h>
 #include <asm/processor.h>
 #include <asm/sstep.h>
 #include <asm/debug.h>
+#include <asm/debugfs.h>
+#include <asm/hvcall.h>
 #include <linux/uaccess.h>
 
 /*
@@ -174,7 +178,7 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,
        if (!ppc_breakpoint_available())
                return -ENODEV;
        length_max = 8; /* DABR */
-       if (cpu_has_feature(CPU_FTR_DAWR)) {
+       if (dawr_enabled()) {
                length_max = 512 ; /* 64 doublewords */
                /* DAWR region can't cross 512 boundary */
                if ((attr->bp_addr >> 9) !=
@@ -376,3 +380,59 @@ void hw_breakpoint_pmu_read(struct perf_event *bp)
 {
        /* TODO */
 }
+
+bool dawr_force_enable;
+EXPORT_SYMBOL_GPL(dawr_force_enable);
+
+static ssize_t dawr_write_file_bool(struct file *file,
+                                   const char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct arch_hw_breakpoint null_brk = {0, 0, 0};
+       size_t rc;
+
+       /* Send error to user if they hypervisor won't allow us to write DAWR */
+       if ((!dawr_force_enable) &&
+           (firmware_has_feature(FW_FEATURE_LPAR)) &&
+           (set_dawr(&null_brk) != H_SUCCESS))
+               return -1;
+
+       rc = debugfs_write_file_bool(file, user_buf, count, ppos);
+       if (rc)
+               return rc;
+
+       /* If we are clearing, make sure all CPUs have the DAWR cleared */
+       if (!dawr_force_enable)
+               smp_call_function((smp_call_func_t)set_dawr, &null_brk, 0);
+
+       return rc;
+}
+
+static const struct file_operations dawr_enable_fops = {
+       .read =         debugfs_read_file_bool,
+       .write =        dawr_write_file_bool,
+       .open =         simple_open,
+       .llseek =       default_llseek,
+};
+
+static int __init dawr_force_setup(void)
+{
+       dawr_force_enable = false;
+
+       if (cpu_has_feature(CPU_FTR_DAWR)) {
+               /* Don't setup sysfs file for user control on P8 */
+               dawr_force_enable = true;
+               return 0;
+       }
+
+       if (PVR_VER(mfspr(SPRN_PVR)) == PVR_POWER9) {
+               /* Turn DAWR off by default, but allow admin to turn it on */
+               dawr_force_enable = false;
+               debugfs_create_file_unsafe("dawr_enable_dangerous", 0600,
+                                          powerpc_debugfs_root,
+                                          &dawr_force_enable,
+                                          &dawr_enable_fops);
+       }
+       return 0;
+}
+arch_initcall(dawr_force_setup);
index dd9e0d5386ee7030fe4539ef67e0b3a4d6e3eb46..225705aac814ba84ff5f65d8d52d910e00b473d4 100644 (file)
@@ -67,6 +67,7 @@
 #include <asm/cpu_has_feature.h>
 #include <asm/asm-prototypes.h>
 #include <asm/stacktrace.h>
+#include <asm/hw_breakpoint.h>
 
 #include <linux/kprobes.h>
 #include <linux/kdebug.h>
@@ -784,7 +785,7 @@ static inline int set_dabr(struct arch_hw_breakpoint *brk)
        return __set_dabr(dabr, dabrx);
 }
 
-static inline int set_dawr(struct arch_hw_breakpoint *brk)
+int set_dawr(struct arch_hw_breakpoint *brk)
 {
        unsigned long dawr, dawrx, mrd;
 
@@ -816,7 +817,7 @@ void __set_breakpoint(struct arch_hw_breakpoint *brk)
 {
        memcpy(this_cpu_ptr(&current_brk), brk, sizeof(*brk));
 
-       if (cpu_has_feature(CPU_FTR_DAWR))
+       if (dawr_enabled())
                // Power8 or later
                set_dawr(brk);
        else if (!cpu_has_feature(CPU_FTR_ARCH_207S))
@@ -830,8 +831,8 @@ void __set_breakpoint(struct arch_hw_breakpoint *brk)
 /* Check if we have DAWR or DABR hardware */
 bool ppc_breakpoint_available(void)
 {
-       if (cpu_has_feature(CPU_FTR_DAWR))
-               return true; /* POWER8 DAWR */
+       if (dawr_enabled())
+               return true; /* POWER8 DAWR or POWER9 forced DAWR */
        if (cpu_has_feature(CPU_FTR_ARCH_207S))
                return false; /* POWER9 with DAWR disabled */
        /* DABR: Everything but POWER8 and POWER9 */
index d9ac7d94656ee1a6c39dc3b4be7edbb5e5594b2c..684b0b315c327a44c2eb34493c43e3c3494e437c 100644 (file)
@@ -43,6 +43,7 @@
 #include <asm/tm.h>
 #include <asm/asm-prototypes.h>
 #include <asm/debug.h>
+#include <asm/hw_breakpoint.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/syscalls.h>
@@ -3088,7 +3089,7 @@ long arch_ptrace(struct task_struct *child, long request,
                dbginfo.sizeof_condition = 0;
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
                dbginfo.features = PPC_DEBUG_FEATURE_DATA_BP_RANGE;
-               if (cpu_has_feature(CPU_FTR_DAWR))
+               if (dawr_enabled())
                        dbginfo.features |= PPC_DEBUG_FEATURE_DATA_BP_DAWR;
 #else
                dbginfo.features = 0;
index 06964350b97a94118d065d90a257c882b5280136..0fab0a20102722b5386071cdea06b659e645951e 100644 (file)
@@ -74,6 +74,7 @@
 #include <asm/opal.h>
 #include <asm/xics.h>
 #include <asm/xive.h>
+#include <asm/hw_breakpoint.h>
 
 #include "book3s.h"
 
@@ -3374,7 +3375,7 @@ static int kvmhv_load_hv_regs_and_go(struct kvm_vcpu *vcpu, u64 time_limit,
        mtspr(SPRN_PURR, vcpu->arch.purr);
        mtspr(SPRN_SPURR, vcpu->arch.spurr);
 
-       if (cpu_has_feature(CPU_FTR_DAWR)) {
+       if (dawr_enabled()) {
                mtspr(SPRN_DAWR, vcpu->arch.dawr);
                mtspr(SPRN_DAWRX, vcpu->arch.dawrx);
        }
index 3a5e719ef032bcdc7097f840b1932c47408bafc3..139027c62dc2fec7a52a1a492382c13bed6cd50a 100644 (file)
@@ -822,18 +822,21 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
        mtspr   SPRN_IAMR, r5
        mtspr   SPRN_PSPB, r6
        mtspr   SPRN_FSCR, r7
-       ld      r5, VCPU_DAWR(r4)
-       ld      r6, VCPU_DAWRX(r4)
-       ld      r7, VCPU_CIABR(r4)
-       ld      r8, VCPU_TAR(r4)
        /*
         * Handle broken DAWR case by not writing it. This means we
         * can still store the DAWR register for migration.
         */
-BEGIN_FTR_SECTION
+       LOAD_REG_ADDR(r5, dawr_force_enable)
+       lbz     r5, 0(r5)
+       cmpdi   r5, 0
+       beq     1f
+       ld      r5, VCPU_DAWR(r4)
+       ld      r6, VCPU_DAWRX(r4)
        mtspr   SPRN_DAWR, r5
        mtspr   SPRN_DAWRX, r6
-END_FTR_SECTION_IFSET(CPU_FTR_DAWR)
+1:
+       ld      r7, VCPU_CIABR(r4)
+       ld      r8, VCPU_TAR(r4)
        mtspr   SPRN_CIABR, r7
        mtspr   SPRN_TAR, r8
        ld      r5, VCPU_IC(r4)
@@ -2513,11 +2516,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
        blr
 
 2:
-BEGIN_FTR_SECTION
-       /* POWER9 with disabled DAWR */
+       LOAD_REG_ADDR(r11, dawr_force_enable)
+       lbz     r11, 0(r11)
+       cmpdi   r11, 0
        li      r3, H_HARDWARE
-       blr
-END_FTR_SECTION_IFCLR(CPU_FTR_DAWR)
+       beqlr
        /* Emulate H_SET_DABR/X on P8 for the sake of compat mode guests */
        rlwimi  r5, r4, 5, DAWRX_DR | DAWRX_DW
        rlwimi  r5, r4, 2, DAWRX_WT