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 <[email protected]>
---
 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


Reply via email to