OSDN Git Service

powerpc: Add rethook support
authorMasami Hiramatsu <mhiramat@kernel.org>
Tue, 15 Mar 2022 14:01:25 +0000 (23:01 +0900)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 18 Mar 2022 03:16:44 +0000 (20:16 -0700)
Add rethook powerpc64 implementation. Most of the code has been copied from
kretprobes on powerpc64.

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Tested-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/164735288495.1084943.539630613772422267.stgit@devnote2
arch/powerpc/Kconfig
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/rethook.c [new file with mode: 0644]

index b779603..5feaa24 100644 (file)
@@ -229,6 +229,7 @@ config PPC
        select HAVE_PERF_EVENTS_NMI             if PPC64
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
+       select HAVE_RETHOOK                     if KPROBES
        select HAVE_REGS_AND_STACK_ACCESS_API
        select HAVE_RELIABLE_STACKTRACE
        select HAVE_RSEQ
index 4d78293..feb24ea 100644 (file)
@@ -115,6 +115,7 @@ obj-$(CONFIG_SMP)           += smp.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
 obj-$(CONFIG_OPTPROBES)                += optprobes.o optprobes_head.o
 obj-$(CONFIG_KPROBES_ON_FTRACE)        += kprobes-ftrace.o
+obj-$(CONFIG_RETHOOK)          += rethook.o
 obj-$(CONFIG_UPROBES)          += uprobes.o
 obj-$(CONFIG_PPC_UDBG_16550)   += legacy_serial.o udbg_16550.o
 obj-$(CONFIG_SWIOTLB)          += dma-swiotlb.o
diff --git a/arch/powerpc/kernel/rethook.c b/arch/powerpc/kernel/rethook.c
new file mode 100644 (file)
index 0000000..a8a1287
--- /dev/null
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PowerPC implementation of rethook. This depends on kprobes.
+ */
+
+#include <linux/kprobes.h>
+#include <linux/rethook.h>
+
+/*
+ * Function return trampoline:
+ *     - init_kprobes() establishes a probepoint here
+ *     - When the probed function returns, this probe
+ *             causes the handlers to fire
+ */
+asm(".global arch_rethook_trampoline\n"
+       ".type arch_rethook_trampoline, @function\n"
+       "arch_rethook_trampoline:\n"
+       "nop\n"
+       "blr\n"
+       ".size arch_rethook_trampoline, .-arch_rethook_trampoline\n");
+
+/*
+ * Called when the probe at kretprobe trampoline is hit
+ */
+static int trampoline_rethook_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       unsigned long orig_ret_address;
+
+       orig_ret_address = rethook_trampoline_handler(regs, 0);
+       /*
+        * We get here through one of two paths:
+        * 1. by taking a trap -> kprobe_handler() -> here
+        * 2. by optprobe branch -> optimized_callback() -> opt_pre_handler() -> here
+        *
+        * When going back through (1), we need regs->nip to be setup properly
+        * as it is used to determine the return address from the trap.
+        * For (2), since nip is not honoured with optprobes, we instead setup
+        * the link register properly so that the subsequent 'blr' in
+        * __kretprobe_trampoline jumps back to the right instruction.
+        *
+        * For nip, we should set the address to the previous instruction since
+        * we end up emulating it in kprobe_handler(), which increments the nip
+        * again.
+        */
+       regs_set_return_ip(regs, orig_ret_address - 4);
+       regs->link = orig_ret_address;
+
+       return 0;
+}
+NOKPROBE_SYMBOL(trampoline_rethook_handler);
+
+void arch_rethook_prepare(struct rethook_node *rh, struct pt_regs *regs, bool mcount)
+{
+       rh->ret_addr = regs->link;
+       rh->frame = 0;
+
+       /* Replace the return addr with trampoline addr */
+       regs->link = (unsigned long)arch_rethook_trampoline;
+}
+NOKPROBE_SYMBOL(arch_prepare_kretprobe);
+
+static struct kprobe trampoline_p = {
+       .addr = (kprobe_opcode_t *) &arch_rethook_trampoline,
+       .pre_handler = trampoline_rethook_handler
+};
+
+static int init_arch_rethook(void)
+{
+       return register_kprobe(&trampoline_p);
+}
+
+core_initcall(init_arch_rethook);