xref: /openbmc/linux/arch/riscv/kernel/probes/ftrace.c (revision 53df89dd)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/kprobes.h>
4 
5 /* Ftrace callback handler for kprobes -- called under preepmt disabed */
6 void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
7 			   struct ftrace_ops *ops, struct ftrace_regs *regs)
8 {
9 	struct kprobe *p;
10 	struct kprobe_ctlblk *kcb;
11 
12 	p = get_kprobe((kprobe_opcode_t *)ip);
13 	if (unlikely(!p) || kprobe_disabled(p))
14 		return;
15 
16 	kcb = get_kprobe_ctlblk();
17 	if (kprobe_running()) {
18 		kprobes_inc_nmissed_count(p);
19 	} else {
20 		unsigned long orig_ip = instruction_pointer(&(regs->regs));
21 
22 		instruction_pointer_set(&(regs->regs), ip);
23 
24 		__this_cpu_write(current_kprobe, p);
25 		kcb->kprobe_status = KPROBE_HIT_ACTIVE;
26 		if (!p->pre_handler || !p->pre_handler(p, &(regs->regs))) {
27 			/*
28 			 * Emulate singlestep (and also recover regs->pc)
29 			 * as if there is a nop
30 			 */
31 			instruction_pointer_set(&(regs->regs),
32 				(unsigned long)p->addr + MCOUNT_INSN_SIZE);
33 			if (unlikely(p->post_handler)) {
34 				kcb->kprobe_status = KPROBE_HIT_SSDONE;
35 				p->post_handler(p, &(regs->regs), 0);
36 			}
37 			instruction_pointer_set(&(regs->regs), orig_ip);
38 		}
39 
40 		/*
41 		 * If pre_handler returns !0, it changes regs->pc. We have to
42 		 * skip emulating post_handler.
43 		 */
44 		__this_cpu_write(current_kprobe, NULL);
45 	}
46 }
47 NOKPROBE_SYMBOL(kprobe_ftrace_handler);
48 
49 int arch_prepare_kprobe_ftrace(struct kprobe *p)
50 {
51 	p->ainsn.api.insn = NULL;
52 	return 0;
53 }
54