powerpc/hw_breakpoints: Rewrite 8xx breakpoints to allow any address range size.

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 4ad8622dc5 ("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
This commit is contained in:
Christophe Leroy 2019-11-26 17:43:29 +00:00 committed by Michael Ellerman
parent 991d656d72
commit 39413ae009
4 changed files with 61 additions and 29 deletions

View 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

View File

@ -35,7 +35,21 @@
#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

View 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'

View 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);