|
@@ -1052,6 +1052,54 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef KPROBES_CAN_USE_FTRACE
|
|
|
+/* Ftrace callback handler for kprobes */
|
|
|
+void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
|
|
|
+ struct ftrace_ops *ops, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ struct kprobe *p;
|
|
|
+ struct kprobe_ctlblk *kcb;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ /* Disable irq for emulating a breakpoint and avoiding preempt */
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ p = get_kprobe((kprobe_opcode_t *)ip);
|
|
|
+ if (unlikely(!p) || kprobe_disabled(p))
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ kcb = get_kprobe_ctlblk();
|
|
|
+ if (kprobe_running()) {
|
|
|
+ kprobes_inc_nmissed_count(p);
|
|
|
+ } else {
|
|
|
+ regs->ip += sizeof(kprobe_opcode_t);
|
|
|
+
|
|
|
+ __this_cpu_write(current_kprobe, p);
|
|
|
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
|
|
+ if (p->pre_handler)
|
|
|
+ p->pre_handler(p, regs);
|
|
|
+
|
|
|
+ if (unlikely(p->post_handler)) {
|
|
|
+ /* Emulate singlestep as if there is a 5byte nop */
|
|
|
+ regs->ip = ip + MCOUNT_INSN_SIZE;
|
|
|
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
|
|
+ p->post_handler(p, regs, 0);
|
|
|
+ }
|
|
|
+ __this_cpu_write(current_kprobe, NULL);
|
|
|
+ regs->ip = ip; /* Recover for next callback */
|
|
|
+ }
|
|
|
+end:
|
|
|
+ local_irq_restore(flags);
|
|
|
+}
|
|
|
+
|
|
|
+int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p)
|
|
|
+{
|
|
|
+ p->ainsn.insn = NULL;
|
|
|
+ p->ainsn.boostable = -1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
int __init arch_init_kprobes(void)
|
|
|
{
|
|
|
return arch_init_optprobes();
|