relbranch_fixup() moves the target of a PC-relative branch from the
single-step slot back to the probed instruction, but it only knows
call, Bicc and BPcc. A taken BPr (brz, brnz, ...) or FBPfcc/FBfcc is
taken for an absolute jump, and execution resumes at the target
relative to the slot:
Unable to handle kernel paging request at virtual address 000000000fffff98
TPC: <0xfffff98>
Treat them like the other PC-relative branches.
Link: https://github.com/sparclinux/issues/issues/110
Signed-off-by: Stian Halseth <redacted>
---
arch/sparc/kernel/kprobes.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/sparc/kernel/kprobes.c b/arch/sparc/kernel/kprobes.c
index 608efda8cb948..38e6d9b51a207 100644
--- a/arch/sparc/kernel/kprobes.c
+++ b/arch/sparc/kernel/kprobes.c
@@ -219,12 +219,15 @@ static unsigned long __kprobes relbranch_fixup(u32 insn, struct kprobe *p,
if (regs->tnpc == regs->tpc + 0x4UL)
return real_pc + 0x8UL;
- /* The three cases are call, branch w/prediction,
- * and traditional branch.
+ /* The cases are call and the branches BPcc, Bicc, BPr, FBPfcc
+ * and FBfcc.
*/
if ((insn & 0xc0000000) == 0x40000000 ||
(insn & 0xc1c00000) == 0x00400000 ||
- (insn & 0xc1c00000) == 0x00800000) {
+ (insn & 0xc1c00000) == 0x00800000 ||
+ (insn & 0xc1c00000) == 0x00c00000 ||
+ (insn & 0xc1c00000) == 0x01400000 ||
+ (insn & 0xc1c00000) == 0x01800000) {
unsigned long ainsn_addr;
ainsn_addr = (unsigned long) &p->ainsn.insn[0];--
2.43.0