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