1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Dynamic Ftrace based Kprobes Optimization |
4 | * |
5 | * Copyright (C) Hitachi Ltd., 2012 |
6 | * Copyright 2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> |
7 | * IBM Corporation |
8 | */ |
9 | #include <linux/kprobes.h> |
10 | #include <linux/ptrace.h> |
11 | #include <linux/hardirq.h> |
12 | #include <linux/preempt.h> |
13 | #include <linux/ftrace.h> |
14 | |
15 | /* Ftrace callback handler for kprobes */ |
16 | void kprobe_ftrace_handler(unsigned long nip, unsigned long parent_nip, |
17 | struct ftrace_ops *ops, struct ftrace_regs *fregs) |
18 | { |
19 | struct kprobe *p; |
20 | struct kprobe_ctlblk *kcb; |
21 | struct pt_regs *regs; |
22 | int bit; |
23 | |
24 | bit = ftrace_test_recursion_trylock(ip: nip, parent_ip: parent_nip); |
25 | if (bit < 0) |
26 | return; |
27 | |
28 | regs = ftrace_get_regs(fregs); |
29 | p = get_kprobe(addr: (kprobe_opcode_t *)nip); |
30 | if (unlikely(!p) || kprobe_disabled(p)) |
31 | goto out; |
32 | |
33 | kcb = get_kprobe_ctlblk(); |
34 | if (kprobe_running()) { |
35 | kprobes_inc_nmissed_count(p); |
36 | } else { |
37 | /* |
38 | * On powerpc, NIP is *before* this instruction for the |
39 | * pre handler |
40 | */ |
41 | regs_add_return_ip(regs, -MCOUNT_INSN_SIZE); |
42 | |
43 | __this_cpu_write(current_kprobe, p); |
44 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; |
45 | if (!p->pre_handler || !p->pre_handler(p, regs)) { |
46 | /* |
47 | * Emulate singlestep (and also recover regs->nip) |
48 | * as if there is a nop |
49 | */ |
50 | regs_add_return_ip(regs, MCOUNT_INSN_SIZE); |
51 | if (unlikely(p->post_handler)) { |
52 | kcb->kprobe_status = KPROBE_HIT_SSDONE; |
53 | p->post_handler(p, regs, 0); |
54 | } |
55 | } |
56 | /* |
57 | * If pre_handler returns !0, it changes regs->nip. We have to |
58 | * skip emulating post_handler. |
59 | */ |
60 | __this_cpu_write(current_kprobe, NULL); |
61 | } |
62 | out: |
63 | ftrace_test_recursion_unlock(bit); |
64 | } |
65 | NOKPROBE_SYMBOL(kprobe_ftrace_handler); |
66 | |
67 | int arch_prepare_kprobe_ftrace(struct kprobe *p) |
68 | { |
69 | p->ainsn.insn = NULL; |
70 | p->ainsn.boostable = -1; |
71 | return 0; |
72 | } |
73 | |