csky: Add uprobes support
authorGuo Ren <guoren@linux.alibaba.com>
Thu, 2 Apr 2020 11:52:27 +0000 (19:52 +0800)
committerGuo Ren <guoren@linux.alibaba.com>
Fri, 3 Apr 2020 03:37:51 +0000 (11:37 +0800)
This patch adds support for uprobes on csky architecture.

Just like kprobe, it support single-step and simulate instructions.

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Steven Rostedt (VMware) <rostedt@goodmis.org>
arch/csky/Kconfig
arch/csky/include/asm/thread_info.h
arch/csky/include/asm/uprobes.h [new file with mode: 0644]
arch/csky/kernel/entry.S
arch/csky/kernel/probes/Makefile
arch/csky/kernel/probes/uprobes.c [new file with mode: 0644]
arch/csky/kernel/signal.c
arch/csky/kernel/traps.c

index f76c333..94545d5 100644 (file)
@@ -75,6 +75,9 @@ config CSKY
 config LOCKDEP_SUPPORT
        def_bool y
 
+config ARCH_SUPPORTS_UPROBES
+       def_bool y if !CPU_CK610
+
 config CPU_HAS_CACHEV2
        bool
 
index 0b546a5..442feda 100644 (file)
@@ -57,6 +57,7 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_SYSCALL_TRACE      3       /* syscall trace active */
 #define TIF_SYSCALL_TRACEPOINT 4       /* syscall tracepoint instrumentation */
 #define TIF_SYSCALL_AUDIT      5       /* syscall auditing */
+#define TIF_UPROBE             6       /* uprobe breakpoint or singlestep */
 #define TIF_POLLING_NRFLAG     16      /* poll_idle() is TIF_NEED_RESCHED */
 #define TIF_MEMDIE             18      /* is terminating due to OOM killer */
 #define TIF_RESTORE_SIGMASK    20      /* restore signal mask in do_signal() */
