// SPDX-License-Identifier: GPL-2.0 /* * arch/parisc/kernel/kprobes.c * * PA-RISC kprobes implementation * * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> * Copyright (c) 2022 Helge Deller <deller@gmx.de> */ #include <linux/types.h> #include <linux/kprobes.h> #include <linux/slab.h> #include <asm/cacheflush.h> #include <asm/patch.h> DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); int __kprobes arch_prepare_kprobe(struct kprobe *p) { if ((unsigned long)p->addr & 3UL) return -EINVAL; p->ainsn.insn = get_insn_slot(); if (!p->ainsn.insn) return -ENOMEM; /* * Set up new instructions. Second break instruction will * trigger call of parisc_kprobe_ss_handler(). */ p->opcode = *p->addr; p->ainsn.insn[0] = p->opcode; p->ainsn.insn[1] = PARISC_KPROBES_BREAK_INSN2; flush_insn_slot(p); return 0; } void __kprobes arch_remove_kprobe(struct kprobe *p) { if (!p->ainsn.insn) return; free_insn_slot(p->ainsn.insn, 0); p->ainsn.insn = NULL; } void __kprobes arch_arm_kprobe(struct kprobe *p) { patch_text(p->addr, PARISC_KPROBES_BREAK_INSN); } void __kprobes arch_disarm_kprobe(struct kprobe *p) { patch_text(p->addr, p->opcode); } static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) { kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.status = kcb->kprobe_status; } static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; } static inline void __kprobes set_current_kprobe(struct kprobe *p) { __this_cpu_write(current_kprobe, p); } static void __kprobes setup_singlestep(struct kprobe *p, struct kprobe_ctlblk *kcb, struct pt_regs *regs) { kcb->iaoq[0] = regs->iaoq[0]; kcb->iaoq[1] = regs->iaoq[1]; instruction_pointer_set(regs, (unsigned long)p->ainsn.insn); } int __kprobes parisc_kprobe_break_handler(struct pt_regs *regs) { struct kprobe *p; struct kprobe_ctlblk *kcb; preempt_disable(); kcb = get_kprobe_ctlblk(); p = get_kprobe((unsigned long *)regs->iaoq[0]); if (!p) { preempt_enable_no_resched(); return 0; } if (kprobe_running()) { /* * We have reentered the kprobe_handler, since another kprobe * was hit while within the handler, we save the original * kprobes and single step on the instruction of the new probe * without calling any user handlers to avoid recursive * kprobes. */ save_previous_kprobe(kcb); set_current_kprobe(p); kprobes_inc_nmissed_count(p); setup_singlestep(p, kcb, regs); kcb->kprobe_status = KPROBE_REENTER; return 1; } set_current_kprobe(p); kcb->kprobe_status = KPROBE_HIT_ACTIVE; /* If we have no pre-handler or it returned 0, we continue with * normal processing. If we have a pre-handler and it returned * non-zero - which means user handler setup registers to exit * to another instruction, we must skip the single stepping. */ if (!p->pre_handler || !p->pre_handler(p, regs)) { setup_singlestep(p, kcb, regs); kcb->kprobe_status = KPROBE_HIT_SS; } else { reset_current_kprobe(); preempt_enable_no_resched(); } return 1; } int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs) { struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe *p = kprobe_running(); if (!p) return 0; if (regs->iaoq[0] != (unsigned long)p->ainsn.insn+4) return 0; /* restore back original saved kprobe variables and continue */ if (kcb->kprobe_status == KPROBE_REENTER) { restore_previous_kprobe(kcb); return 1; } /* for absolute branch instructions we can copy iaoq_b. for relative * branch instructions we need to calculate the new address based on the * difference between iaoq_f and iaoq_b. We cannot use iaoq_b without * modifications because it's based on our ainsn.insn address. */ if (p->post_handler) p->post_handler(p, regs, 0); switch (regs->iir >> 26) { case 0x38: /* BE */ case 0x39: /* BE,L */ case 0x3a: /* BV */ case 0x3b: /* BVE */ /* for absolute branches, regs->iaoq[1] has already the right * address */ regs->iaoq[0] = kcb->iaoq[1]; break; default: regs->iaoq[0] = kcb->iaoq[1]; regs->iaoq[1] = regs->iaoq[0] + 4; break; } kcb->kprobe_status = KPROBE_HIT_SSDONE; reset_current_kprobe(); return 1; } void __kretprobe_trampoline(void) { asm volatile("nop"); asm volatile("nop"); } static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs); static struct kprobe trampoline_p = { .pre_handler = trampoline_probe_handler }; static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) { __kretprobe_trampoline_handler(regs, NULL); return 1; } void arch_kretprobe_fixup_return(struct pt_regs *regs, kprobe_opcode_t *correct_ret_addr) { regs->gr[2] = (unsigned long)correct_ret_addr; } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { ri->ret_addr = (kprobe_opcode_t *)regs->gr[2]; ri->fp = NULL; /* Replace the return addr with trampoline addr. */ regs->gr[2] = (unsigned long)trampoline_p.addr; } int __kprobes arch_trampoline_kprobe(struct kprobe *p) { return p->addr == trampoline_p.addr; } int __init arch_init_kprobes(void) { trampoline_p.addr = (kprobe_opcode_t *) dereference_function_descriptor(__kretprobe_trampoline); return register_kprobe(&trampoline_p); }