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