]> git.baikalelectronics.ru Git - kernel.git/commitdiff
powerpc/hw_breakpoints: Rewrite 8xx breakpoints to allow any address range size.
authorChristophe Leroy <christophe.leroy@c-s.fr>
Tue, 26 Nov 2019 17:43:29 +0000 (17:43 +0000)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 23 Jan 2020 10:31:14 +0000 (21:31 +1100)
Unlike standard powerpc, Powerpc 8xx doesn't have SPRN_DABR, but
it has a breakpoint support based on a set of comparators which
allow more flexibility.

Commit 4ad8622dc548 ("powerpc/8xx: Implement hw_breakpoint")
implemented breakpoints by emulating the DABR behaviour. It did
this by setting one comparator the match 4 bytes at breakpoint address
and the other comparator to match 4 bytes at breakpoint address + 4.

Rewrite 8xx hw_breakpoint to make breakpoints match all addresses
defined by the breakpoint address and length by making full use of
comparators.

Now, comparator E is set to match any address greater than breakpoint
address minus one. Comparator F is set to match any address lower than
breakpoint address plus breakpoint length. Addresses are aligned
to 32 bits.

When the breakpoint range starts at address 0, the breakpoint is set
to match comparator F only. When the breakpoint range end at address
0xffffffff, the breakpoint is set to match comparator E only.
Otherwise the breakpoint is set to match comparator E and F.

At the same time, use registers bit names instead of hardcoded values.

Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/05105deeaf63bc02151aea2cdeaf525534e0e9d4.1574790198.git.christophe.leroy@c-s.fr
arch/powerpc/include/asm/hw_breakpoint.h
arch/powerpc/include/asm/reg_8xx.h
arch/powerpc/kernel/hw_breakpoint.c
arch/powerpc/kernel/process.c

index 27ac6f5d28918b3f8a00533c3bfe1296c04dc8ee..f2f8d8aa8e3b5a0429b73b96ee16ff4c21481078 100644 (file)
@@ -34,7 +34,11 @@ struct arch_hw_breakpoint {
 #define HW_BRK_TYPE_PRIV_ALL   (HW_BRK_TYPE_USER | HW_BRK_TYPE_KERNEL | \
                                 HW_BRK_TYPE_HYP)
 
+#ifdef CONFIG_PPC_8xx
+#define HW_BREAKPOINT_ALIGN 0x3
+#else
 #define HW_BREAKPOINT_ALIGN 0x7
+#endif
 
 #define DABR_MAX_LEN   8
 #define DAWR_MAX_LEN   512
index 07df35ee8cbcfeefd737fe5a0d0d66dcfe32b614..299ee7be0f6719ecba6e417c371034acdc076546 100644 (file)
 #define SPRN_CMPE      152
 #define SPRN_CMPF      153
 #define SPRN_LCTRL1    156
+#define   LCTRL1_CTE_GT                0xc0000000
+#define   LCTRL1_CTF_LT                0x14000000
+#define   LCTRL1_CRWE_RW       0x00000000
+#define   LCTRL1_CRWE_RO       0x00040000
+#define   LCTRL1_CRWE_WO       0x000c0000
+#define   LCTRL1_CRWF_RW       0x00000000
+#define   LCTRL1_CRWF_RO       0x00010000
+#define   LCTRL1_CRWF_WO       0x00030000
 #define SPRN_LCTRL2    157
+#define   LCTRL2_LW0EN         0x80000000
+#define   LCTRL2_LW0LA_E       0x00000000
+#define   LCTRL2_LW0LA_F       0x04000000
+#define   LCTRL2_LW0LA_EandF   0x08000000
+#define   LCTRL2_LW0LADC       0x02000000
+#define   LCTRL2_SLW0EN                0x00000002
 #ifdef CONFIG_PPC_8xx
 #define SPRN_ICTRL     158
 #endif
index 58ce3d37c2a3093e68352cb61226259162f12e7e..2462cd7c565c6053f4866447b148ab0b7b3391c9 100644 (file)
@@ -160,6 +160,9 @@ static int hw_breakpoint_validate_len(struct arch_hw_breakpoint *hw)
                /* DAWR region can't cross 512 bytes boundary */
                if ((start_addr >> 9) != (end_addr >> 9))
                        return -EINVAL;
+       } else if (IS_ENABLED(CONFIG_PPC_8xx)) {
+               /* 8xx can setup a range without limitation */
+               max_len = U16_MAX;
        }
 
        if (hw_len > max_len)
@@ -328,13 +331,11 @@ int hw_breakpoint_handler(struct die_args *args)
        }
 
        info->type &= ~HW_BRK_TYPE_EXTRANEOUS_IRQ;