@@ -68,6 +69,7 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_SYSCALL_TRACE     (1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_TRACEPOINT        (1 << TIF_SYSCALL_TRACEPOINT)
 #define _TIF_SYSCALL_AUDIT     (1 << TIF_SYSCALL_AUDIT)
+#define _TIF_UPROBE            (1 << TIF_UPROBE)
 #define _TIF_POLLING_NRFLAG    (1 << TIF_POLLING_NRFLAG)
 #define _TIF_MEMDIE            (1 << TIF_MEMDIE)
 #define _TIF_RESTORE_SIGMASK   (1 << TIF_RESTORE_SIGMASK)
diff --git a/arch/csky/include/asm/uprobes.h b/arch/csky/include/asm/uprobes.h
new file mode 100644 (file)
index 0000000..600388e
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __ASM_CSKY_UPROBES_H
+#define __ASM_CSKY_UPROBES_H
+
+#include <asm/probes.h>
+
+#define MAX_UINSN_BYTES                4
+
+#define UPROBE_SWBP_INSN       USR_BKPT
+#define UPROBE_SWBP_INSN_SIZE  2
+#define UPROBE_XOL_SLOT_BYTES  MAX_UINSN_BYTES
+
+typedef u32 uprobe_opcode_t;
+
+struct arch_uprobe_task {
+       unsigned long   saved_trap_no;
+};
+
+struct arch_uprobe {
+       union {
+               u8 insn[MAX_UINSN_BYTES];
+               u8 ixol[MAX_UINSN_BYTES];
+       };
+       struct arch_probe_insn api;
+       unsigned long insn_size;
+       bool simulate;
+};
+
+int uprobe_breakpoint_handler(struct pt_regs *regs);
+int uprobe_single_step_handler(struct pt_regs *regs);
+
+#endif /* __ASM_CSKY_UPROBES_H */
index 8b040a1..3648195 100644 (file)
@@ -221,7 +221,7 @@ ret_from_exception:
        andn    r9, r10
 
        ldw     r12, (r9, TINFO_FLAGS)
-       andi    r12, (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED)
+       andi    r12, (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | _TIF_UPROBE)
        cmpnei  r12, 0
        bt      exit_work
 1:
index 3a857d0..1c7c6e6 100644 (file)
@@ -2,5 +2,6 @@
 obj-$(CONFIG_KPROBES)          += kprobes.o decode-insn.o simulate-insn.o
 obj-$(CONFIG_KPROBES)          += kprobes_trampoline.o
 obj-$(CONFIG_KPROBES_ON_FTRACE)        += ftrace.o
+obj-$(CONFIG_UPROBES)          += uprobes.o decode-insn.o simulate-insn.o
 
 CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
diff --git a/arch/csky/kernel/probes/uprobes.c b/arch/csky/kernel/probes/uprobes.c
new file mode 100644 (file)
index 0000000..b3a56c2
--- /dev/null
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2014-2016 Pratyush Anand <panand@redhat.com>
+ */
+#include <linux/highmem.h>
+#include <linux/ptrace.h>
+#include <linux/uprobes.h>
+#include <asm/cacheflush.h>
+
+#include "decode-insn.h"
+
+#define UPROBE_TRAP_NR UINT_MAX
+
+unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
+{
+       return instruction_pointer(regs);
+}
+
+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
+               unsigned long addr)
+{
+       probe_opcode_t insn;
+
+       insn = *(probe_opcode_t *)(&auprobe->insn[0]);
+
+       auprobe->insn_size = is_insn32(insn) ? 4 : 2;
+
+       switch (csky_probe_decode_insn(&insn, &auprobe->api)) {
+       case INSN_REJECTED:
+               return -EINVAL;
+
+       case INSN_GOOD_NO_SLOT:
+               auprobe->simulate = true;
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       utask->autask.saved_trap_no = current->thread.trap_no;
+       current->thread.trap_no = UPROBE_TRAP_NR;
+
+       instruction_pointer_set(regs, utask->xol_vaddr);
+
+       user_enable_single_step(current);
+
+       return 0;
+}
+
+int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
+
+       instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size);
+
+       user_disable_single_step(current);
+
+       return 0;
+}
+
+bool arch_uprobe_xol_was_trapped(struct task_struct *t)
+{
+       if (t->thread.trap_no != UPROBE_TRAP_NR)
+               return true;
+
+       return false;
+}
+
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       probe_opcode_t insn;
+       unsigned long addr;
+
+       if (!auprobe->simulate)
+               return false;
+
+       insn = *(probe_opcode_t *)(&auprobe->insn[0]);
+       addr = instruction_pointer(regs);
+
+       if (auprobe->api.handler)
+               auprobe->api.handler(insn, addr, regs);
+
+       return true;
+}
+
+void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       /*
+        * Task has received a fatal signal, so reset back to probbed
+        * address.
+        */
+       instruction_pointer_set(regs, utask->vaddr);
+
+       user_disable_single_step(current);
+}
+
+bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
+               struct pt_regs *regs)
+{
+       if (ctx == RP_CHECK_CHAIN_CALL)
+               return regs->usp <= ret->stack;
+       else
+               return regs->usp < ret->stack;
+}
+
+unsigned long
+arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+                                 struct pt_regs *regs)
+{
+       unsigned long ra;
+
+       ra = regs->lr;
+
+       regs->lr = trampoline_vaddr;
+
+       return ra;
+}
+
+int arch_uprobe_exception_notify(struct notifier_block *self,
+                                unsigned long val, void *data)
+{
+       return NOTIFY_DONE;
+}
+
+int uprobe_breakpoint_handler(struct pt_regs *regs)
+{
+       if (uprobe_pre_sstep_notifier(regs))
+               return 1;
+
+       return 0;
+}
+
+int uprobe_single_step_handler(struct pt_regs *regs)
+{
+       if (uprobe_post_sstep_notifier(regs))
+               return 1;
+
+       return 0;
+}
index 3d6b728..9452d65 100644 (file)
@@ -253,6 +253,9 @@ static void do_signal(struct pt_regs *regs)
 asmlinkage void do_notify_resume(struct pt_regs *regs,
        unsigned long thread_info_flags)
 {
+       if (thread_info_flags & _TIF_UPROBE)
+               uprobe_notify_resume(regs);
+
        /* Handle pending signal delivery */
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs);
index c6a4080..fcc3a69 100644 (file)
@@ -129,6 +129,10 @@ asmlinkage void trap_c(struct pt_regs *regs)
 #ifdef CONFIG_KPROBES
                if (kprobe_single_step_handler(regs))
                        return;
+#endif
+#ifdef CONFIG_UPROBES
+               if (uprobe_single_step_handler(regs))
+                       return;
 #endif
                info.si_code = TRAP_TRACE;
                sig = SIGTRAP;
@@ -138,6 +142,10 @@ asmlinkage void trap_c(struct pt_regs *regs)
 #ifdef CONFIG_KPROBES
                if (kprobe_breakpoint_handler(regs))
                        return;
+#endif
+#ifdef CONFIG_UPROBES
+               if (uprobe_breakpoint_handler(regs))
+                       return;
 #endif
                die_if_kernel("Kernel mode ILLEGAL", regs, vector);
 #ifndef CONFIG_CPU_NO_USER_BKPT