POWER10 DD1 has an issue where it generates watchpoint exceptions when it
shouldn't. The conditions where this occur are:

 - octword op
 - ending address of DAWR range is less than starting address of op
 - those addresses need to be in the same or in two consecutive 512B
   blocks
 - 'op address + 64B' generates an address that has a carry into bit
   52 (crosses 2K boundary)

Handle such spurious exception by considering them as extraneous and
emulating/single-steeping instruction without generating an event.

Signed-off-by: Ravi Bangoria <ravi.bango...@linux.ibm.com>
[Fixed build warning reported by kernel test robot]
Reported-by: kernel test robot <l...@intel.com>
---

Dependency: VSX-32 byte emulation support patches
  https://lore.kernel.org/r/20201011050908.72173-1-ravi.bango...@linux.ibm.com

 arch/powerpc/kernel/hw_breakpoint.c | 67 ++++++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/hw_breakpoint.c 
b/arch/powerpc/kernel/hw_breakpoint.c
index f4e8f21046f5..9e83dd3d2ec5 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -499,6 +499,11 @@ static bool is_larx_stcx_instr(int type)
        return type == LARX || type == STCX;
 }
 
+static bool is_octword_vsx_instr(int type, int size)
+{
+       return ((type == LOAD_VSX || type == STORE_VSX) && size == 32);
+}
+
 /*
  * We've failed in reliably handling the hw-breakpoint. Unregister
  * it and throw a warning message to let the user know about it.
@@ -549,6 +554,58 @@ static bool stepping_handler(struct pt_regs *regs, struct 
perf_event **bp,
        return true;
 }
 
+static void handle_p10dd1_spurious_exception(struct arch_hw_breakpoint **info,
+                                            int *hit, unsigned long ea)
+{
+       int i;
+       unsigned long hw_end_addr;
+
+       /*
+        * Handle spurious exception only when any bp_per_reg is set.
+        * Otherwise this might be created by xmon and not actually a
+        * spurious exception.
+        */
+       for (i = 0; i < nr_wp_slots(); i++) {
+               if (!info[i])
+                       continue;
+
+               hw_end_addr = ALIGN(info[i]->address + info[i]->len, 
HW_BREAKPOINT_SIZE);
+
+               /*
+                * Ending address of DAWR range is less than starting
+                * address of op.
+                */
+               if ((hw_end_addr - 1) >= ea)
+                       continue;
+
+               /*
+                * Those addresses need to be in the same or in two
+                * consecutive 512B blocks;
+                */
+               if (((hw_end_addr - 1) >> 10) != (ea >> 10))
+                       continue;
+
+               /*
+                * 'op address + 64B' generates an address that has a
+                * carry into bit 52 (crosses 2K boundary).
+                */
+               if ((ea & 0x800) == ((ea + 64) & 0x800))
+                       continue;
+
+               break;
+       }
+
+       if (i == nr_wp_slots())
+               return;
+
+       for (i = 0; i < nr_wp_slots(); i++) {
+               if (info[i]) {
+                       hit[i] = 1;
+                       info[i]->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
+               }
+       }
+}
+
 int hw_breakpoint_handler(struct die_args *args)
 {
        bool err = false;
@@ -607,8 +664,14 @@ int hw_breakpoint_handler(struct die_args *args)
                goto reset;
 
        if (!nr_hit) {
-               rc = NOTIFY_DONE;
-               goto out;
+               if (cpu_has_feature(CPU_FTR_POWER10_DD1) &&
+                   !IS_ENABLED(CONFIG_PPC_8xx) &&
+                   is_octword_vsx_instr(type, size)) {
+                       handle_p10dd1_spurious_exception(info, hit, ea);
+               } else {
+                       rc = NOTIFY_DONE;
+                       goto out;
+               }
        }
 
        /*
-- 
2.25.1

Reply via email to