-       if (IS_ENABLED(CONFIG_PPC_8xx)) {
-               if (!dar_within_range(regs->dar, info))
-                       info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
-       } else {
-               if (!stepping_handler(regs, bp, info))
-                       goto out;
-       }
+       if (!dar_within_range(regs->dar, info))
+               info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
+
+       if (!IS_ENABLED(CONFIG_PPC_8xx) && !stepping_handler(regs, bp, info))
+               goto out;
 
        /*
         * As a policy, the callback is invoked in a 'trigger-after-execute'
index 4df94b6e2f3229dc3030b5d33d1f79cbe15a679a..7fcf72e58826a722ecc167c8a9cea11d06a1508c 100644 (file)
@@ -740,28 +740,6 @@ static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
                mtspr(SPRN_DABRX, dabrx);
        return 0;
 }
-#elif defined(CONFIG_PPC_8xx)
-static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
-{
-       unsigned long addr = dabr & ~HW_BRK_TYPE_DABR;
-       unsigned long lctrl1 = 0x90000000; /* compare type: equal on E & F */
-       unsigned long lctrl2 = 0x8e000002; /* watchpoint 1 on cmp E | F */
-
-       if ((dabr & HW_BRK_TYPE_RDWR) == HW_BRK_TYPE_READ)
-               lctrl1 |= 0xa0000;
-       else if ((dabr & HW_BRK_TYPE_RDWR) == HW_BRK_TYPE_WRITE)
-               lctrl1 |= 0xf0000;
-       else if ((dabr & HW_BRK_TYPE_RDWR) == 0)
-               lctrl2 = 0;
-
-       mtspr(SPRN_LCTRL2, 0);
-       mtspr(SPRN_CMPE, addr);
-       mtspr(SPRN_CMPF, addr + 4);
-       mtspr(SPRN_LCTRL1, lctrl1);
-       mtspr(SPRN_LCTRL2, lctrl2);
-
-       return 0;
-}
 #else
 static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
 {
@@ -782,6 +760,39 @@ static inline int set_dabr(struct arch_hw_breakpoint *brk)
        return __set_dabr(dabr, dabrx);
 }
 
+static inline int set_breakpoint_8xx(struct arch_hw_breakpoint *brk)
+{
+       unsigned long lctrl1 = LCTRL1_CTE_GT | LCTRL1_CTF_LT | LCTRL1_CRWE_RW |
+                              LCTRL1_CRWF_RW;
+       unsigned long lctrl2 = LCTRL2_LW0EN | LCTRL2_LW0LADC | LCTRL2_SLW0EN;
+       unsigned long start_addr = brk->address & ~HW_BREAKPOINT_ALIGN;
+       unsigned long end_addr = (brk->address + brk->len - 1) | HW_BREAKPOINT_ALIGN;
+
+       if (start_addr == 0)
+               lctrl2 |= LCTRL2_LW0LA_F;
+       else if (end_addr == ~0U)
+               lctrl2 |= LCTRL2_LW0LA_E;
+       else
+               lctrl2 |= LCTRL2_LW0LA_EandF;
+
+       mtspr(SPRN_LCTRL2, 0);
+
+       if ((brk->type & HW_BRK_TYPE_RDWR) == 0)
+               return 0;
+
+       if ((brk->type & HW_BRK_TYPE_RDWR) == HW_BRK_TYPE_READ)
+               lctrl1 |= LCTRL1_CRWE_RO | LCTRL1_CRWF_RO;
+       if ((brk->type & HW_BRK_TYPE_RDWR) == HW_BRK_TYPE_WRITE)
+               lctrl1 |= LCTRL1_CRWE_WO | LCTRL1_CRWF_WO;
+
+       mtspr(SPRN_CMPE, start_addr - 1);
+       mtspr(SPRN_CMPF, end_addr + 1);
+       mtspr(SPRN_LCTRL1, lctrl1);
+       mtspr(SPRN_LCTRL2, lctrl2);
+
+       return 0;
+}
+
 void __set_breakpoint(struct arch_hw_breakpoint *brk)
 {
        memcpy(this_cpu_ptr(&current_brk), brk, sizeof(*brk));
@@ -789,6 +800,8 @@ void __set_breakpoint(struct arch_hw_breakpoint *brk)
        if (dawr_enabled())
                // Power8 or later
                set_dawr(brk);
+       else if (IS_ENABLED(CONFIG_PPC_8xx))
+               set_breakpoint_8xx(brk);
        else if (!cpu_has_feature(CPU_FTR_ARCH_207S))
                // Power7 or earlier
                set_dabr(brk);