| // 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); | |
| } |