From: Guo Ren <[email protected]>
The patchset includes kprobe/uprobe support and some related fixups.
Patrick provides HAVE_REGS_AND_STACK_ACCESS_API support and some
kprobe's code. The framework of k/uprobe is from csky but also refers
to other arches'. kprobes on ftrace is also supported in the patchset.
Modify dynamic ftrace mechanism from mcount to fentry.
There is no single step exception in riscv ISA, only single-step
facility for jtag. See riscv-Privileged spec:
Interrupt Exception Code-Description
1 0 Reserved
1 1 Supervisor software interrupt
1 2–4 Reserved
1 5 Supervisor timer interrupt
1 6–8 Reserved
1 9 Supervisor external interrupt
1 10–15 Reserved
1 ≥16 Available for platform use
0 0 Instruction address misaligned
0 1 Instruction access fault
0 2 Illegal instruction
0 3 Breakpoint
0 4 Load address misaligned
0 5 Load access fault
0 6 Store/AMO address misaligned
0 7 Store/AMO access fault
0 8 Environment call from U-mode
0 9 Environment call from S-mode
0 10–11 Reserved
0 12 Instruction page fault
0 13 Load page fault
0 14 Reserved
0 15 Store/AMO page fault
0 16–23 Reserved
0 24–31 Available for custom use
0 32–47 Reserved
0 48–63 Available for custom use
0 ≥64 Reserved
No single step!
Other arches use hardware single-step exception for k/uprobe, eg:
- powerpc: regs->msr |= MSR_SINGLESTEP
- arm/arm64: PSTATE.D for enabling software step exceptions
- s390: Set PER control regs, turns on single step for the given address
- x86: regs->flags |= X86_EFLAGS_TF
- csky: of course use hw single step :)
All the above arches use a hardware single-step exception
mechanism to execute the instruction that was replaced with a probe
breakpoint. So utilize ebreak to simulate.
Some pc related instructions couldn't be executed out of line and some
system/fence instructions couldn't be a trace site at all. So we give
out a reject list and simulate list in decode-insn.c.
You could use uprobe to test simulate code like this:
echo 'p:enter_current_state_one /hello:0x6e4 a0=%a0 a1=%a1' >> /sys/kernel/debug/tracing/uprobe_events
echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable
/hello
^C
cat /sys/kernel/debug/tracing/trace
tracer: nop
entries-in-buffer/entries-written: 1/1 #P:1
_-----=> irqs-off
/ _----=> need-resched
| / _---=> hardirq/softirq
|| / _--=> preempt-depth
||| / delay
TASK-PID CPU# |||| TIMESTAMP FUNCTION
| | | |||| | |
hello-94 [000] d... 55.404242: enter_current_state_one: (0x106e4) a0=0x1 a1=0x3fffa8ada8
Be care /hello:0x6e4 is the file offset in elf and it relate to 0x106e4
in memory and hello is your target elf program.
Try kprobe like this:
echo 'p:myprobe _do_fork dfd=%a0 filename=%a1 flags=%a2 mode=+4($stack)' > /sys/kernel/debug/tracing/kprobe_events
echo 'r:myretprobe _do_fork $retval' >> /sys/kernel/debug/tracing/kprobe_event
echo 1 >/sys/kernel/debug/tracing/events/kprobes/enable
cat /sys/kernel/debug/tracing/trace
tracer: nop
entries-in-buffer/entries-written: 2/2 #P:1
_-----=> irqs-off
/ _----=> need-resched
| / _---=> hardirq/softirq
|| / _--=> preempt-depth
||| / delay
TASK-PID CPU# |||| TIMESTAMP FUNCTION
| | | |||| | |
sh-92 [000] .n.. 131.804230: myprobe: (_do_fork+0x0/0x2e6) dfd=0xffffffe03929fdf8 filename=0x0 flags=0x101000 mode=0x1200000ffffffe0
sh-92 [000] d... 131.806607: myretprobe: (__do_sys_clone+0x70/0x82 <- _do_fork) arg1=0x5f
cat /sys/kernel/debug/tracing/trace
Changlog v4:
- Revert fixup kprobes handler couldn't change pc
- Using PATCHABLE_FUNCTION_ENTRY instead of MCOUNT
- rebase on linux-tree:
commit 071a0578b0ce0b0e543d1e38ee6926b9cc21c198
Merge: fad7011 be4df0c
Author: Linus Torvalds <[email protected]>
Date: Fri Oct 16 15:29:46 2020 -0700
Merge tag 'ovl-update-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Changlog v3:
- Add upport for function error injection
- Fixup kprobes handler couldn't change pc
Changlog v2:
- Add Reviewed-by, Tested-by, Acked-by, thx for all of you
- Add kprobes on ftrace feature
- Use __always_inline as same as fix_to_virt for fixup
BUILD_BUG_ON
- Use const "const unsigned int" for 2th param for fixup
BUILD_BUG_ON
Guo Ren (8):
riscv: Fixup compile error BUILD_BUG_ON failed
riscv: Fixup wrong ftrace remove cflag
riscv: Using PATCHABLE_FUNCTION_ENTRY instead of MCOUNT
riscv: Add kprobes supported
riscv: Add KPROBES_ON_FTRACE supported
riscv: Add uprobes supported
riscv: Add support for function error injection
riscv: Fixup lockdep_assert_held(&text_mutex) in patch_insn_write
Patrick Stählin (1):
RISC-V: Implement ptrace regs and stack API
arch/riscv/Kconfig | 8 +
arch/riscv/Makefile | 2 +
arch/riscv/include/asm/kprobes.h | 40 +++
arch/riscv/include/asm/probes.h | 24 ++
arch/riscv/include/asm/processor.h | 1 +
arch/riscv/include/asm/ptrace.h | 35 +++
arch/riscv/include/asm/thread_info.h | 4 +-
arch/riscv/include/asm/uprobes.h | 40 +++
arch/riscv/kernel/Makefile | 5 +-
arch/riscv/kernel/ftrace.c | 94 +++---
arch/riscv/kernel/mcount-dyn.S | 211 ++++++--------
arch/riscv/kernel/patch.c | 15 +-
arch/riscv/kernel/probes/Makefile | 6 +
arch/riscv/kernel/probes/decode-insn.c | 48 ++++
arch/riscv/kernel/probes/decode-insn.h | 18 ++
arch/riscv/kernel/probes/ftrace.c | 52 ++++
arch/riscv/kernel/probes/kprobes.c | 398 ++++++++++++++++++++++++++
arch/riscv/kernel/probes/kprobes_trampoline.S | 93 ++++++
arch/riscv/kernel/probes/simulate-insn.c | 85 ++++++
arch/riscv/kernel/probes/simulate-insn.h | 47 +++
arch/riscv/kernel/probes/uprobes.c | 186 ++++++++++++
arch/riscv/kernel/ptrace.c | 99 +++++++
arch/riscv/kernel/signal.c | 3 +
arch/riscv/kernel/traps.c | 19 ++
arch/riscv/lib/Makefile | 2 +
arch/riscv/lib/error-inject.c | 10 +
arch/riscv/mm/Makefile | 2 +-
arch/riscv/mm/fault.c | 11 +
28 files changed, 1369 insertions(+), 189 deletions(-)
create mode 100644 arch/riscv/include/asm/probes.h
create mode 100644 arch/riscv/include/asm/uprobes.h
create mode 100644 arch/riscv/kernel/probes/Makefile
create mode 100644 arch/riscv/kernel/probes/decode-insn.c
create mode 100644 arch/riscv/kernel/probes/decode-insn.h
create mode 100644 arch/riscv/kernel/probes/ftrace.c
create mode 100644 arch/riscv/kernel/probes/kprobes.c
create mode 100644 arch/riscv/kernel/probes/kprobes_trampoline.S
create mode 100644 arch/riscv/kernel/probes/simulate-insn.c
create mode 100644 arch/riscv/kernel/probes/simulate-insn.h
create mode 100644 arch/riscv/kernel/probes/uprobes.c
create mode 100644 arch/riscv/lib/error-inject.c
--
2.7.4
From: Patrick Stählin <[email protected]>
Needed for kprobes support. Copied and adapted from arm64 code.
Guo Ren fixup pt_regs type for linux-5.8-rc1.
Signed-off-by: Patrick Stählin <[email protected]>
Signed-off-by: Guo Ren <[email protected]>
Reviewed-by: Pekka Enberg <[email protected]>
Reviewed-by: Zong Li <[email protected]>
Reviewed-by: Masami Hiramatsu <[email protected]>
---
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/ptrace.h | 29 ++++++++++++
arch/riscv/kernel/ptrace.c | 99 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 129 insertions(+)
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index b7821ac..e6424d8b 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -87,6 +87,7 @@ config RISCV
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select THREAD_INFO_IN_TASK
+ select HAVE_REGS_AND_STACK_ACCESS_API
config ARCH_MMAP_RND_BITS_MIN
default 18 if 64BIT
diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
index ee49f80..23372bb 100644
--- a/arch/riscv/include/asm/ptrace.h
+++ b/arch/riscv/include/asm/ptrace.h
@@ -8,6 +8,7 @@
#include <uapi/asm/ptrace.h>
#include <asm/csr.h>
+#include <linux/compiler.h>
#ifndef __ASSEMBLY__
@@ -60,6 +61,7 @@ struct pt_regs {
#define user_mode(regs) (((regs)->status & SR_PP) == 0)
+#define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0)
/* Helpers for working with the instruction pointer */
static inline unsigned long instruction_pointer(struct pt_regs *regs)
@@ -85,6 +87,12 @@ static inline void user_stack_pointer_set(struct pt_regs *regs,
regs->sp = val;
}
+/* Valid only for Kernel mode traps. */
+static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+{
+ return regs->sp;
+}
+
/* Helpers for working with the frame pointer */
static inline unsigned long frame_pointer(struct pt_regs *regs)
{
@@ -101,6 +109,27 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
return regs->a0;
}
+extern int regs_query_register_offset(const char *name);
+extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
+ unsigned int n);
+
+/**
+ * regs_get_register() - get register value from its offset
+ * @regs: pt_regs from which register value is gotten
+ * @offset: offset of the register.
+ *
+ * regs_get_register returns the value of a register whose offset from @regs.
+ * The @offset is the offset of the register in struct pt_regs.
+ * If @offset is bigger than MAX_REG_OFFSET, this returns 0.
+ */
+static inline unsigned long regs_get_register(struct pt_regs *regs,
+ unsigned int offset)
+{
+ if (unlikely(offset > MAX_REG_OFFSET))
+ return 0;
+
+ return *(unsigned long *)((unsigned long)regs + offset);
+}
#endif /* __ASSEMBLY__ */
#endif /* _ASM_RISCV_PTRACE_H */
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index 2d6395f..1a85305 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -114,6 +114,105 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
return &riscv_user_native_view;
}
+struct pt_regs_offset {
+ const char *name;
+ int offset;
+};
+
+#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
+#define REG_OFFSET_END {.name = NULL, .offset = 0}
+
+static const struct pt_regs_offset regoffset_table[] = {
+ REG_OFFSET_NAME(epc),
+ REG_OFFSET_NAME(ra),
+ REG_OFFSET_NAME(sp),
+ REG_OFFSET_NAME(gp),
+ REG_OFFSET_NAME(tp),
+ REG_OFFSET_NAME(t0),
+ REG_OFFSET_NAME(t1),
+ REG_OFFSET_NAME(t2),
+ REG_OFFSET_NAME(s0),
+ REG_OFFSET_NAME(s1),
+ REG_OFFSET_NAME(a0),
+ REG_OFFSET_NAME(a1),
+ REG_OFFSET_NAME(a2),
+ REG_OFFSET_NAME(a3),
+ REG_OFFSET_NAME(a4),
+ REG_OFFSET_NAME(a5),
+ REG_OFFSET_NAME(a6),
+ REG_OFFSET_NAME(a7),
+ REG_OFFSET_NAME(s2),
+ REG_OFFSET_NAME(s3),
+ REG_OFFSET_NAME(s4),
+ REG_OFFSET_NAME(s5),
+ REG_OFFSET_NAME(s6),
+ REG_OFFSET_NAME(s7),
+ REG_OFFSET_NAME(s8),
+ REG_OFFSET_NAME(s9),
+ REG_OFFSET_NAME(s10),
+ REG_OFFSET_NAME(s11),
+ REG_OFFSET_NAME(t3),
+ REG_OFFSET_NAME(t4),
+ REG_OFFSET_NAME(t5),
+ REG_OFFSET_NAME(t6),
+ REG_OFFSET_NAME(status),
+ REG_OFFSET_NAME(badaddr),
+ REG_OFFSET_NAME(cause),
+ REG_OFFSET_NAME(orig_a0),
+ REG_OFFSET_END,
+};
+
+/**
+ * regs_query_register_offset() - query register offset from its name
+ * @name: the name of a register
+ *
+ * regs_query_register_offset() returns the offset of a register in struct
+ * pt_regs from its name. If the name is invalid, this returns -EINVAL;
+ */
+int regs_query_register_offset(const char *name)
+{
+ const struct pt_regs_offset *roff;
+
+ for (roff = regoffset_table; roff->name != NULL; roff++)
+ if (!strcmp(roff->name, name))
+ return roff->offset;
+ return -EINVAL;
+}
+
+/**
+ * regs_within_kernel_stack() - check the address in the stack
+ * @regs: pt_regs which contains kernel stack pointer.
+ * @addr: address which is checked.
+ *
+ * regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
+ * If @addr is within the kernel stack, it returns true. If not, returns false.
+ */
+static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+{
+ return (addr & ~(THREAD_SIZE - 1)) ==
+ (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1));
+}
+
+/**
+ * regs_get_kernel_stack_nth() - get Nth entry of the stack
+ * @regs: pt_regs which contains kernel stack pointer.
+ * @n: stack entry number.
+ *
+ * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
+ * is specified by @regs. If the @n th entry is NOT in the kernel stack,
+ * this returns 0.
+ */
+unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+{
+ unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
+
+ addr += n;
+ if (regs_within_kernel_stack(regs, (unsigned long)addr))
+ return *addr;
+ else
+ return 0;
+}
+
void ptrace_disable(struct task_struct *child)
{
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
--
2.7.4
From: Guo Ren <[email protected]>
Unfortunately, the current code couldn't be compiled:
CC arch/riscv/kernel/patch.o
In file included from ./include/linux/kernel.h:11,
from ./include/linux/list.h:9,
from ./include/linux/preempt.h:11,
from ./include/linux/spinlock.h:51,
from arch/riscv/kernel/patch.c:6:
In function ‘fix_to_virt’,
inlined from ‘patch_map’ at arch/riscv/kernel/patch.c:37:17:
./include/linux/compiler.h:392:38: error: call to ‘__compiletime_assert_205’ declared with attribute error: BUILD_BUG_ON failed: idx >= __end_of_fixed_addresses
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
^
./include/linux/compiler.h:373:4: note: in definition of macro ‘__compiletime_assert’
prefix ## suffix(); \
^~~~~~
./include/linux/compiler.h:392:2: note: in expansion of macro ‘_compiletime_assert’
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
^~~~~~~~~~~~~~~~~~~
./include/linux/build_bug.h:39:37: note: in expansion of macro ‘compiletime_assert’
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
^~~~~~~~~~~~~~~~~~
./include/linux/build_bug.h:50:2: note: in expansion of macro ‘BUILD_BUG_ON_MSG’
BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
^~~~~~~~~~~~~~~~
./include/asm-generic/fixmap.h:32:2: note: in expansion of macro ‘BUILD_BUG_ON’
BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
^~~~~~~~~~~~
Because fix_to_virt(, idx) needs a const value, not a dynamic variable of
reg-a0 or BUILD_BUG_ON failed with "idx >= __end_of_fixed_addresses".
Signed-off-by: Guo Ren <[email protected]>
Reviewed-by: Masami Hiramatsu <[email protected]>
Reviewed-by: Pekka Enberg <[email protected]>
Cc: Paul Walmsley <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
---
arch/riscv/kernel/patch.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
index 3fe7a52..0b55287 100644
--- a/arch/riscv/kernel/patch.c
+++ b/arch/riscv/kernel/patch.c
@@ -20,7 +20,12 @@ struct patch_insn {
};
#ifdef CONFIG_MMU
-static void *patch_map(void *addr, int fixmap)
+/*
+ * The fix_to_virt(, idx) needs a const value (not a dynamic variable of
+ * reg-a0) or BUILD_BUG_ON failed with "idx >= __end_of_fixed_addresses".
+ * So use '__always_inline' and 'const unsigned int fixmap' here.
+ */
+static __always_inline void *patch_map(void *addr, const unsigned int fixmap)
{
uintptr_t uintaddr = (uintptr_t) addr;
struct page *page;
@@ -37,7 +42,6 @@ static void *patch_map(void *addr, int fixmap)
return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
(uintaddr & ~PAGE_MASK));
}
-NOKPROBE_SYMBOL(patch_map);
static void patch_unmap(int fixmap)
{
--
2.7.4
From: Guo Ren <[email protected]>
We must use $(CC_FLAGS_FTRACE) instead of directly using -pg. It
will cause -fpatchable-function-entry error.
Signed-off-by: Guo Ren <[email protected]>
---
arch/riscv/kernel/Makefile | 4 ++--
arch/riscv/mm/Makefile | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index dc93710..f14aed2 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -4,8 +4,8 @@
#
ifdef CONFIG_FTRACE
-CFLAGS_REMOVE_ftrace.o = -pg
-CFLAGS_REMOVE_patch.o = -pg
+CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_patch.o = $(CC_FLAGS_FTRACE)
endif
extra-y += head.o
diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile
index c0185e5..6b4b7ec 100644
--- a/arch/riscv/mm/Makefile
+++ b/arch/riscv/mm/Makefile
@@ -2,7 +2,7 @@
CFLAGS_init.o := -mcmodel=medany
ifdef CONFIG_FTRACE
-CFLAGS_REMOVE_init.o = -pg
+CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
endif
KCOV_INSTRUMENT_init.o := n
--
2.7.4
From: Guo Ren <[email protected]>
This patch changes the current detour mechanism of dynamic ftrace
which has been discussed during LPC 2020 RISCV-MC [1].
Before the patch, we used mcount for detour:
<funca>:
addi sp,sp,-16
sd ra,8(sp)
sd s0,0(sp)
addi s0,sp,16
mv a5,ra
mv a0,a5
auipc ra,0x0 -> nop
jalr -296(ra) <_mcount@plt> ->nop
...
After the patch:
<funca>:
nop
nop
nop, nop
nop, nop
nop
nop
...
The mcount mechanism is mixed with gcc function prologue which is
not very clear. The patchable function entry just put 16 bytes nop
before the front of the function prologue which could be filled
with a separated detour mechanism.
[1] https://www.linuxplumbersconf.org/event/7/contributions/807/
Signed-off-by: Guo Ren <[email protected]>
Cc: Alan Kao <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
Cc: Paul Walmsley <[email protected]>
---
arch/riscv/Makefile | 2 +
arch/riscv/kernel/ftrace.c | 94 +++++++++---------
arch/riscv/kernel/mcount-dyn.S | 211 ++++++++++++++++-------------------------
3 files changed, 131 insertions(+), 176 deletions(-)
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index fb6e37d..599aed6 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -12,6 +12,8 @@ OBJCOPYFLAGS := -O binary
LDFLAGS_vmlinux :=
ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
LDFLAGS_vmlinux := --no-relax
+ KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
+ CC_FLAGS_FTRACE := -fpatchable-function-entry=8
endif
ifeq ($(CONFIG_64BIT)$(CONFIG_CMODEL_MEDLOW),yy)
diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
index 99e12fa..a4e66b4 100644
--- a/arch/riscv/kernel/ftrace.c
+++ b/arch/riscv/kernel/ftrace.c
@@ -72,29 +72,55 @@ static int __ftrace_modify_call(unsigned long hook_pos, unsigned long target,
return 0;
}
+/*
+ * Put 5 instructions with 16 bytes at the front of function within
+ * patchable function entry nops' area.
+ *
+ * 0: addi sp,sp,-8
+ * 1: sd ra, 0(sp)
+ * 2: auipc ra, 0x?
+ * 3: jalr -?(ra)
+ * 4: ld ra,0(sp)
+ * 5: addi sp,sp,8
+ *
+ * So the opcodes is:
+ * 0: 0x1161
+ * 1: 0xe006
+ * 2: 0x????????
+ * 3: 0x????????
+ * 4: 0x6082
+ * 5: 0x0121
+ */
+#define INSN0_1 0xe0061161
+#define INSN4_5 0x01216082
+
+#define FUNC_ENTRY_SIZE 16
+#define FUNC_ENTRY_JMP 4
+
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
- int ret = ftrace_check_current_call(rec->ip, NULL);
+ unsigned int call[4] = {INSN0_1, 0, 0, INSN4_5};
+ unsigned long target = addr;
+ unsigned long caller = rec->ip + FUNC_ENTRY_JMP;
- if (ret)
- return ret;
+ call[1] = to_auipc_insn((unsigned int)(target - caller));
+ call[2] = to_jalr_insn((unsigned int)(target - caller));
+
+ if (patch_text_nosync((void *)rec->ip, call, FUNC_ENTRY_SIZE))
+ return -EPERM;
- return __ftrace_modify_call(rec->ip, addr, true);
+ return 0;
}
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
unsigned long addr)
{
- unsigned int call[2];
- int ret;
-
- make_call(rec->ip, addr, call);
- ret = ftrace_check_current_call(rec->ip, call);
+ unsigned int nops[4] = {NOP4, NOP4, NOP4, NOP4};
- if (ret)
- return ret;
+ if (patch_text_nosync((void *)rec->ip, nops, FUNC_ENTRY_SIZE))
+ return -EPERM;
- return __ftrace_modify_call(rec->ip, addr, false);
+ return 0;
}
@@ -139,15 +165,16 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
unsigned long addr)
{
unsigned int call[2];
+ unsigned long caller = rec->ip + FUNC_ENTRY_JMP;
int ret;
- make_call(rec->ip, old_addr, call);
- ret = ftrace_check_current_call(rec->ip, call);
+ make_call(caller, old_addr, call);
+ ret = ftrace_check_current_call(caller, call);
if (ret)
return ret;
- return __ftrace_modify_call(rec->ip, addr, true);
+ return __ftrace_modify_call(caller, addr, true);
}
#endif
@@ -176,53 +203,30 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
#ifdef CONFIG_DYNAMIC_FTRACE
extern void ftrace_graph_call(void);
+extern void ftrace_graph_regs_call(void);
int ftrace_enable_ftrace_graph_caller(void)
{
- unsigned int call[2];
- static int init_graph = 1;
int ret;
- make_call(&ftrace_graph_call, &ftrace_stub, call);
-
- /*
- * When enabling graph tracer for the first time, ftrace_graph_call
- * should contains a call to ftrace_stub. Once it has been disabled,
- * the 8-bytes at the position becomes NOPs.
- */
- if (init_graph) {
- ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
- call);
- init_graph = 0;
- } else {
- ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
- NULL);
- }
-
+ ret = __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+ (unsigned long)&prepare_ftrace_return, true);
if (ret)
return ret;
- return __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+ return __ftrace_modify_call((unsigned long)&ftrace_graph_regs_call,
(unsigned long)&prepare_ftrace_return, true);
}
int ftrace_disable_ftrace_graph_caller(void)
{
- unsigned int call[2];
int ret;
- make_call(&ftrace_graph_call, &prepare_ftrace_return, call);
-
- /*
- * This is to make sure that ftrace_enable_ftrace_graph_caller
- * did the right thing.
- */
- ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
- call);
-
+ ret = __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+ (unsigned long)&prepare_ftrace_return, false);
if (ret)
return ret;
- return __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+ return __ftrace_modify_call((unsigned long)&ftrace_graph_regs_call,
(unsigned long)&prepare_ftrace_return, false);
}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/riscv/kernel/mcount-dyn.S b/arch/riscv/kernel/mcount-dyn.S
index 35a6ed7..f49ac2d 100644
--- a/arch/riscv/kernel/mcount-dyn.S
+++ b/arch/riscv/kernel/mcount-dyn.S
@@ -13,117 +13,52 @@
.text
- .macro SAVE_ABI_STATE
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- addi sp, sp, -48
- sd s0, 32(sp)
- sd ra, 40(sp)
- addi s0, sp, 48
- sd t0, 24(sp)
- sd t1, 16(sp)
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
- sd t2, 8(sp)
-#endif
-#else
- addi sp, sp, -16
- sd s0, 0(sp)
- sd ra, 8(sp)
- addi s0, sp, 16
-#endif
- .endm
-
- .macro RESTORE_ABI_STATE
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- ld s0, 32(sp)
- ld ra, 40(sp)
- addi sp, sp, 48
-#else
- ld ra, 8(sp)
- ld s0, 0(sp)
- addi sp, sp, 16
-#endif
+#define FENTRY_RA_OFFSET 12
+#define ABI_SIZE_ON_STACK 72
+#define ABI_A0 0
+#define ABI_A1 8
+#define ABI_A2 16
+#define ABI_A3 24
+#define ABI_A4 32
+#define ABI_A5 40
+#define ABI_A6 48
+#define ABI_A7 56
+#define ABI_RA 64
+
+ .macro SAVE_ABI
+ addi sp, sp, -ABI_SIZE_ON_STACK
+
+ sd a0, ABI_A0(sp)
+ sd a1, ABI_A1(sp)
+ sd a2, ABI_A2(sp)
+ sd a3, ABI_A3(sp)
+ sd a4, ABI_A4(sp)
+ sd a5, ABI_A5(sp)
+ sd a6, ABI_A6(sp)
+ sd a7, ABI_A7(sp)
+ sd ra, ABI_RA(sp)
.endm
- .macro RESTORE_GRAPH_ARGS
- ld a0, 24(sp)
- ld a1, 16(sp)
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
- ld a2, 8(sp)
-#endif
+ .macro RESTORE_ABI
+ ld a0, ABI_A0(sp)
+ ld a1, ABI_A1(sp)
+ ld a2, ABI_A2(sp)
+ ld a3, ABI_A3(sp)
+ ld a4, ABI_A4(sp)
+ ld a5, ABI_A5(sp)
+ ld a6, ABI_A6(sp)
+ ld a7, ABI_A7(sp)
+ ld ra, ABI_RA(sp)
+
+ addi sp, sp, ABI_SIZE_ON_STACK
.endm
-ENTRY(ftrace_graph_caller)
- addi sp, sp, -16
- sd s0, 0(sp)
- sd ra, 8(sp)
- addi s0, sp, 16
-ftrace_graph_call:
- .global ftrace_graph_call
- /*
- * Calling ftrace_enable/disable_ftrace_graph_caller would overwrite the
- * call below. Check ftrace_modify_all_code for details.
- */
- call ftrace_stub
- ld ra, 8(sp)
- ld s0, 0(sp)
- addi sp, sp, 16
- ret
-ENDPROC(ftrace_graph_caller)
-
-ENTRY(ftrace_caller)
- /*
- * a0: the address in the caller when calling ftrace_caller
- * a1: the caller's return address
- * a2: the address of global variable function_trace_op
- */
- ld a1, -8(s0)
- addi a0, ra, -MCOUNT_INSN_SIZE
- la t5, function_trace_op
- ld a2, 0(t5)
-
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- /*
- * the graph tracer (specifically, prepare_ftrace_return) needs these
- * arguments but for now the function tracer occupies the regs, so we
- * save them in temporary regs to recover later.
- */
- addi t0, s0, -8
- mv t1, a0
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
- ld t2, -16(s0)
-#endif
-#endif
-
- SAVE_ABI_STATE
-ftrace_call:
- .global ftrace_call
- /*
- * For the dynamic ftrace to work, here we should reserve at least
- * 8 bytes for a functional auipc-jalr pair. The following call
- * serves this purpose.
- *
- * Calling ftrace_update_ftrace_func would overwrite the nops below.
- * Check ftrace_modify_all_code for details.
- */
- call ftrace_stub
-
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- RESTORE_GRAPH_ARGS
- call ftrace_graph_caller
-#endif
-
- RESTORE_ABI_STATE
- ret
-ENDPROC(ftrace_caller)
-
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
.macro SAVE_ALL
- addi sp, sp, -(PT_SIZE_ON_STACK+16)
- sd s0, (PT_SIZE_ON_STACK)(sp)
- sd ra, (PT_SIZE_ON_STACK+8)(sp)
- addi s0, sp, (PT_SIZE_ON_STACK+16)
+ addi sp, sp, -PT_SIZE_ON_STACK
sd x1, PT_RA(sp)
+ sd x1, PT_EPC(sp)
sd x2, PT_SP(sp)
sd x3, PT_GP(sp)
sd x4, PT_TP(sp)
@@ -157,7 +92,7 @@ ENDPROC(ftrace_caller)
.endm
.macro RESTORE_ALL
- ld x1, PT_RA(sp)
+ ld x1, PT_EPC(sp)
ld x2, PT_SP(sp)
ld x3, PT_GP(sp)
ld x4, PT_TP(sp)
@@ -189,48 +124,62 @@ ENDPROC(ftrace_caller)
ld x30, PT_T5(sp)
ld x31, PT_T6(sp)
- ld s0, (PT_SIZE_ON_STACK)(sp)
- ld ra, (PT_SIZE_ON_STACK+8)(sp)
- addi sp, sp, (PT_SIZE_ON_STACK+16)
+ addi sp, sp, PT_SIZE_ON_STACK
.endm
+#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
- .macro RESTORE_GRAPH_REG_ARGS
- ld a0, PT_T0(sp)
- ld a1, PT_T1(sp)
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
- ld a2, PT_T2(sp)
-#endif
- .endm
+ENTRY(ftrace_caller)
+ SAVE_ABI
-/*
- * Most of the contents are the same as ftrace_caller.
- */
-ENTRY(ftrace_regs_caller)
- /*
- * a3: the address of all registers in the stack
- */
- ld a1, -8(s0)
- addi a0, ra, -MCOUNT_INSN_SIZE
- la t5, function_trace_op
- ld a2, 0(t5)
- addi a3, sp, -(PT_SIZE_ON_STACK+16)
+ addi a0, ra, -FENTRY_RA_OFFSET
+ la a1, function_trace_op
+ ld a2, 0(a1)
+ ld a1, ABI_SIZE_ON_STACK(sp)
+ mv a3, sp
+
+ftrace_call:
+ .global ftrace_call
+ call ftrace_stub
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- addi t0, s0, -8
- mv t1, a0
+ addi a0, sp, ABI_SIZE_ON_STACK
+ ld a1, ABI_RA(sp)
+ addi a1, a1, -FENTRY_RA_OFFSET
#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
- ld t2, -16(s0)
+ mv a2, s0
#endif
+ftrace_graph_call:
+ .global ftrace_graph_call
+ call ftrace_stub
#endif
+ RESTORE_ABI
+ ret
+ENDPROC(ftrace_caller)
+
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+ENTRY(ftrace_regs_caller)
SAVE_ALL
+ addi a0, ra, -FENTRY_RA_OFFSET
+ la a1, function_trace_op
+ ld a2, 0(a1)
+ ld a1, PT_SIZE_ON_STACK(sp)
+ mv a3, sp
+
ftrace_regs_call:
.global ftrace_regs_call
call ftrace_stub
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- RESTORE_GRAPH_REG_ARGS
- call ftrace_graph_caller
+ addi a0, sp, PT_SIZE_ON_STACK
+ ld a1, PT_RA(sp)
+ addi a1, a1, -FENTRY_RA_OFFSET
+#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
+ mv a2, s0
+#endif
+ftrace_graph_regs_call:
+ .global ftrace_graph_regs_call
+ call ftrace_stub
#endif
RESTORE_ALL
--
2.7.4
From: Guo Ren <[email protected]>
This patch adds support for kprobes on ftrace call sites to avoids
much of the overhead with regular kprobes. Try it with simple
steps:
1. Get _do_fork ftrace call site.
Dump of assembler code for function _do_fork:
0xffffffe00020af64 <+0>: addi sp,sp,-128
0xffffffe00020af66 <+2>: sd s0,112(sp)
0xffffffe00020af68 <+4>: sd ra,120(sp)
0xffffffe00020af6a <+6>: addi s0,sp,128
0xffffffe00020af6c <+8>: sd s1,104(sp)
0xffffffe00020af6e <+10>: sd s2,96(sp)
0xffffffe00020af70 <+12>: sd s3,88(sp)
0xffffffe00020af72 <+14>: sd s4,80(sp)
0xffffffe00020af74 <+16>: sd s5,72(sp)
0xffffffe00020af76 <+18>: sd s6,64(sp)
0xffffffe00020af78 <+20>: sd s7,56(sp)
0xffffffe00020af7a <+22>: mv s4,a0
0xffffffe00020af7c <+24>: mv a0,ra
0xffffffe00020af7e <+26>: nop <<<<<<<< here!
0xffffffe00020af82 <+30>: nop
0xffffffe00020af86 <+34>: ld s3,0(s4)
2. Set _do_fork+26 as the kprobe.
echo 'p:myprobe _do_fork+26 dfd=%a0 filename=%a1 flags=%a2 mode=+4($stack)' > /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/kprobes/enable
cat /sys/kernel/debug/tracing/trace
tracer: nop
entries-in-buffer/entries-written: 3/3 #P:1
_-----=> irqs-off
/ _----=> need-resched
| / _---=> hardirq/softirq
|| / _--=> preempt-depth
||| / delay
TASK-PID CPU# |||| TIMESTAMP FUNCTION
| | | |||| | |
sh-87 [000] .... 551.557031: myprobe: (_do_fork+0x1a/0x2e6) dfd=0xffffffe00020af7e filename=0xffffffe00020b34e flags=0xffffffe00101e7c0 mode=0x20af86ffffffe0
cat /sys/kernel/debug/kprobes/list
ffffffe00020af7e k _do_fork+0x1a [FTRACE]
^^^^^^
Signed-off-by: Guo Ren <[email protected]>
Reviewed-by: Masami Hiramatsu <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
Cc: Paul Walmsley <[email protected]>
Cc: Björn Töpel <[email protected]>
Cc: Zong Li <[email protected]>
Cc: Pekka Enberg <[email protected]>
---
arch/riscv/Kconfig | 1 +
arch/riscv/kernel/probes/Makefile | 1 +
arch/riscv/kernel/probes/ftrace.c | 52 +++++++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+)
create mode 100644 arch/riscv/kernel/probes/ftrace.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index c07214a..feafc9d 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -68,6 +68,7 @@ config RISCV
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_VDSO if MMU && 64BIT
select HAVE_KPROBES
+ select HAVE_KPROBES_ON_FTRACE
select HAVE_KRETPROBES
select HAVE_PCI
select HAVE_PERF_EVENTS
diff --git a/arch/riscv/kernel/probes/Makefile b/arch/riscv/kernel/probes/Makefile
index 8a39507..abbd131 100644
--- a/arch/riscv/kernel/probes/Makefile
+++ b/arch/riscv/kernel/probes/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o simulate-insn.o
obj-$(CONFIG_KPROBES) += kprobes_trampoline.o
+obj-$(CONFIG_KPROBES_ON_FTRACE) += ftrace.o
CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
diff --git a/arch/riscv/kernel/probes/ftrace.c b/arch/riscv/kernel/probes/ftrace.c
new file mode 100644
index 00000000..e0fe58a
--- /dev/null
+++ b/arch/riscv/kernel/probes/ftrace.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kprobes.h>
+
+/* Ftrace callback handler for kprobes -- called under preepmt disabed */
+void 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;
+
+ p = get_kprobe((kprobe_opcode_t *)ip);
+ if (unlikely(!p) || kprobe_disabled(p))
+ return;
+
+ kcb = get_kprobe_ctlblk();
+ if (kprobe_running()) {
+ kprobes_inc_nmissed_count(p);
+ } else {
+ unsigned long orig_ip = instruction_pointer(regs);
+ instruction_pointer_set(regs, ip);
+
+ __this_cpu_write(current_kprobe, p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ /*
+ * Emulate singlestep (and also recover regs->pc)
+ * as if there is a nop
+ */
+ instruction_pointer_set(regs,
+ (unsigned long)p->addr + MCOUNT_INSN_SIZE);
+ if (unlikely(p->post_handler)) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ p->post_handler(p, regs, 0);
+ }
+ instruction_pointer_set(regs, orig_ip);
+ }
+
+ /*
+ * If pre_handler returns !0, it changes regs->pc. We have to
+ * skip emulating post_handler.
+ */
+ __this_cpu_write(current_kprobe, NULL);
+ }
+}
+NOKPROBE_SYMBOL(kprobe_ftrace_handler);
+
+int arch_prepare_kprobe_ftrace(struct kprobe *p)
+{
+ p->ainsn.api.insn = NULL;
+ return 0;
+}
--
2.7.4
From: Guo Ren <[email protected]>
This patch adds support for uprobes on riscv architecture.
Just like kprobe, it support single-step and simulate instructions.
Signed-off-by: Guo Ren <[email protected]>
Reviewed-by: Pekka Enberg <[email protected]>
Cc: Oleg Nesterov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
---
arch/riscv/Kconfig | 3 +
arch/riscv/include/asm/processor.h | 1 +
arch/riscv/include/asm/thread_info.h | 4 +-
arch/riscv/include/asm/uprobes.h | 40 ++++++++
arch/riscv/kernel/probes/Makefile | 1 +
arch/riscv/kernel/probes/uprobes.c | 186 +++++++++++++++++++++++++++++++++++
arch/riscv/kernel/signal.c | 3 +
arch/riscv/kernel/traps.c | 10 ++
arch/riscv/mm/fault.c | 7 ++
9 files changed, 254 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/include/asm/uprobes.h
create mode 100644 arch/riscv/kernel/probes/uprobes.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index feafc9d..4081ecf 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -158,6 +158,9 @@ config ARCH_WANT_GENERAL_HUGETLB
config ARCH_SUPPORTS_DEBUG_PAGEALLOC
def_bool y
+config ARCH_SUPPORTS_UPROBES
+ def_bool y
+
config SYS_SUPPORTS_HUGETLBFS
depends on MMU
def_bool y
diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
index bdddcd5..3a24003 100644
--- a/arch/riscv/include/asm/processor.h
+++ b/arch/riscv/include/asm/processor.h
@@ -34,6 +34,7 @@ struct thread_struct {
unsigned long sp; /* Kernel mode stack */
unsigned long s[12]; /* s[0]: frame pointer */
struct __riscv_d_ext_state fstate;
+ unsigned long bad_cause;
};
#define INIT_THREAD { \
diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
index 464a2bb..27240ea 100644
--- a/arch/riscv/include/asm/thread_info.h
+++ b/arch/riscv/include/asm/thread_info.h
@@ -80,6 +80,7 @@ struct thread_info {
#define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing */
#define TIF_SECCOMP 8 /* syscall secure computing */
+#define TIF_UPROBE 9 /* uprobe breakpoint or singlestep */
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
@@ -88,9 +89,10 @@ struct thread_info {
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1 << TIF_SECCOMP)
+#define _TIF_UPROBE (1 << TIF_UPROBE)
#define _TIF_WORK_MASK \
- (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED)
+ (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_UPROBE)
#define _TIF_SYSCALL_WORK \
(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT | \
diff --git a/arch/riscv/include/asm/uprobes.h b/arch/riscv/include/asm/uprobes.h
new file mode 100644
index 00000000..f2183e0
--- /dev/null
+++ b/arch/riscv/include/asm/uprobes.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ASM_RISCV_UPROBES_H
+#define _ASM_RISCV_UPROBES_H
+
+#include <asm/probes.h>
+#include <asm/patch.h>
+#include <asm/bug.h>
+
+#define MAX_UINSN_BYTES 8
+
+#ifdef CONFIG_RISCV_ISA_C
+#define UPROBE_SWBP_INSN __BUG_INSN_16
+#define UPROBE_SWBP_INSN_SIZE 2
+#else
+#define UPROBE_SWBP_INSN __BUG_INSN_32
+#define UPROBE_SWBP_INSN_SIZE 4
+#endif
+#define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES
+
+typedef u32 uprobe_opcode_t;
+
+struct arch_uprobe_task {
+ unsigned long saved_cause;
+};
+
+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;
+};
+
+bool uprobe_breakpoint_handler(struct pt_regs *regs);
+bool uprobe_single_step_handler(struct pt_regs *regs);
+
+#endif /* _ASM_RISCV_UPROBES_H */
diff --git a/arch/riscv/kernel/probes/Makefile b/arch/riscv/kernel/probes/Makefile
index abbd131..7f0840d 100644
--- a/arch/riscv/kernel/probes/Makefile
+++ b/arch/riscv/kernel/probes/Makefile
@@ -2,4 +2,5 @@
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/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c
new file mode 100644
index 00000000..7a057b5
--- /dev/null
+++ b/arch/riscv/kernel/probes/uprobes.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/highmem.h>
+#include <linux/ptrace.h>
+#include <linux/uprobes.h>
+
+#include "decode-insn.h"
+
+#define UPROBE_TRAP_NR UINT_MAX
+
+bool is_swbp_insn(uprobe_opcode_t *insn)
+{
+#ifdef CONFIG_RISCV_ISA_C
+ return (*insn & 0xffff) == UPROBE_SWBP_INSN;
+#else
+ return *insn == UPROBE_SWBP_INSN;
+#endif
+}
+
+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 opcode;
+
+ opcode = *(probe_opcode_t *)(&auprobe->insn[0]);
+
+ auprobe->insn_size = GET_INSN_LENGTH(opcode);
+
+ switch (riscv_probe_decode_insn(&opcode, &auprobe->api)) {
+ case INSN_REJECTED:
+ return -EINVAL;
+
+ case INSN_GOOD_NO_SLOT:
+ auprobe->simulate = true;
+ break;
+
+ case INSN_GOOD:
+ auprobe->simulate = false;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ struct uprobe_task *utask = current->utask;
+
+ utask->autask.saved_cause = current->thread.bad_cause;
+ current->thread.bad_cause = UPROBE_TRAP_NR;
+
+ instruction_pointer_set(regs, utask->xol_vaddr);
+
+ regs->status &= ~SR_SPIE;
+
+ 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.bad_cause != UPROBE_TRAP_NR);
+
+ instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size);
+
+ regs->status |= SR_SPIE;
+
+ return 0;
+}
+
+bool arch_uprobe_xol_was_trapped(struct task_struct *t)
+{
+ if (t->thread.bad_cause != 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);
+
+ regs->status &= ~SR_SPIE;
+}
+
+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->sp <= ret->stack;
+ else
+ return regs->sp < ret->stack;
+}
+
+unsigned long
+arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+ struct pt_regs *regs)
+{
+ unsigned long ra;
+
+ ra = regs->ra;
+
+ regs->ra = trampoline_vaddr;
+
+ return ra;
+}
+
+int arch_uprobe_exception_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ return NOTIFY_DONE;
+}
+
+bool uprobe_breakpoint_handler(struct pt_regs *regs)
+{
+ if (uprobe_pre_sstep_notifier(regs))
+ return true;
+
+ return false;
+}
+
+bool uprobe_single_step_handler(struct pt_regs *regs)
+{
+ if (uprobe_post_sstep_notifier(regs))
+ return true;
+
+ return false;
+}
+
+void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
+ void *src, unsigned long len)
+{
+ /* Initialize the slot */
+ void *kaddr = kmap_atomic(page);
+ void *dst = kaddr + (vaddr & ~PAGE_MASK);
+
+ memcpy(dst, src, len);
+
+ /* Add ebreak behind opcode to simulate singlestep */
+ if (vaddr) {
+ dst += GET_INSN_LENGTH(*(probe_opcode_t *)src);
+ *(uprobe_opcode_t *)dst = __BUG_INSN_32;
+ }
+
+ kunmap_atomic(kaddr);
+
+ /*
+ * We probably need flush_icache_user_page() but it needs vma.
+ * This should work on most of architectures by default. If
+ * architecture needs to do something different it can define
+ * its own version of the function.
+ */
+ flush_dcache_page(page);
+}
diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
index e996e08..3c169ff 100644
--- a/arch/riscv/kernel/signal.c
+++ b/arch/riscv/kernel/signal.c
@@ -309,6 +309,9 @@ static void do_signal(struct pt_regs *regs)
asmlinkage __visible 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);
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 19a788a..2bca2fa 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -76,6 +76,8 @@ void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
static void do_trap_error(struct pt_regs *regs, int signo, int code,
unsigned long addr, const char *str)
{
+ current->thread.bad_cause = regs->cause;
+
if (user_mode(regs)) {
do_trap(regs, signo, code, addr);
} else {
@@ -153,6 +155,14 @@ asmlinkage __visible void do_trap_break(struct pt_regs *regs)
if (kprobe_breakpoint_handler(regs))
return;
#endif
+#ifdef CONFIG_UPROBES
+ if (uprobe_single_step_handler(regs))
+ return;
+
+ if (uprobe_breakpoint_handler(regs))
+ return;
+#endif
+ current->thread.bad_cause = regs->cause;
if (user_mode(regs))
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c
index 6afd59d..c186455 100644
--- a/arch/riscv/mm/fault.c
+++ b/arch/riscv/mm/fault.c
@@ -155,11 +155,14 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
mmap_read_unlock(mm);
/* User mode accesses just cause a SIGSEGV */
if (user_mode(regs)) {
+ tsk->thread.bad_cause = cause;
do_trap(regs, SIGSEGV, code, addr);
return;
}
no_context:
+ tsk->thread.bad_cause = cause;
+
/* Are we prepared to handle this kernel fault? */
if (fixup_exception(regs))
return;
@@ -180,6 +183,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
* (which will retry the fault, or kill us if we got oom-killed).
*/
out_of_memory:
+ tsk->thread.bad_cause = cause;
+
mmap_read_unlock(mm);
if (!user_mode(regs))
goto no_context;
@@ -187,6 +192,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
return;
do_sigbus:
+ tsk->thread.bad_cause = cause;
+
mmap_read_unlock(mm);
/* Kernel mode? Handle exceptions or die */
if (!user_mode(regs))
--
2.7.4
From: Guo Ren <[email protected]>
This patch enables "kprobe & kretprobe" to work with ftrace
interface. It utilized software breakpoint as single-step
mechanism.
Some instructions which can't be single-step executed must be
simulated in kernel execution slot, such as: branch, jal, auipc,
la ...
Some instructions should be rejected for probing and we use a
blacklist to filter, such as: ecall, ebreak, ...
We use ebreak & c.ebreak to replace origin instruction and the
kprobe handler prepares an executable memory slot for out-of-line
execution with a copy of the original instruction being probed.
In execution slot we add ebreak behind original instruction to
simulate a single-setp mechanism.
The patch is based on packi's work [1] and csky's work [2].
- The kprobes_trampoline.S is all from packi's patch
- The single-step mechanism is new designed for riscv without hw
single-step trap
- The simulation codes are from csky
- Frankly, all codes refer to other archs' implementation
[1] https://lore.kernel.org/linux-riscv/[email protected]/
[2] https://lore.kernel.org/linux-csky/[email protected]/
Signed-off-by: Guo Ren <[email protected]>
Co-Developed-by: Patrick Stählin <[email protected]>
Acked-by: Masami Hiramatsu <[email protected]>
Tested-by: Zong Li <[email protected]>
Reviewed-by: Pekka Enberg <[email protected]>
Cc: Patrick Stählin <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
Cc: Björn Töpel <[email protected]>
---
arch/riscv/Kconfig | 2 +
arch/riscv/include/asm/kprobes.h | 40 +++
arch/riscv/include/asm/probes.h | 24 ++
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/probes/Makefile | 4 +
arch/riscv/kernel/probes/decode-insn.c | 48 ++++
arch/riscv/kernel/probes/decode-insn.h | 18 ++
arch/riscv/kernel/probes/kprobes.c | 398 ++++++++++++++++++++++++++
arch/riscv/kernel/probes/kprobes_trampoline.S | 93 ++++++
arch/riscv/kernel/probes/simulate-insn.c | 85 ++++++
arch/riscv/kernel/probes/simulate-insn.h | 47 +++
arch/riscv/kernel/traps.c | 9 +
arch/riscv/mm/fault.c | 4 +
13 files changed, 773 insertions(+)
create mode 100644 arch/riscv/include/asm/probes.h
create mode 100644 arch/riscv/kernel/probes/Makefile
create mode 100644 arch/riscv/kernel/probes/decode-insn.c
create mode 100644 arch/riscv/kernel/probes/decode-insn.h
create mode 100644 arch/riscv/kernel/probes/kprobes.c
create mode 100644 arch/riscv/kernel/probes/kprobes_trampoline.S
create mode 100644 arch/riscv/kernel/probes/simulate-insn.c
create mode 100644 arch/riscv/kernel/probes/simulate-insn.h
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index e6424d8b..c07214a 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -67,6 +67,8 @@ config RISCV
select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_VDSO if MMU && 64BIT
+ select HAVE_KPROBES
+ select HAVE_KRETPROBES
select HAVE_PCI
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
diff --git a/arch/riscv/include/asm/kprobes.h b/arch/riscv/include/asm/kprobes.h
index 56a98ea3..4647d38 100644
--- a/arch/riscv/include/asm/kprobes.h
+++ b/arch/riscv/include/asm/kprobes.h
@@ -11,4 +11,44 @@
#include <asm-generic/kprobes.h>
+#ifdef CONFIG_KPROBES
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/percpu.h>
+
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+#define MAX_INSN_SIZE 2
+
+#define flush_insn_slot(p) do { } while (0)
+#define kretprobe_blacklist_size 0
+
+#include <asm/probes.h>
+
+struct prev_kprobe {
+ struct kprobe *kp;
+ unsigned int status;
+};
+
+/* Single step context for kprobe */
+struct kprobe_step_ctx {
+ unsigned long ss_pending;
+ unsigned long match_addr;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+ unsigned int kprobe_status;
+ unsigned long saved_status;
+ struct prev_kprobe prev_kprobe;
+ struct kprobe_step_ctx ss_ctx;
+};
+
+void arch_remove_kprobe(struct kprobe *p);
+int kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr);
+bool kprobe_breakpoint_handler(struct pt_regs *regs);
+bool kprobe_single_step_handler(struct pt_regs *regs);
+void kretprobe_trampoline(void);
+void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
+
+#endif /* CONFIG_KPROBES */
#endif /* _ASM_RISCV_KPROBES_H */
diff --git a/arch/riscv/include/asm/probes.h b/arch/riscv/include/asm/probes.h
new file mode 100644
index 00000000..a787e6d
--- /dev/null
+++ b/arch/riscv/include/asm/probes.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_RISCV_PROBES_H
+#define _ASM_RISCV_PROBES_H
+
+typedef u32 probe_opcode_t;
+typedef bool (probes_handler_t) (u32 opcode, unsigned long addr, struct pt_regs *);
+
+/* architecture specific copy of original instruction */
+struct arch_probe_insn {
+ probe_opcode_t *insn;
+ probes_handler_t *handler;
+ /* restore address after simulation */
+ unsigned long restore;
+};
+
+#ifdef CONFIG_KPROBES
+typedef u32 kprobe_opcode_t;
+struct arch_specific_insn {
+ struct arch_probe_insn api;
+};
+#endif
+
+#endif /* _ASM_RISCV_PROBES_H */
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index f14aed2..80958cb 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -29,6 +29,7 @@ obj-y += riscv_ksyms.o
obj-y += stacktrace.o
obj-y += cacheinfo.o
obj-y += patch.o
+obj-y += probes/
obj-$(CONFIG_MMU) += vdso.o vdso/
obj-$(CONFIG_RISCV_M_MODE) += traps_misaligned.o
diff --git a/arch/riscv/kernel/probes/Makefile b/arch/riscv/kernel/probes/Makefile
new file mode 100644
index 00000000..8a39507
--- /dev/null
+++ b/arch/riscv/kernel/probes/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o simulate-insn.o
+obj-$(CONFIG_KPROBES) += kprobes_trampoline.o
+CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
diff --git a/arch/riscv/kernel/probes/decode-insn.c b/arch/riscv/kernel/probes/decode-insn.c
new file mode 100644
index 00000000..0876c30
--- /dev/null
+++ b/arch/riscv/kernel/probes/decode-insn.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <asm/sections.h>
+
+#include "decode-insn.h"
+#include "simulate-insn.h"
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ */
+enum probe_insn __kprobes
+riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *api)
+{
+ probe_opcode_t insn = le32_to_cpu(*addr);
+
+ /*
+ * Reject instructions list:
+ */
+ RISCV_INSN_REJECTED(system, insn);
+ RISCV_INSN_REJECTED(fence, insn);
+
+ /*
+ * Simulate instructions list:
+ * TODO: the REJECTED ones below need to be implemented
+ */
+#ifdef CONFIG_RISCV_ISA_C
+ RISCV_INSN_REJECTED(c_j, insn);
+ RISCV_INSN_REJECTED(c_jr, insn);
+ RISCV_INSN_REJECTED(c_jal, insn);
+ RISCV_INSN_REJECTED(c_jalr, insn);
+ RISCV_INSN_REJECTED(c_beqz, insn);
+ RISCV_INSN_REJECTED(c_bnez, insn);
+ RISCV_INSN_REJECTED(c_ebreak, insn);
+#endif
+
+ RISCV_INSN_REJECTED(auipc, insn);
+ RISCV_INSN_REJECTED(branch, insn);
+
+ RISCV_INSN_SET_SIMULATE(jal, insn);
+ RISCV_INSN_SET_SIMULATE(jalr, insn);
+
+ return INSN_GOOD;
+}
diff --git a/arch/riscv/kernel/probes/decode-insn.h b/arch/riscv/kernel/probes/decode-insn.h
new file mode 100644
index 00000000..42269a7
--- /dev/null
+++ b/arch/riscv/kernel/probes/decode-insn.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _RISCV_KERNEL_KPROBES_DECODE_INSN_H
+#define _RISCV_KERNEL_KPROBES_DECODE_INSN_H
+
+#include <asm/sections.h>
+#include <asm/kprobes.h>
+
+enum probe_insn {
+ INSN_REJECTED,
+ INSN_GOOD_NO_SLOT,
+ INSN_GOOD,
+};
+
+enum probe_insn __kprobes
+riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *asi);
+
+#endif /* _RISCV_KERNEL_KPROBES_DECODE_INSN_H */
diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c
new file mode 100644
index 00000000..e60893b
--- /dev/null
+++ b/arch/riscv/kernel/probes/kprobes.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/kprobes.h>
+#include <linux/extable.h>
+#include <linux/slab.h>
+#include <linux/stop_machine.h>
+#include <asm/ptrace.h>
+#include <linux/uaccess.h>
+#include <asm/sections.h>
+#include <asm/cacheflush.h>
+#include <asm/bug.h>
+#include <asm/patch.h>
+
+#include "decode-insn.h"
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *, struct pt_regs *);
+
+static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
+{
+ unsigned long offset = GET_INSN_LENGTH(p->opcode);
+
+ p->ainsn.api.restore = (unsigned long)p->addr + offset;
+
+ patch_text(p->ainsn.api.insn, p->opcode);
+ patch_text((void *)((unsigned long)(p->ainsn.api.insn) + offset),
+ __BUG_INSN_32);
+}
+
+static void __kprobes arch_prepare_simulate(struct kprobe *p)
+{
+ p->ainsn.api.restore = 0;
+}
+
+static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ if (p->ainsn.api.handler)
+ p->ainsn.api.handler((u32)p->opcode,
+ (unsigned long)p->addr, regs);
+
+ post_kprobe_handler(kcb, regs);
+}
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ unsigned long probe_addr = (unsigned long)p->addr;
+
+ if (probe_addr & 0x1) {
+ pr_warn("Address not aligned.\n");
+
+ return -EINVAL;
+ }
+
+ /* copy instruction */
+ p->opcode = le32_to_cpu(*p->addr);
+
+ /* decode instruction */
+ switch (riscv_probe_decode_insn(p->addr, &p->ainsn.api)) {
+ case INSN_REJECTED: /* insn not supported */
+ return -EINVAL;
+
+ case INSN_GOOD_NO_SLOT: /* insn need simulation */
+ p->ainsn.api.insn = NULL;
+ break;
+
+ case INSN_GOOD: /* instruction uses slot */
+ p->ainsn.api.insn = get_insn_slot();
+ if (!p->ainsn.api.insn)
+ return -ENOMEM;
+ break;
+ }
+
+ /* prepare the instruction */
+ if (p->ainsn.api.insn)
+ arch_prepare_ss_slot(p);
+ else
+ arch_prepare_simulate(p);
+
+ return 0;
+}
+
+/* install breakpoint in text */
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ if ((p->opcode & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
+ patch_text(p->addr, __BUG_INSN_32);
+ else
+ patch_text(p->addr, __BUG_INSN_16);
+}
+
+/* remove breakpoint from text */
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ patch_text(p->addr, p->opcode);
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+ __this_cpu_write(current_kprobe, p);
+}
+
+/*
+ * Interrupts need to be disabled before single-step mode is set, and not
+ * reenabled until after single-step mode ends.
+ * Without disabling interrupt on local CPU, there is a chance of
+ * interrupt occurrence in the period of exception return and start of
+ * out-of-line single-step, that result in wrongly single stepping
+ * into the interrupt handler.
+ */
+static void __kprobes kprobes_save_local_irqflag(struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ kcb->saved_status = regs->status;
+ regs->status &= ~SR_SPIE;
+}
+
+static void __kprobes kprobes_restore_local_irqflag(struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ regs->status = kcb->saved_status;
+}
+
+static void __kprobes
+set_ss_context(struct kprobe_ctlblk *kcb, unsigned long addr, struct kprobe *p)
+{
+ unsigned long offset = GET_INSN_LENGTH(p->opcode);
+
+ kcb->ss_ctx.ss_pending = true;
+ kcb->ss_ctx.match_addr = addr + offset;
+}
+
+static void __kprobes clear_ss_context(struct kprobe_ctlblk *kcb)
+{
+ kcb->ss_ctx.ss_pending = false;
+ kcb->ss_ctx.match_addr = 0;
+}
+
+static void __kprobes setup_singlestep(struct kprobe *p,
+ struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb, int reenter)
+{
+ unsigned long slot;
+
+ if (reenter) {
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ } else {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ }
+
+ if (p->ainsn.api.insn) {
+ /* prepare for single stepping */
+ slot = (unsigned long)p->ainsn.api.insn;
+
+ set_ss_context(kcb, slot, p); /* mark pending ss */
+
+ /* IRQs and single stepping do not mix well. */
+ kprobes_save_local_irqflag(kcb, regs);
+
+ instruction_pointer_set(regs, slot);
+ } else {
+ /* insn simulation */
+ arch_simulate_insn(p, regs);
+ }
+}
+
+static int __kprobes reenter_kprobe(struct kprobe *p,
+ struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb)
+{
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SSDONE:
+ case KPROBE_HIT_ACTIVE:
+ kprobes_inc_nmissed_count(p);
+ setup_singlestep(p, regs, kcb, 1);
+ break;
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ pr_warn("Unrecoverable kprobe detected.\n");
+ dump_kprobe(p);
+ BUG();
+ break;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *kcb, struct pt_regs *regs)
+{
+ struct kprobe *cur = kprobe_running();
+
+ if (!cur)
+ return;
+
+ /* return addr restore if non-branching insn */
+ if (cur->ainsn.api.restore != 0)
+ regs->epc = cur->ainsn.api.restore;
+
+ /* restore back original saved kprobe variables and continue */
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ return;
+ }
+
+ /* call post handler */
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ if (cur->post_handler) {
+ /* post_handler can hit breakpoint and single step
+ * again, so we enable D-flag for recursive exception.
+ */
+ cur->post_handler(cur, regs, 0);
+ }
+
+ reset_current_kprobe();
+}
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the ip points back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ regs->epc = (unsigned long) cur->addr;
+ if (!instruction_pointer(regs))
+ BUG();
+
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ restore_previous_kprobe(kcb);
+ else
+ reset_current_kprobe();
+
+ break;
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * We increment the nmissed count for accounting,
+ * we can also use npre/npostfault count for accounting
+ * these specific fault cases.
+ */
+ kprobes_inc_nmissed_count(cur);
+
+ /*
+ * We come here because instructions in the pre/post
+ * handler caused the page_fault, this could happen
+ * if handler tries to access user space by
+ * copy_from_user(), get_user() etc. Let the
+ * user-specified handler try to fix it first.
+ */
+ if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+ return 1;
+
+ /*
+ * In case the user-specified fault handler returned
+ * zero, try to fix up.
+ */
+ if (fixup_exception(regs))
+ return 1;
+ }
+ return 0;
+}
+
+bool __kprobes
+kprobe_breakpoint_handler(struct pt_regs *regs)
+{
+ struct kprobe *p, *cur_kprobe;
+ struct kprobe_ctlblk *kcb;
+ unsigned long addr = instruction_pointer(regs);
+
+ kcb = get_kprobe_ctlblk();
+ cur_kprobe = kprobe_running();
+
+ p = get_kprobe((kprobe_opcode_t *) addr);
+
+ if (p) {
+ if (cur_kprobe) {
+ if (reenter_kprobe(p, regs, kcb))
+ return true;
+ } else {
+ /* Probe hit */
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ /*
+ * If we have no pre-handler or it returned 0, we
+ * continue with normal processing. If we have a
+ * pre-handler and it returned non-zero, it will
+ * modify the execution path and no need to single
+ * stepping. Let's just reset current kprobe and exit.
+ *
+ * pre_handler can hit a breakpoint and can step thru
+ * before return.
+ */
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ setup_singlestep(p, regs, kcb, 0);
+ else
+ reset_current_kprobe();
+ }
+ return true;
+ }
+
+ /*
+ * The breakpoint instruction was removed right
+ * after we hit it. Another cpu has removed
+ * either a probepoint or a debugger breakpoint
+ * at this address. In either case, no further
+ * handling of this interrupt is appropriate.
+ * Return back to original instruction, and continue.
+ */
+ return false;
+}
+
+bool __kprobes
+kprobe_single_step_handler(struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ if ((kcb->ss_ctx.ss_pending)
+ && (kcb->ss_ctx.match_addr == instruction_pointer(regs))) {
+ clear_ss_context(kcb); /* clear pending ss */
+
+ kprobes_restore_local_irqflag(kcb, regs);
+
+ post_kprobe_handler(kcb, regs);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Provide a blacklist of symbols identifying ranges which cannot be kprobed.
+ * This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
+ */
+int __init arch_populate_kprobe_blacklist(void)
+{
+ int ret;
+
+ ret = kprobe_add_area_blacklist((unsigned long)__irqentry_text_start,
+ (unsigned long)__irqentry_text_end);
+ return ret;
+}
+
+void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs)
+{
+ return (void *)kretprobe_trampoline_handler(regs, &kretprobe_trampoline, NULL);
+}
+
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ ri->ret_addr = (kprobe_opcode_t *)regs->ra;
+ ri->fp = NULL;
+ regs->ra = (unsigned long) &kretprobe_trampoline;
+}
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+ return 0;
+}
+
+int __init arch_init_kprobes(void)
+{
+ return 0;
+}
diff --git a/arch/riscv/kernel/probes/kprobes_trampoline.S b/arch/riscv/kernel/probes/kprobes_trampoline.S
new file mode 100644
index 00000000..6e85d02
--- /dev/null
+++ b/arch/riscv/kernel/probes/kprobes_trampoline.S
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Author: Patrick Stählin <[email protected]>
+ */
+#include <linux/linkage.h>
+
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+
+ .text
+ .altmacro
+
+ .macro save_all_base_regs
+ REG_S x1, PT_RA(sp)
+ REG_S x3, PT_GP(sp)
+ REG_S x4, PT_TP(sp)
+ REG_S x5, PT_T0(sp)
+ REG_S x6, PT_T1(sp)
+ REG_S x7, PT_T2(sp)
+ REG_S x8, PT_S0(sp)
+ REG_S x9, PT_S1(sp)
+ REG_S x10, PT_A0(sp)
+ REG_S x11, PT_A1(sp)
+ REG_S x12, PT_A2(sp)
+ REG_S x13, PT_A3(sp)
+ REG_S x14, PT_A4(sp)
+ REG_S x15, PT_A5(sp)
+ REG_S x16, PT_A6(sp)
+ REG_S x17, PT_A7(sp)
+ REG_S x18, PT_S2(sp)
+ REG_S x19, PT_S3(sp)
+ REG_S x20, PT_S4(sp)
+ REG_S x21, PT_S5(sp)
+ REG_S x22, PT_S6(sp)
+ REG_S x23, PT_S7(sp)
+ REG_S x24, PT_S8(sp)
+ REG_S x25, PT_S9(sp)
+ REG_S x26, PT_S10(sp)
+ REG_S x27, PT_S11(sp)
+ REG_S x28, PT_T3(sp)
+ REG_S x29, PT_T4(sp)
+ REG_S x30, PT_T5(sp)
+ REG_S x31, PT_T6(sp)
+ .endm
+
+ .macro restore_all_base_regs
+ REG_L x3, PT_GP(sp)
+ REG_L x4, PT_TP(sp)
+ REG_L x5, PT_T0(sp)
+ REG_L x6, PT_T1(sp)
+ REG_L x7, PT_T2(sp)
+ REG_L x8, PT_S0(sp)
+ REG_L x9, PT_S1(sp)
+ REG_L x10, PT_A0(sp)
+ REG_L x11, PT_A1(sp)
+ REG_L x12, PT_A2(sp)
+ REG_L x13, PT_A3(sp)
+ REG_L x14, PT_A4(sp)
+ REG_L x15, PT_A5(sp)
+ REG_L x16, PT_A6(sp)
+ REG_L x17, PT_A7(sp)
+ REG_L x18, PT_S2(sp)
+ REG_L x19, PT_S3(sp)
+ REG_L x20, PT_S4(sp)
+ REG_L x21, PT_S5(sp)
+ REG_L x22, PT_S6(sp)
+ REG_L x23, PT_S7(sp)
+ REG_L x24, PT_S8(sp)
+ REG_L x25, PT_S9(sp)
+ REG_L x26, PT_S10(sp)
+ REG_L x27, PT_S11(sp)
+ REG_L x28, PT_T3(sp)
+ REG_L x29, PT_T4(sp)
+ REG_L x30, PT_T5(sp)
+ REG_L x31, PT_T6(sp)
+ .endm
+
+ENTRY(kretprobe_trampoline)
+ addi sp, sp, -(PT_SIZE_ON_STACK)
+ save_all_base_regs
+
+ move a0, sp /* pt_regs */
+
+ call trampoline_probe_handler
+
+ /* use the result as the return-address */
+ move ra, a0
+
+ restore_all_base_regs
+ addi sp, sp, PT_SIZE_ON_STACK
+
+ ret
+ENDPROC(kretprobe_trampoline)
diff --git a/arch/riscv/kernel/probes/simulate-insn.c b/arch/riscv/kernel/probes/simulate-insn.c
new file mode 100644
index 00000000..2519ce2
--- /dev/null
+++ b/arch/riscv/kernel/probes/simulate-insn.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "decode-insn.h"
+#include "simulate-insn.h"
+
+static inline bool rv_insn_reg_get_val(struct pt_regs *regs, u32 index,
+ unsigned long *ptr)
+{
+ if (index == 0)
+ *ptr = 0;
+ else if (index <= 31)
+ *ptr = *((unsigned long *)regs + index);
+ else
+ return false;
+
+ return true;
+}
+
+static inline bool rv_insn_reg_set_val(struct pt_regs *regs, u32 index,
+ unsigned long val)
+{
+ if (index == 0)
+ return false;
+ else if (index <= 31)
+ *((unsigned long *)regs + index) = val;
+ else
+ return false;
+
+ return true;
+}
+
+bool __kprobes simulate_jal(u32 opcode, unsigned long addr, struct pt_regs *regs)
+{
+ /*
+ * 31 30 21 20 19 12 11 7 6 0
+ * imm [20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode
+ * 1 10 1 8 5 JAL/J
+ */
+ bool ret;
+ u32 imm;
+ u32 index = (opcode >> 7) & 0x1f;
+
+ ret = rv_insn_reg_set_val(regs, index, addr + 4);
+ if (!ret)
+ return ret;
+
+ imm = ((opcode >> 21) & 0x3ff) << 1;
+ imm |= ((opcode >> 20) & 0x1) << 11;
+ imm |= ((opcode >> 12) & 0xff) << 12;
+ imm |= ((opcode >> 31) & 0x1) << 20;
+
+ instruction_pointer_set(regs, addr + sign_extend32((imm), 20));
+
+ return ret;
+}
+
+bool __kprobes simulate_jalr(u32 opcode, unsigned long addr, struct pt_regs *regs)
+{
+ /*
+ * 31 20 19 15 14 12 11 7 6 0
+ * offset[11:0] | rs1 | 010 | rd | opcode
+ * 12 5 3 5 JALR/JR
+ */
+ bool ret;
+ unsigned long base_addr;
+ u32 imm = (opcode >> 20) & 0xfff;
+ u32 rd_index = (opcode >> 7) & 0x1f;
+ u32 rs1_index = (opcode >> 15) & 0x1f;
+
+ ret = rv_insn_reg_set_val(regs, rd_index, addr + 4);
+ if (!ret)
+ return ret;
+
+ ret = rv_insn_reg_get_val(regs, rs1_index, &base_addr);
+ if (!ret)
+ return ret;
+
+ instruction_pointer_set(regs, (base_addr + sign_extend32((imm), 11))&~1);
+
+ return ret;
+}
diff --git a/arch/riscv/kernel/probes/simulate-insn.h b/arch/riscv/kernel/probes/simulate-insn.h
new file mode 100644
index 00000000..a62d784
--- /dev/null
+++ b/arch/riscv/kernel/probes/simulate-insn.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _RISCV_KERNEL_PROBES_SIMULATE_INSN_H
+#define _RISCV_KERNEL_PROBES_SIMULATE_INSN_H
+
+#define __RISCV_INSN_FUNCS(name, mask, val) \
+static __always_inline bool riscv_insn_is_##name(probe_opcode_t code) \
+{ \
+ BUILD_BUG_ON(~(mask) & (val)); \
+ return (code & (mask)) == (val); \
+} \
+bool simulate_##name(u32 opcode, unsigned long addr, \
+ struct pt_regs *regs);
+
+#define RISCV_INSN_REJECTED(name, code) \
+ do { \
+ if (riscv_insn_is_##name(code)) { \
+ return INSN_REJECTED; \
+ } \
+ } while (0)
+
+__RISCV_INSN_FUNCS(system, 0x7f, 0x73)
+__RISCV_INSN_FUNCS(fence, 0x7f, 0x0f)
+
+#define RISCV_INSN_SET_SIMULATE(name, code) \
+ do { \
+ if (riscv_insn_is_##name(code)) { \
+ api->handler = simulate_##name; \
+ return INSN_GOOD_NO_SLOT; \
+ } \
+ } while (0)
+
+__RISCV_INSN_FUNCS(c_j, 0xe003, 0xa001)
+__RISCV_INSN_FUNCS(c_jr, 0xf007, 0x8002)
+__RISCV_INSN_FUNCS(c_jal, 0xe003, 0x2001)
+__RISCV_INSN_FUNCS(c_jalr, 0xf007, 0x9002)
+__RISCV_INSN_FUNCS(c_beqz, 0xe003, 0xc001)
+__RISCV_INSN_FUNCS(c_bnez, 0xe003, 0xe001)
+__RISCV_INSN_FUNCS(c_ebreak, 0xffff, 0x9002)
+
+__RISCV_INSN_FUNCS(auipc, 0x7f, 0x17)
+__RISCV_INSN_FUNCS(branch, 0x7f, 0x63)
+
+__RISCV_INSN_FUNCS(jal, 0x7f, 0x6f)
+__RISCV_INSN_FUNCS(jalr, 0x707f, 0x67)
+
+#endif /* _RISCV_KERNEL_PROBES_SIMULATE_INSN_H */
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index ad14f44..19a788a 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -12,6 +12,7 @@
#include <linux/signal.h>
#include <linux/kdebug.h>
#include <linux/uaccess.h>
+#include <linux/kprobes.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/irq.h>
@@ -145,6 +146,14 @@ static inline unsigned long get_break_insn_length(unsigned long pc)
asmlinkage __visible void do_trap_break(struct pt_regs *regs)
{
+#ifdef CONFIG_KPROBES
+ if (kprobe_single_step_handler(regs))
+ return;
+
+ if (kprobe_breakpoint_handler(regs))
+ return;
+#endif
+
if (user_mode(regs))
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
#ifdef CONFIG_KGDB
diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c
index 716d64e..6afd59d 100644
--- a/arch/riscv/mm/fault.c
+++ b/arch/riscv/mm/fault.c
@@ -13,6 +13,7 @@
#include <linux/perf_event.h>
#include <linux/signal.h>
#include <linux/uaccess.h>
+#include <linux/kprobes.h>
#include <asm/ptrace.h>
#include <asm/tlbflush.h>
@@ -39,6 +40,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
tsk = current;
mm = tsk->mm;
+ if (kprobe_page_fault(regs, cause))
+ return;
+
/*
* Fault-in kernel-space virtual memory on-demand.
* The 'reference' page table is init_mm.pgd.
--
2.7.4
From: Guo Ren <[email protected]>
Inspired by the commit 42d038c4fb00 ("arm64: Add support for function
error injection"), this patch supports function error injection for
riscv.
This patch mainly support two functions: one is regs_set_return_value()
which is used to overwrite the return value; the another function is
override_function_with_return() which is to override the probed
function returning and jump to its caller.
Test log:
cd /sys/kernel/debug/fail_function
echo sys_clone > inject
echo 100 > probability
echo 1 > interval
ls /
[ 313.176875] FAULT_INJECTION: forcing a failure.
[ 313.176875] name fail_function, interval 1, probability 100, space 0, times 1
[ 313.184357] CPU: 0 PID: 87 Comm: sh Not tainted 5.8.0-rc5-00007-g6a758cc #117
[ 313.187616] Call Trace:
[ 313.189100] [<ffffffe0002036b6>] walk_stackframe+0x0/0xc2
[ 313.191626] [<ffffffe00020395c>] show_stack+0x40/0x4c
[ 313.193927] [<ffffffe000556c60>] dump_stack+0x7c/0x96
[ 313.194795] [<ffffffe0005522e8>] should_fail+0x140/0x142
[ 313.195923] [<ffffffe000299ffc>] fei_kprobe_handler+0x2c/0x5a
[ 313.197687] [<ffffffe0009e2ec4>] kprobe_breakpoint_handler+0xb4/0x18a
[ 313.200054] [<ffffffe00020357e>] do_trap_break+0x36/0xca
[ 313.202147] [<ffffffe000201bca>] ret_from_exception+0x0/0xc
[ 313.204556] [<ffffffe000201bbc>] ret_from_syscall+0x0/0x2
-sh: can't fork: Invalid argument
Signed-off-by: Guo Ren <[email protected]>
Reviewed-by: Masami Hiramatsu <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
Cc: Paul Walmsley <[email protected]>
---
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/ptrace.h | 6 ++++++
arch/riscv/lib/Makefile | 2 ++
arch/riscv/lib/error-inject.c | 10 ++++++++++
4 files changed, 19 insertions(+)
create mode 100644 arch/riscv/lib/error-inject.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 4081ecf..3d094fd 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -66,6 +66,7 @@ config RISCV
select HAVE_EBPF_JIT if MMU
select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_GCC_PLUGINS
+ select HAVE_FUNCTION_ERROR_INJECTION
select HAVE_GENERIC_VDSO if MMU && 64BIT
select HAVE_KPROBES
select HAVE_KPROBES_ON_FTRACE
diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
index 23372bb..cb4abb6 100644
--- a/arch/riscv/include/asm/ptrace.h
+++ b/arch/riscv/include/asm/ptrace.h
@@ -109,6 +109,12 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
return regs->a0;
}
+static inline void regs_set_return_value(struct pt_regs *regs,
+ unsigned long val)
+{
+ regs->a0 = val;
+}
+
extern int regs_query_register_offset(const char *name);
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
unsigned int n);
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index 0d0db80..04baa93 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -4,3 +4,5 @@ lib-y += memcpy.o
lib-y += memset.o
lib-y += uaccess.o
lib-$(CONFIG_64BIT) += tishift.o
+
+obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
diff --git a/arch/riscv/lib/error-inject.c b/arch/riscv/lib/error-inject.c
new file mode 100644
index 00000000..d667ade
--- /dev/null
+++ b/arch/riscv/lib/error-inject.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/error-injection.h>
+#include <linux/kprobes.h>
+
+void override_function_with_return(struct pt_regs *regs)
+{
+ instruction_pointer_set(regs, regs->ra);
+}
+NOKPROBE_SYMBOL(override_function_with_return);
--
2.7.4
From: Guo Ren <[email protected]>
It will cause warning messages:
echo function_graph > /sys/kernel/debug/tracing/current_tracer
[ 47.691397] ------------[ cut here ]------------
[ 47.692899] WARNING: CPU: 0 PID: 11 at arch/riscv/kernel/patch.c:63 patch_insn_write+0x182/0x19a
[ 47.694483] Modules linked in:
[ 47.695754] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
[ 47.698228] epc: ffffffe000204530 ra : ffffffe00020452c sp : ffffffe0023ffc20 gp : ffffffe0013e1fe0 tp : ffffffe0023e4e00 t0 : 0000000000000000
[ 47.701872] t1 : 000000000000000e t2 : 000000000000001b s0 : ffffffe0023ffc70 s1 : ffffffe000206850 a0 : 0000000000000000 a1 : ffffffffffffffff
[ 47.705550] a2 : 0000000000000000 a3 : ffffffe03af7c5e8 a4 : 0000000000000000 a5 : 0000000000000000 a6 : 0000000000000000 a7 : 00000000150b02d8
[ 47.709159] s2 : 0000000000000008 s3 : 0000000000000858 s4 : ffffffe0023ffc98 s5 : 0000000000000850 s6 : 0000000000000003 s7 : 0000000000000002
[ 47.714135] s8 : 0000000000000004 s9 : 0000000000000001 s10: 0000000000000001 s11: 0000000000000003 t3 : ffffffffffffe000 t4 : 0000000000d86254
[ 47.716574] t5 : 0000000000000005 t6 : 0000000000040000status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003
[ 47.720019] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
[ 47.722074] Call Trace:
[ 47.722561] [<ffffffe000203c88>] walk_stackframe+0x0/0xc2
[ 47.724608] [<ffffffe000b2020a>] show_stack+0x46/0x52
[ 47.726246] [<ffffffe000b23c24>] dump_stack+0x90/0xb6
[ 47.727672] [<ffffffe00020c9c4>] __warn+0x98/0xfa
[ 47.729131] [<ffffffe0005f4be0>] report_bug+0xaa/0x11e
[ 47.730624] [<ffffffe000203b74>] do_trap_break+0x96/0xfe
[ 47.732448] [<ffffffe000201daa>] ret_from_exception+0x0/0x14
[ 47.734341] [<ffffffe00020452c>] patch_insn_write+0x17e/0x19a
[ 47.737235] irq event stamp: 39
[ 47.738521] hardirqs last enabled at (39): [<ffffffe000201d0a>] _save_context+0xa2/0xe6
[ 47.741055] hardirqs last disabled at (38): [<ffffffe0002c2404>] multi_cpu_stop+0x130/0x166
[ 47.743551] softirqs last enabled at (0): [<ffffffe00020a4d2>] copy_process+0x430/0x1316
[ 47.746031] softirqs last disabled at (0): [<0000000000000000>] 0x0
[ 47.748617] ---[ end trace 88a1054faa6524ef ]---
Because the path of stop_machine(__ftrace_modify_code)->
ftrace_modify_all_code->...->patch_insn_write has no pair of
lock&unlock text_mutex, so we shouldn't put assert here.
Signed-off-by: Guo Ren <[email protected]>
---
arch/riscv/kernel/patch.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
index 0b55287..2ee5063 100644
--- a/arch/riscv/kernel/patch.c
+++ b/arch/riscv/kernel/patch.c
@@ -55,13 +55,6 @@ static int patch_insn_write(void *addr, const void *insn, size_t len)
bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
int ret;
- /*
- * Before reaching here, it was expected to lock the text_mutex
- * already, so we don't need to give another lock here and could
- * ensure that it was safe between each cores.
- */
- lockdep_assert_held(&text_mutex);
-
if (across_pages)
patch_map(addr + len, FIX_TEXT_POKE1);
--
2.7.4
On Sat, 17 Oct 2020 07:06:17 +0000
[email protected] wrote:
> From: Guo Ren <[email protected]>
>
> It will cause warning messages:
> echo function_graph > /sys/kernel/debug/tracing/current_tracer
> [ 47.691397] ------------[ cut here ]------------
> [ 47.692899] WARNING: CPU: 0 PID: 11 at arch/riscv/kernel/patch.c:63 patch_insn_write+0x182/0x19a
> [ 47.694483] Modules linked in:
> [ 47.695754] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> [ 47.698228] epc: ffffffe000204530 ra : ffffffe00020452c sp : ffffffe0023ffc20 gp : ffffffe0013e1fe0 tp : ffffffe0023e4e00 t0 : 0000000000000000
> [ 47.701872] t1 : 000000000000000e t2 : 000000000000001b s0 : ffffffe0023ffc70 s1 : ffffffe000206850 a0 : 0000000000000000 a1 : ffffffffffffffff
> [ 47.705550] a2 : 0000000000000000 a3 : ffffffe03af7c5e8 a4 : 0000000000000000 a5 : 0000000000000000 a6 : 0000000000000000 a7 : 00000000150b02d8
> [ 47.709159] s2 : 0000000000000008 s3 : 0000000000000858 s4 : ffffffe0023ffc98 s5 : 0000000000000850 s6 : 0000000000000003 s7 : 0000000000000002
> [ 47.714135] s8 : 0000000000000004 s9 : 0000000000000001 s10: 0000000000000001 s11: 0000000000000003 t3 : ffffffffffffe000 t4 : 0000000000d86254
> [ 47.716574] t5 : 0000000000000005 t6 : 0000000000040000status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003
> [ 47.720019] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> [ 47.722074] Call Trace:
> [ 47.722561] [<ffffffe000203c88>] walk_stackframe+0x0/0xc2
> [ 47.724608] [<ffffffe000b2020a>] show_stack+0x46/0x52
> [ 47.726246] [<ffffffe000b23c24>] dump_stack+0x90/0xb6
> [ 47.727672] [<ffffffe00020c9c4>] __warn+0x98/0xfa
> [ 47.729131] [<ffffffe0005f4be0>] report_bug+0xaa/0x11e
> [ 47.730624] [<ffffffe000203b74>] do_trap_break+0x96/0xfe
> [ 47.732448] [<ffffffe000201daa>] ret_from_exception+0x0/0x14
> [ 47.734341] [<ffffffe00020452c>] patch_insn_write+0x17e/0x19a
> [ 47.737235] irq event stamp: 39
> [ 47.738521] hardirqs last enabled at (39): [<ffffffe000201d0a>] _save_context+0xa2/0xe6
> [ 47.741055] hardirqs last disabled at (38): [<ffffffe0002c2404>] multi_cpu_stop+0x130/0x166
> [ 47.743551] softirqs last enabled at (0): [<ffffffe00020a4d2>] copy_process+0x430/0x1316
> [ 47.746031] softirqs last disabled at (0): [<0000000000000000>] 0x0
> [ 47.748617] ---[ end trace 88a1054faa6524ef ]---
>
> Because the path of stop_machine(__ftrace_modify_code)->
> ftrace_modify_all_code->...->patch_insn_write has no pair of
> lock&unlock text_mutex, so we shouldn't put assert here.
Hm, are there any chance to get the text_mutex in that path?
There are ftrace_arch_code_modify_prepare/post_process() in arch/riscv/kernel/ftrace.c,
aren't those used?
Thank you,
>
> Signed-off-by: Guo Ren <[email protected]>
> ---
> arch/riscv/kernel/patch.c | 7 -------
> 1 file changed, 7 deletions(-)
>
> diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
> index 0b55287..2ee5063 100644
> --- a/arch/riscv/kernel/patch.c
> +++ b/arch/riscv/kernel/patch.c
> @@ -55,13 +55,6 @@ static int patch_insn_write(void *addr, const void *insn, size_t len)
> bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
> int ret;
>
> - /*
> - * Before reaching here, it was expected to lock the text_mutex
> - * already, so we don't need to give another lock here and could
> - * ensure that it was safe between each cores.
> - */
> - lockdep_assert_held(&text_mutex);
> -
> if (across_pages)
> patch_map(addr + len, FIX_TEXT_POKE1);
>
> --
> 2.7.4
>
--
Masami Hiramatsu <[email protected]>
Good Idea! I'll try :P
On Mon, Oct 19, 2020 at 4:36 PM Masami Hiramatsu <[email protected]> wrote:
>
> On Sat, 17 Oct 2020 07:06:17 +0000
> [email protected] wrote:
>
> > From: Guo Ren <[email protected]>
> >
> > It will cause warning messages:
> > echo function_graph > /sys/kernel/debug/tracing/current_tracer
> > [ 47.691397] ------------[ cut here ]------------
> > [ 47.692899] WARNING: CPU: 0 PID: 11 at arch/riscv/kernel/patch.c:63 patch_insn_write+0x182/0x19a
> > [ 47.694483] Modules linked in:
> > [ 47.695754] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> > [ 47.698228] epc: ffffffe000204530 ra : ffffffe00020452c sp : ffffffe0023ffc20 gp : ffffffe0013e1fe0 tp : ffffffe0023e4e00 t0 : 0000000000000000
> > [ 47.701872] t1 : 000000000000000e t2 : 000000000000001b s0 : ffffffe0023ffc70 s1 : ffffffe000206850 a0 : 0000000000000000 a1 : ffffffffffffffff
> > [ 47.705550] a2 : 0000000000000000 a3 : ffffffe03af7c5e8 a4 : 0000000000000000 a5 : 0000000000000000 a6 : 0000000000000000 a7 : 00000000150b02d8
> > [ 47.709159] s2 : 0000000000000008 s3 : 0000000000000858 s4 : ffffffe0023ffc98 s5 : 0000000000000850 s6 : 0000000000000003 s7 : 0000000000000002
> > [ 47.714135] s8 : 0000000000000004 s9 : 0000000000000001 s10: 0000000000000001 s11: 0000000000000003 t3 : ffffffffffffe000 t4 : 0000000000d86254
> > [ 47.716574] t5 : 0000000000000005 t6 : 0000000000040000status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003
> > [ 47.720019] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> > [ 47.722074] Call Trace:
> > [ 47.722561] [<ffffffe000203c88>] walk_stackframe+0x0/0xc2
> > [ 47.724608] [<ffffffe000b2020a>] show_stack+0x46/0x52
> > [ 47.726246] [<ffffffe000b23c24>] dump_stack+0x90/0xb6
> > [ 47.727672] [<ffffffe00020c9c4>] __warn+0x98/0xfa
> > [ 47.729131] [<ffffffe0005f4be0>] report_bug+0xaa/0x11e
> > [ 47.730624] [<ffffffe000203b74>] do_trap_break+0x96/0xfe
> > [ 47.732448] [<ffffffe000201daa>] ret_from_exception+0x0/0x14
> > [ 47.734341] [<ffffffe00020452c>] patch_insn_write+0x17e/0x19a
> > [ 47.737235] irq event stamp: 39
> > [ 47.738521] hardirqs last enabled at (39): [<ffffffe000201d0a>] _save_context+0xa2/0xe6
> > [ 47.741055] hardirqs last disabled at (38): [<ffffffe0002c2404>] multi_cpu_stop+0x130/0x166
> > [ 47.743551] softirqs last enabled at (0): [<ffffffe00020a4d2>] copy_process+0x430/0x1316
> > [ 47.746031] softirqs last disabled at (0): [<0000000000000000>] 0x0
> > [ 47.748617] ---[ end trace 88a1054faa6524ef ]---
> >
> > Because the path of stop_machine(__ftrace_modify_code)->
> > ftrace_modify_all_code->...->patch_insn_write has no pair of
> > lock&unlock text_mutex, so we shouldn't put assert here.
>
> Hm, are there any chance to get the text_mutex in that path?
> There are ftrace_arch_code_modify_prepare/post_process() in arch/riscv/kernel/ftrace.c,
> aren't those used?
>
> Thank you,
>
> >
> > Signed-off-by: Guo Ren <[email protected]>
> > ---
> > arch/riscv/kernel/patch.c | 7 -------
> > 1 file changed, 7 deletions(-)
> >
> > diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
> > index 0b55287..2ee5063 100644
> > --- a/arch/riscv/kernel/patch.c
> > +++ b/arch/riscv/kernel/patch.c
> > @@ -55,13 +55,6 @@ static int patch_insn_write(void *addr, const void *insn, size_t len)
> > bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
> > int ret;
> >
> > - /*
> > - * Before reaching here, it was expected to lock the text_mutex
> > - * already, so we don't need to give another lock here and could
> > - * ensure that it was safe between each cores.
> > - */
> > - lockdep_assert_held(&text_mutex);
> > -
> > if (across_pages)
> > patch_map(addr + len, FIX_TEXT_POKE1);
> >
> > --
> > 2.7.4
> >
>
>
> --
> Masami Hiramatsu <[email protected]>
--
Best Regards
Guo Ren
ML: https://lore.kernel.org/linux-csky/
On Sat, Oct 17, 2020 at 12:07 AM <[email protected]> wrote:
>
> From: Guo Ren <[email protected]>
>
> It will cause warning messages:
> echo function_graph > /sys/kernel/debug/tracing/current_tracer
> [ 47.691397] ------------[ cut here ]------------
> [ 47.692899] WARNING: CPU: 0 PID: 11 at arch/riscv/kernel/patch.c:63 patch_insn_write+0x182/0x19a
> [ 47.694483] Modules linked in:
> [ 47.695754] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> [ 47.698228] epc: ffffffe000204530 ra : ffffffe00020452c sp : ffffffe0023ffc20 gp : ffffffe0013e1fe0 tp : ffffffe0023e4e00 t0 : 0000000000000000
> [ 47.701872] t1 : 000000000000000e t2 : 000000000000001b s0 : ffffffe0023ffc70 s1 : ffffffe000206850 a0 : 0000000000000000 a1 : ffffffffffffffff
> [ 47.705550] a2 : 0000000000000000 a3 : ffffffe03af7c5e8 a4 : 0000000000000000 a5 : 0000000000000000 a6 : 0000000000000000 a7 : 00000000150b02d8
> [ 47.709159] s2 : 0000000000000008 s3 : 0000000000000858 s4 : ffffffe0023ffc98 s5 : 0000000000000850 s6 : 0000000000000003 s7 : 0000000000000002
> [ 47.714135] s8 : 0000000000000004 s9 : 0000000000000001 s10: 0000000000000001 s11: 0000000000000003 t3 : ffffffffffffe000 t4 : 0000000000d86254
> [ 47.716574] t5 : 0000000000000005 t6 : 0000000000040000status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003
> [ 47.720019] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> [ 47.722074] Call Trace:
> [ 47.722561] [<ffffffe000203c88>] walk_stackframe+0x0/0xc2
> [ 47.724608] [<ffffffe000b2020a>] show_stack+0x46/0x52
> [ 47.726246] [<ffffffe000b23c24>] dump_stack+0x90/0xb6
> [ 47.727672] [<ffffffe00020c9c4>] __warn+0x98/0xfa
> [ 47.729131] [<ffffffe0005f4be0>] report_bug+0xaa/0x11e
> [ 47.730624] [<ffffffe000203b74>] do_trap_break+0x96/0xfe
> [ 47.732448] [<ffffffe000201daa>] ret_from_exception+0x0/0x14
> [ 47.734341] [<ffffffe00020452c>] patch_insn_write+0x17e/0x19a
> [ 47.737235] irq event stamp: 39
> [ 47.738521] hardirqs last enabled at (39): [<ffffffe000201d0a>] _save_context+0xa2/0xe6
> [ 47.741055] hardirqs last disabled at (38): [<ffffffe0002c2404>] multi_cpu_stop+0x130/0x166
> [ 47.743551] softirqs last enabled at (0): [<ffffffe00020a4d2>] copy_process+0x430/0x1316
> [ 47.746031] softirqs last disabled at (0): [<0000000000000000>] 0x0
> [ 47.748617] ---[ end trace 88a1054faa6524ef ]---
>
> Because the path of stop_machine(__ftrace_modify_code)->
> ftrace_modify_all_code->...->patch_insn_write has no pair of
> lock&unlock text_mutex, so we shouldn't put assert here.
>
What platform are you testing ? We are seeing a crash while enabling
any of the tracers multiple times
on Qemu/HiFive Unleashed.
http://lists.infradead.org/pipermail/linux-riscv/2020-October/002354.html
As per the thread, Zong agreed to take a look into that.
@Zong Li : Do you have any update regarding the ftrace issue.
> Signed-off-by: Guo Ren <[email protected]>
> ---
> arch/riscv/kernel/patch.c | 7 -------
> 1 file changed, 7 deletions(-)
>
> diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
> index 0b55287..2ee5063 100644
> --- a/arch/riscv/kernel/patch.c
> +++ b/arch/riscv/kernel/patch.c
> @@ -55,13 +55,6 @@ static int patch_insn_write(void *addr, const void *insn, size_t len)
> bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
> int ret;
>
> - /*
> - * Before reaching here, it was expected to lock the text_mutex
> - * already, so we don't need to give another lock here and could
> - * ensure that it was safe between each cores.
> - */
> - lockdep_assert_held(&text_mutex);
> -
> if (across_pages)
> patch_map(addr + len, FIX_TEXT_POKE1);
>
> --
> 2.7.4
>
>
> _______________________________________________
> linux-riscv mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-riscv
--
Regards,
Atish
On Tue, Oct 20, 2020 at 2:54 AM Atish Patra <[email protected]> wrote:
>
> On Sat, Oct 17, 2020 at 12:07 AM <[email protected]> wrote:
> >
> > From: Guo Ren <[email protected]>
> >
> > It will cause warning messages:
> > echo function_graph > /sys/kernel/debug/tracing/current_tracer
> > [ 47.691397] ------------[ cut here ]------------
> > [ 47.692899] WARNING: CPU: 0 PID: 11 at arch/riscv/kernel/patch.c:63 patch_insn_write+0x182/0x19a
> > [ 47.694483] Modules linked in:
> > [ 47.695754] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> > [ 47.698228] epc: ffffffe000204530 ra : ffffffe00020452c sp : ffffffe0023ffc20 gp : ffffffe0013e1fe0 tp : ffffffe0023e4e00 t0 : 0000000000000000
> > [ 47.701872] t1 : 000000000000000e t2 : 000000000000001b s0 : ffffffe0023ffc70 s1 : ffffffe000206850 a0 : 0000000000000000 a1 : ffffffffffffffff
> > [ 47.705550] a2 : 0000000000000000 a3 : ffffffe03af7c5e8 a4 : 0000000000000000 a5 : 0000000000000000 a6 : 0000000000000000 a7 : 00000000150b02d8
> > [ 47.709159] s2 : 0000000000000008 s3 : 0000000000000858 s4 : ffffffe0023ffc98 s5 : 0000000000000850 s6 : 0000000000000003 s7 : 0000000000000002
> > [ 47.714135] s8 : 0000000000000004 s9 : 0000000000000001 s10: 0000000000000001 s11: 0000000000000003 t3 : ffffffffffffe000 t4 : 0000000000d86254
> > [ 47.716574] t5 : 0000000000000005 t6 : 0000000000040000status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003
> > [ 47.720019] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> > [ 47.722074] Call Trace:
> > [ 47.722561] [<ffffffe000203c88>] walk_stackframe+0x0/0xc2
> > [ 47.724608] [<ffffffe000b2020a>] show_stack+0x46/0x52
> > [ 47.726246] [<ffffffe000b23c24>] dump_stack+0x90/0xb6
> > [ 47.727672] [<ffffffe00020c9c4>] __warn+0x98/0xfa
> > [ 47.729131] [<ffffffe0005f4be0>] report_bug+0xaa/0x11e
> > [ 47.730624] [<ffffffe000203b74>] do_trap_break+0x96/0xfe
> > [ 47.732448] [<ffffffe000201daa>] ret_from_exception+0x0/0x14
> > [ 47.734341] [<ffffffe00020452c>] patch_insn_write+0x17e/0x19a
> > [ 47.737235] irq event stamp: 39
> > [ 47.738521] hardirqs last enabled at (39): [<ffffffe000201d0a>] _save_context+0xa2/0xe6
> > [ 47.741055] hardirqs last disabled at (38): [<ffffffe0002c2404>] multi_cpu_stop+0x130/0x166
> > [ 47.743551] softirqs last enabled at (0): [<ffffffe00020a4d2>] copy_process+0x430/0x1316
> > [ 47.746031] softirqs last disabled at (0): [<0000000000000000>] 0x0
> > [ 47.748617] ---[ end trace 88a1054faa6524ef ]---
> >
> > Because the path of stop_machine(__ftrace_modify_code)->
> > ftrace_modify_all_code->...->patch_insn_write has no pair of
> > lock&unlock text_mutex, so we shouldn't put assert here.
> >
>
> What platform are you testing ? We are seeing a crash while enabling
> any of the tracers multiple times
> on Qemu/HiFive Unleashed.
I use qemu for testing. I've changed dynamic ftrace mechanism from
mcount to -fpatchable-entry.
The problem is made by the lockdep checking of text_mutex.
>
> http://lists.infradead.org/pipermail/linux-riscv/2020-October/002354.html
>
> As per the thread, Zong agreed to take a look into that.
> @Zong Li : Do you have any update regarding the ftrace issue.
>
> > Signed-off-by: Guo Ren <[email protected]>
> > ---
> > arch/riscv/kernel/patch.c | 7 -------
> > 1 file changed, 7 deletions(-)
> >
> > diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
> > index 0b55287..2ee5063 100644
> > --- a/arch/riscv/kernel/patch.c
> > +++ b/arch/riscv/kernel/patch.c
> > @@ -55,13 +55,6 @@ static int patch_insn_write(void *addr, const void *insn, size_t len)
> > bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
> > int ret;
> >
> > - /*
> > - * Before reaching here, it was expected to lock the text_mutex
> > - * already, so we don't need to give another lock here and could
> > - * ensure that it was safe between each cores.
> > - */
> > - lockdep_assert_held(&text_mutex);
> > -
> > if (across_pages)
> > patch_map(addr + len, FIX_TEXT_POKE1);
> >
> > --
> > 2.7.4
> >
> >
> > _______________________________________________
> > linux-riscv mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-riscv
>
>
>
> --
> Regards,
> Atish
--
Best Regards
Guo Ren
ML: https://lore.kernel.org/linux-csky/
On Mon, Oct 19, 2020 at 4:36 PM Masami Hiramatsu <[email protected]> wrote:
>
> On Sat, 17 Oct 2020 07:06:17 +0000
> [email protected] wrote:
>
> > From: Guo Ren <[email protected]>
> >
> > It will cause warning messages:
> > echo function_graph > /sys/kernel/debug/tracing/current_tracer
> > [ 47.691397] ------------[ cut here ]------------
> > [ 47.692899] WARNING: CPU: 0 PID: 11 at arch/riscv/kernel/patch.c:63 patch_insn_write+0x182/0x19a
> > [ 47.694483] Modules linked in:
> > [ 47.695754] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> > [ 47.698228] epc: ffffffe000204530 ra : ffffffe00020452c sp : ffffffe0023ffc20 gp : ffffffe0013e1fe0 tp : ffffffe0023e4e00 t0 : 0000000000000000
> > [ 47.701872] t1 : 000000000000000e t2 : 000000000000001b s0 : ffffffe0023ffc70 s1 : ffffffe000206850 a0 : 0000000000000000 a1 : ffffffffffffffff
> > [ 47.705550] a2 : 0000000000000000 a3 : ffffffe03af7c5e8 a4 : 0000000000000000 a5 : 0000000000000000 a6 : 0000000000000000 a7 : 00000000150b02d8
> > [ 47.709159] s2 : 0000000000000008 s3 : 0000000000000858 s4 : ffffffe0023ffc98 s5 : 0000000000000850 s6 : 0000000000000003 s7 : 0000000000000002
> > [ 47.714135] s8 : 0000000000000004 s9 : 0000000000000001 s10: 0000000000000001 s11: 0000000000000003 t3 : ffffffffffffe000 t4 : 0000000000d86254
> > [ 47.716574] t5 : 0000000000000005 t6 : 0000000000040000status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003
> > [ 47.720019] CPU: 0 PID: 11 Comm: migration/0 Not tainted 5.9.0-11367-g1054335 #132
> > [ 47.722074] Call Trace:
> > [ 47.722561] [<ffffffe000203c88>] walk_stackframe+0x0/0xc2
> > [ 47.724608] [<ffffffe000b2020a>] show_stack+0x46/0x52
> > [ 47.726246] [<ffffffe000b23c24>] dump_stack+0x90/0xb6
> > [ 47.727672] [<ffffffe00020c9c4>] __warn+0x98/0xfa
> > [ 47.729131] [<ffffffe0005f4be0>] report_bug+0xaa/0x11e
> > [ 47.730624] [<ffffffe000203b74>] do_trap_break+0x96/0xfe
> > [ 47.732448] [<ffffffe000201daa>] ret_from_exception+0x0/0x14
> > [ 47.734341] [<ffffffe00020452c>] patch_insn_write+0x17e/0x19a
> > [ 47.737235] irq event stamp: 39
> > [ 47.738521] hardirqs last enabled at (39): [<ffffffe000201d0a>] _save_context+0xa2/0xe6
> > [ 47.741055] hardirqs last disabled at (38): [<ffffffe0002c2404>] multi_cpu_stop+0x130/0x166
> > [ 47.743551] softirqs last enabled at (0): [<ffffffe00020a4d2>] copy_process+0x430/0x1316
> > [ 47.746031] softirqs last disabled at (0): [<0000000000000000>] 0x0
> > [ 47.748617] ---[ end trace 88a1054faa6524ef ]---
> >
> > Because the path of stop_machine(__ftrace_modify_code)->
> > ftrace_modify_all_code->...->patch_insn_write has no pair of
> > lock&unlock text_mutex, so we shouldn't put assert here.
>
> Hm, are there any chance to get the text_mutex in that path?
> There are ftrace_arch_code_modify_prepare/post_process() in arch/riscv/kernel/ftrace.c,
> aren't those used?
I found ftrace_run_update_code:
- ftrace_arch_code_modify_prepare()-> mutex_lock(&text_mutex)
- stop_machine(__ftrace_modify_code, &command, NULL); - >
__ftrace_modify_code -> patch_insn_write -> lockdep_assert_held ->
lockdep_is_held
- ftrace_arch_code_modify_post_process()-> mutex_unlock(&text_mutex)
Because of stop_machine, __ftrace_modify_code and
ftrace_run_update_code are not the same task. This will cause
lockdep_is_held return 0.
ref to __lock_is_held:
volatile int __lock_is_held(const struct lockdep_map *lock, int read)
{
struct task_struct *curr = current; // current is from
stop_machine, not ftrace_run_update_code. So never match!
int i;
for (i = 0; i < curr->lockdep_depth; i++) {
struct held_lock *hlock = curr->held_locks + i;
if (match_held_lock(hlock, lock)) {
if (read == -1 || hlock->read == read)
return 1;
return 0;
}
}
return 0;
}
Does it seem a comm issue? I'm not so expert on lockdep mechanism.
>
> Thank you,
>
> >
> > Signed-off-by: Guo Ren <[email protected]>
> > ---
> > arch/riscv/kernel/patch.c | 7 -------
> > 1 file changed, 7 deletions(-)
> >
> > diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
> > index 0b55287..2ee5063 100644
> > --- a/arch/riscv/kernel/patch.c
> > +++ b/arch/riscv/kernel/patch.c
> > @@ -55,13 +55,6 @@ static int patch_insn_write(void *addr, const void *insn, size_t len)
> > bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
> > int ret;
> >
> > - /*
> > - * Before reaching here, it was expected to lock the text_mutex
> > - * already, so we don't need to give another lock here and could
> > - * ensure that it was safe between each cores.
> > - */
> > - lockdep_assert_held(&text_mutex);
> > -
> > if (across_pages)
> > patch_map(addr + len, FIX_TEXT_POKE1);
> >
> > --
> > 2.7.4
> >
>
>
> --
> Masami Hiramatsu <[email protected]>
--
Best Regards
Guo Ren
ML: https://lore.kernel.org/linux-csky/
On Tue, 20 Oct 2020 19:18:01 +0800
Guo Ren <[email protected]> wrote:
> > What platform are you testing ? We are seeing a crash while enabling
> > any of the tracers multiple times
> > on Qemu/HiFive Unleashed.
> I use qemu for testing. I've changed dynamic ftrace mechanism from
> mcount to -fpatchable-entry.
>
> The problem is made by the lockdep checking of text_mutex.
If you are switching to "patchable-entry" you shouldn't need to use
stop_machine for the updates. No?
-- Steve
On Wed, Oct 21, 2020 at 4:41 AM Steven Rostedt <[email protected]> wrote:
>
> On Tue, 20 Oct 2020 19:18:01 +0800
> Guo Ren <[email protected]> wrote:
>
> > > What platform are you testing ? We are seeing a crash while enabling
> > > any of the tracers multiple times
> > > on Qemu/HiFive Unleashed.
> > I use qemu for testing. I've changed dynamic ftrace mechanism from
> > mcount to -fpatchable-entry.
> >
> > The problem is made by the lockdep checking of text_mutex.
>
> If you are switching to "patchable-entry" you shouldn't need to use
> stop_machine for the updates. No?
>
> -- Steve
Hi all,
I'm going to send the patch to fix the problem. Ftrace was broken from
v5.3 kernel version, and only happen on SMP. The problem is caused by
the following two patches:
Commit 4ecf0a43e729a7e641d800c294faabe87378fc05 ("processor: get rid
of cpu_relax_yield")
and
Commit 366237e7b0833faa2d8da7a8d7d7da8c3ca802e5 ("stop_machine:
Provide RCU quiescent state in multi_cpu_stop()")
We have to mark these two functions as notrace. The stopped CPUs
cannot make function calls to traceable functions on RISC-V, the
function call instruction pattern needs two instructions (auipc,
jalr), so there is a change to execute the (auipc + nop) or (nop,
jalr) when patching code.
There is a similar fix as follow:
Commit cb9d7fd51d9fbb329d182423bd7b92d0f8cb0e01 ("watchdog: Mark
watchdog touch functions as notrace")
I have verified my patches, and I'm going to send it to the mailing
list these few days.
Why?
funca:
nop addi sp,sp, -8
nop sd ra, 0(sp)
nop, nop auipc ra, 0x?
nop, nop -> jalr -?(ra)
nop ld ra,0(sp)
nop addi sp,sp, 8
When CPU0 has only changed half of them, CPU1 call funca then CPU1 broken.
On Wed, Oct 21, 2020 at 4:41 AM Steven Rostedt <[email protected]> wrote:
>
> On Tue, 20 Oct 2020 19:18:01 +0800
> Guo Ren <[email protected]> wrote:
>
> > > What platform are you testing ? We are seeing a crash while enabling
> > > any of the tracers multiple times
> > > on Qemu/HiFive Unleashed.
> > I use qemu for testing. I've changed dynamic ftrace mechanism from
> > mcount to -fpatchable-entry.
> >
> > The problem is made by the lockdep checking of text_mutex.
>
> If you are switching to "patchable-entry" you shouldn't need to use
> stop_machine for the updates. No?
>
> -- Steve
--
Best Regards
Guo Ren
ML: https://lore.kernel.org/linux-csky/
On Sat, 17 Oct 2020 00:06:13 PDT (-0700), [email protected] wrote:
> From: Guo Ren <[email protected]>
>
> This patch enables "kprobe & kretprobe" to work with ftrace
> interface. It utilized software breakpoint as single-step
> mechanism.
>
> Some instructions which can't be single-step executed must be
> simulated in kernel execution slot, such as: branch, jal, auipc,
> la ...
>
> Some instructions should be rejected for probing and we use a
> blacklist to filter, such as: ecall, ebreak, ...
>
> We use ebreak & c.ebreak to replace origin instruction and the
> kprobe handler prepares an executable memory slot for out-of-line
> execution with a copy of the original instruction being probed.
> In execution slot we add ebreak behind original instruction to
> simulate a single-setp mechanism.
>
> The patch is based on packi's work [1] and csky's work [2].
> - The kprobes_trampoline.S is all from packi's patch
> - The single-step mechanism is new designed for riscv without hw
> single-step trap
> - The simulation codes are from csky
> - Frankly, all codes refer to other archs' implementation
>
> [1] https://lore.kernel.org/linux-riscv/[email protected]/
> [2] https://lore.kernel.org/linux-csky/[email protected]/
>
> Signed-off-by: Guo Ren <[email protected]>
> Co-Developed-by: Patrick Stählin <[email protected]>
Checkpatch says
WARNING: 'Co-developed-by:' is the preferred signature form
#108:
Co-Developed-by: Patrick Stählin <[email protected]>
WARNING: Co-developed-by: must be immediately followed by Signed-off-by:
#108:
Co-Developed-by: Patrick Stählin <[email protected]>
Acked-by: Masami Hiramatsu <[email protected]>
There's some other checkpatch warnings throughout the patch set, but this is
one I'm not supposed to fix up. Can one of you re-post the patches with the
correct DCO?
> Acked-by: Masami Hiramatsu <[email protected]>
> Tested-by: Zong Li <[email protected]>
> Reviewed-by: Pekka Enberg <[email protected]>
> Cc: Patrick Stählin <[email protected]>
> Cc: Palmer Dabbelt <[email protected]>
> Cc: Björn Töpel <[email protected]>
> ---
> arch/riscv/Kconfig | 2 +
> arch/riscv/include/asm/kprobes.h | 40 +++
> arch/riscv/include/asm/probes.h | 24 ++
> arch/riscv/kernel/Makefile | 1 +
> arch/riscv/kernel/probes/Makefile | 4 +
> arch/riscv/kernel/probes/decode-insn.c | 48 ++++
> arch/riscv/kernel/probes/decode-insn.h | 18 ++
> arch/riscv/kernel/probes/kprobes.c | 398 ++++++++++++++++++++++++++
> arch/riscv/kernel/probes/kprobes_trampoline.S | 93 ++++++
> arch/riscv/kernel/probes/simulate-insn.c | 85 ++++++
> arch/riscv/kernel/probes/simulate-insn.h | 47 +++
> arch/riscv/kernel/traps.c | 9 +
> arch/riscv/mm/fault.c | 4 +
> 13 files changed, 773 insertions(+)
> create mode 100644 arch/riscv/include/asm/probes.h
> create mode 100644 arch/riscv/kernel/probes/Makefile
> create mode 100644 arch/riscv/kernel/probes/decode-insn.c
> create mode 100644 arch/riscv/kernel/probes/decode-insn.h
> create mode 100644 arch/riscv/kernel/probes/kprobes.c
> create mode 100644 arch/riscv/kernel/probes/kprobes_trampoline.S
> create mode 100644 arch/riscv/kernel/probes/simulate-insn.c
> create mode 100644 arch/riscv/kernel/probes/simulate-insn.h
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index e6424d8b..c07214a 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -67,6 +67,8 @@ config RISCV
> select HAVE_FUTEX_CMPXCHG if FUTEX
> select HAVE_GCC_PLUGINS
> select HAVE_GENERIC_VDSO if MMU && 64BIT
> + select HAVE_KPROBES
> + select HAVE_KRETPROBES
> select HAVE_PCI
> select HAVE_PERF_EVENTS
> select HAVE_PERF_REGS
> diff --git a/arch/riscv/include/asm/kprobes.h b/arch/riscv/include/asm/kprobes.h
> index 56a98ea3..4647d38 100644
> --- a/arch/riscv/include/asm/kprobes.h
> +++ b/arch/riscv/include/asm/kprobes.h
> @@ -11,4 +11,44 @@
>
> #include <asm-generic/kprobes.h>
>
> +#ifdef CONFIG_KPROBES
> +#include <linux/types.h>
> +#include <linux/ptrace.h>
> +#include <linux/percpu.h>
> +
> +#define __ARCH_WANT_KPROBES_INSN_SLOT
> +#define MAX_INSN_SIZE 2
> +
> +#define flush_insn_slot(p) do { } while (0)
> +#define kretprobe_blacklist_size 0
> +
> +#include <asm/probes.h>
> +
> +struct prev_kprobe {
> + struct kprobe *kp;
> + unsigned int status;
> +};
> +
> +/* Single step context for kprobe */
> +struct kprobe_step_ctx {
> + unsigned long ss_pending;
> + unsigned long match_addr;
> +};
> +
> +/* per-cpu kprobe control block */
> +struct kprobe_ctlblk {
> + unsigned int kprobe_status;
> + unsigned long saved_status;
> + struct prev_kprobe prev_kprobe;
> + struct kprobe_step_ctx ss_ctx;
> +};
> +
> +void arch_remove_kprobe(struct kprobe *p);
> +int kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr);
> +bool kprobe_breakpoint_handler(struct pt_regs *regs);
> +bool kprobe_single_step_handler(struct pt_regs *regs);
> +void kretprobe_trampoline(void);
> +void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
> +
> +#endif /* CONFIG_KPROBES */
> #endif /* _ASM_RISCV_KPROBES_H */
> diff --git a/arch/riscv/include/asm/probes.h b/arch/riscv/include/asm/probes.h
> new file mode 100644
> index 00000000..a787e6d
> --- /dev/null
> +++ b/arch/riscv/include/asm/probes.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef _ASM_RISCV_PROBES_H
> +#define _ASM_RISCV_PROBES_H
> +
> +typedef u32 probe_opcode_t;
> +typedef bool (probes_handler_t) (u32 opcode, unsigned long addr, struct pt_regs *);
> +
> +/* architecture specific copy of original instruction */
> +struct arch_probe_insn {
> + probe_opcode_t *insn;
> + probes_handler_t *handler;
> + /* restore address after simulation */
> + unsigned long restore;
> +};
> +
> +#ifdef CONFIG_KPROBES
> +typedef u32 kprobe_opcode_t;
> +struct arch_specific_insn {
> + struct arch_probe_insn api;
> +};
> +#endif
> +
> +#endif /* _ASM_RISCV_PROBES_H */
> diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> index f14aed2..80958cb 100644
> --- a/arch/riscv/kernel/Makefile
> +++ b/arch/riscv/kernel/Makefile
> @@ -29,6 +29,7 @@ obj-y += riscv_ksyms.o
> obj-y += stacktrace.o
> obj-y += cacheinfo.o
> obj-y += patch.o
> +obj-y += probes/
> obj-$(CONFIG_MMU) += vdso.o vdso/
>
> obj-$(CONFIG_RISCV_M_MODE) += traps_misaligned.o
> diff --git a/arch/riscv/kernel/probes/Makefile b/arch/riscv/kernel/probes/Makefile
> new file mode 100644
> index 00000000..8a39507
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o simulate-insn.o
> +obj-$(CONFIG_KPROBES) += kprobes_trampoline.o
> +CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
> diff --git a/arch/riscv/kernel/probes/decode-insn.c b/arch/riscv/kernel/probes/decode-insn.c
> new file mode 100644
> index 00000000..0876c30
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/decode-insn.c
> @@ -0,0 +1,48 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +#include <linux/module.h>
> +#include <linux/kallsyms.h>
> +#include <asm/sections.h>
> +
> +#include "decode-insn.h"
> +#include "simulate-insn.h"
> +
> +/* Return:
> + * INSN_REJECTED If instruction is one not allowed to kprobe,
> + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
> + */
> +enum probe_insn __kprobes
> +riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *api)
> +{
> + probe_opcode_t insn = le32_to_cpu(*addr);
> +
> + /*
> + * Reject instructions list:
> + */
> + RISCV_INSN_REJECTED(system, insn);
> + RISCV_INSN_REJECTED(fence, insn);
> +
> + /*
> + * Simulate instructions list:
> + * TODO: the REJECTED ones below need to be implemented
> + */
> +#ifdef CONFIG_RISCV_ISA_C
> + RISCV_INSN_REJECTED(c_j, insn);
> + RISCV_INSN_REJECTED(c_jr, insn);
> + RISCV_INSN_REJECTED(c_jal, insn);
> + RISCV_INSN_REJECTED(c_jalr, insn);
> + RISCV_INSN_REJECTED(c_beqz, insn);
> + RISCV_INSN_REJECTED(c_bnez, insn);
> + RISCV_INSN_REJECTED(c_ebreak, insn);
> +#endif
> +
> + RISCV_INSN_REJECTED(auipc, insn);
> + RISCV_INSN_REJECTED(branch, insn);
> +
> + RISCV_INSN_SET_SIMULATE(jal, insn);
> + RISCV_INSN_SET_SIMULATE(jalr, insn);
> +
> + return INSN_GOOD;
> +}
> diff --git a/arch/riscv/kernel/probes/decode-insn.h b/arch/riscv/kernel/probes/decode-insn.h
> new file mode 100644
> index 00000000..42269a7
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/decode-insn.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#ifndef _RISCV_KERNEL_KPROBES_DECODE_INSN_H
> +#define _RISCV_KERNEL_KPROBES_DECODE_INSN_H
> +
> +#include <asm/sections.h>
> +#include <asm/kprobes.h>
> +
> +enum probe_insn {
> + INSN_REJECTED,
> + INSN_GOOD_NO_SLOT,
> + INSN_GOOD,
> +};
> +
> +enum probe_insn __kprobes
> +riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *asi);
> +
> +#endif /* _RISCV_KERNEL_KPROBES_DECODE_INSN_H */
> diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c
> new file mode 100644
> index 00000000..e60893b
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/kprobes.c
> @@ -0,0 +1,398 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <linux/kprobes.h>
> +#include <linux/extable.h>
> +#include <linux/slab.h>
> +#include <linux/stop_machine.h>
> +#include <asm/ptrace.h>
> +#include <linux/uaccess.h>
> +#include <asm/sections.h>
> +#include <asm/cacheflush.h>
> +#include <asm/bug.h>
> +#include <asm/patch.h>
> +
> +#include "decode-insn.h"
> +
> +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
> +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
> +
> +static void __kprobes
> +post_kprobe_handler(struct kprobe_ctlblk *, struct pt_regs *);
> +
> +static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
> +{
> + unsigned long offset = GET_INSN_LENGTH(p->opcode);
> +
> + p->ainsn.api.restore = (unsigned long)p->addr + offset;
> +
> + patch_text(p->ainsn.api.insn, p->opcode);
> + patch_text((void *)((unsigned long)(p->ainsn.api.insn) + offset),
> + __BUG_INSN_32);
> +}
> +
> +static void __kprobes arch_prepare_simulate(struct kprobe *p)
> +{
> + p->ainsn.api.restore = 0;
> +}
> +
> +static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
> +{
> + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
> +
> + if (p->ainsn.api.handler)
> + p->ainsn.api.handler((u32)p->opcode,
> + (unsigned long)p->addr, regs);
> +
> + post_kprobe_handler(kcb, regs);
> +}
> +
> +int __kprobes arch_prepare_kprobe(struct kprobe *p)
> +{
> + unsigned long probe_addr = (unsigned long)p->addr;
> +
> + if (probe_addr & 0x1) {
> + pr_warn("Address not aligned.\n");
> +
> + return -EINVAL;
> + }
> +
> + /* copy instruction */
> + p->opcode = le32_to_cpu(*p->addr);
> +
> + /* decode instruction */
> + switch (riscv_probe_decode_insn(p->addr, &p->ainsn.api)) {
> + case INSN_REJECTED: /* insn not supported */
> + return -EINVAL;
> +
> + case INSN_GOOD_NO_SLOT: /* insn need simulation */
> + p->ainsn.api.insn = NULL;
> + break;
> +
> + case INSN_GOOD: /* instruction uses slot */
> + p->ainsn.api.insn = get_insn_slot();
> + if (!p->ainsn.api.insn)
> + return -ENOMEM;
> + break;
> + }
> +
> + /* prepare the instruction */
> + if (p->ainsn.api.insn)
> + arch_prepare_ss_slot(p);
> + else
> + arch_prepare_simulate(p);
> +
> + return 0;
> +}
> +
> +/* install breakpoint in text */
> +void __kprobes arch_arm_kprobe(struct kprobe *p)
> +{
> + if ((p->opcode & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
> + patch_text(p->addr, __BUG_INSN_32);
> + else
> + patch_text(p->addr, __BUG_INSN_16);
> +}
> +
> +/* remove breakpoint from text */
> +void __kprobes arch_disarm_kprobe(struct kprobe *p)
> +{
> + patch_text(p->addr, p->opcode);
> +}
> +
> +void __kprobes arch_remove_kprobe(struct kprobe *p)
> +{
> +}
> +
> +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
> +{
> + kcb->prev_kprobe.kp = kprobe_running();
> + kcb->prev_kprobe.status = kcb->kprobe_status;
> +}
> +
> +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
> +{
> + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
> + kcb->kprobe_status = kcb->prev_kprobe.status;
> +}
> +
> +static void __kprobes set_current_kprobe(struct kprobe *p)
> +{
> + __this_cpu_write(current_kprobe, p);
> +}
> +
> +/*
> + * Interrupts need to be disabled before single-step mode is set, and not
> + * reenabled until after single-step mode ends.
> + * Without disabling interrupt on local CPU, there is a chance of
> + * interrupt occurrence in the period of exception return and start of
> + * out-of-line single-step, that result in wrongly single stepping
> + * into the interrupt handler.
> + */
> +static void __kprobes kprobes_save_local_irqflag(struct kprobe_ctlblk *kcb,
> + struct pt_regs *regs)
> +{
> + kcb->saved_status = regs->status;
> + regs->status &= ~SR_SPIE;
> +}
> +
> +static void __kprobes kprobes_restore_local_irqflag(struct kprobe_ctlblk *kcb,
> + struct pt_regs *regs)
> +{
> + regs->status = kcb->saved_status;
> +}
> +
> +static void __kprobes
> +set_ss_context(struct kprobe_ctlblk *kcb, unsigned long addr, struct kprobe *p)
> +{
> + unsigned long offset = GET_INSN_LENGTH(p->opcode);
> +
> + kcb->ss_ctx.ss_pending = true;
> + kcb->ss_ctx.match_addr = addr + offset;
> +}
> +
> +static void __kprobes clear_ss_context(struct kprobe_ctlblk *kcb)
> +{
> + kcb->ss_ctx.ss_pending = false;
> + kcb->ss_ctx.match_addr = 0;
> +}
> +
> +static void __kprobes setup_singlestep(struct kprobe *p,
> + struct pt_regs *regs,
> + struct kprobe_ctlblk *kcb, int reenter)
> +{
> + unsigned long slot;
> +
> + if (reenter) {
> + save_previous_kprobe(kcb);
> + set_current_kprobe(p);
> + kcb->kprobe_status = KPROBE_REENTER;
> + } else {
> + kcb->kprobe_status = KPROBE_HIT_SS;
> + }
> +
> + if (p->ainsn.api.insn) {
> + /* prepare for single stepping */
> + slot = (unsigned long)p->ainsn.api.insn;
> +
> + set_ss_context(kcb, slot, p); /* mark pending ss */
> +
> + /* IRQs and single stepping do not mix well. */
> + kprobes_save_local_irqflag(kcb, regs);
> +
> + instruction_pointer_set(regs, slot);
> + } else {
> + /* insn simulation */
> + arch_simulate_insn(p, regs);
> + }
> +}
> +
> +static int __kprobes reenter_kprobe(struct kprobe *p,
> + struct pt_regs *regs,
> + struct kprobe_ctlblk *kcb)
> +{
> + switch (kcb->kprobe_status) {
> + case KPROBE_HIT_SSDONE:
> + case KPROBE_HIT_ACTIVE:
> + kprobes_inc_nmissed_count(p);
> + setup_singlestep(p, regs, kcb, 1);
> + break;
> + case KPROBE_HIT_SS:
> + case KPROBE_REENTER:
> + pr_warn("Unrecoverable kprobe detected.\n");
> + dump_kprobe(p);
> + BUG();
> + break;
> + default:
> + WARN_ON(1);
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +static void __kprobes
> +post_kprobe_handler(struct kprobe_ctlblk *kcb, struct pt_regs *regs)
> +{
> + struct kprobe *cur = kprobe_running();
> +
> + if (!cur)
> + return;
> +
> + /* return addr restore if non-branching insn */
> + if (cur->ainsn.api.restore != 0)
> + regs->epc = cur->ainsn.api.restore;
> +
> + /* restore back original saved kprobe variables and continue */
> + if (kcb->kprobe_status == KPROBE_REENTER) {
> + restore_previous_kprobe(kcb);
> + return;
> + }
> +
> + /* call post handler */
> + kcb->kprobe_status = KPROBE_HIT_SSDONE;
> + if (cur->post_handler) {
> + /* post_handler can hit breakpoint and single step
> + * again, so we enable D-flag for recursive exception.
> + */
> + cur->post_handler(cur, regs, 0);
> + }
> +
> + reset_current_kprobe();
> +}
> +
> +int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr)
> +{
> + struct kprobe *cur = kprobe_running();
> + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
> +
> + switch (kcb->kprobe_status) {
> + case KPROBE_HIT_SS:
> + case KPROBE_REENTER:
> + /*
> + * We are here because the instruction being single
> + * stepped caused a page fault. We reset the current
> + * kprobe and the ip points back to the probe address
> + * and allow the page fault handler to continue as a
> + * normal page fault.
> + */
> + regs->epc = (unsigned long) cur->addr;
> + if (!instruction_pointer(regs))
> + BUG();
> +
> + if (kcb->kprobe_status == KPROBE_REENTER)
> + restore_previous_kprobe(kcb);
> + else
> + reset_current_kprobe();
> +
> + break;
> + case KPROBE_HIT_ACTIVE:
> + case KPROBE_HIT_SSDONE:
> + /*
> + * We increment the nmissed count for accounting,
> + * we can also use npre/npostfault count for accounting
> + * these specific fault cases.
> + */
> + kprobes_inc_nmissed_count(cur);
> +
> + /*
> + * We come here because instructions in the pre/post
> + * handler caused the page_fault, this could happen
> + * if handler tries to access user space by
> + * copy_from_user(), get_user() etc. Let the
> + * user-specified handler try to fix it first.
> + */
> + if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
> + return 1;
> +
> + /*
> + * In case the user-specified fault handler returned
> + * zero, try to fix up.
> + */
> + if (fixup_exception(regs))
> + return 1;
> + }
> + return 0;
> +}
> +
> +bool __kprobes
> +kprobe_breakpoint_handler(struct pt_regs *regs)
> +{
> + struct kprobe *p, *cur_kprobe;
> + struct kprobe_ctlblk *kcb;
> + unsigned long addr = instruction_pointer(regs);
> +
> + kcb = get_kprobe_ctlblk();
> + cur_kprobe = kprobe_running();
> +
> + p = get_kprobe((kprobe_opcode_t *) addr);
> +
> + if (p) {
> + if (cur_kprobe) {
> + if (reenter_kprobe(p, regs, kcb))
> + return true;
> + } else {
> + /* Probe hit */
> + set_current_kprobe(p);
> + kcb->kprobe_status = KPROBE_HIT_ACTIVE;
> +
> + /*
> + * If we have no pre-handler or it returned 0, we
> + * continue with normal processing. If we have a
> + * pre-handler and it returned non-zero, it will
> + * modify the execution path and no need to single
> + * stepping. Let's just reset current kprobe and exit.
> + *
> + * pre_handler can hit a breakpoint and can step thru
> + * before return.
> + */
> + if (!p->pre_handler || !p->pre_handler(p, regs))
> + setup_singlestep(p, regs, kcb, 0);
> + else
> + reset_current_kprobe();
> + }
> + return true;
> + }
> +
> + /*
> + * The breakpoint instruction was removed right
> + * after we hit it. Another cpu has removed
> + * either a probepoint or a debugger breakpoint
> + * at this address. In either case, no further
> + * handling of this interrupt is appropriate.
> + * Return back to original instruction, and continue.
> + */
> + return false;
> +}
> +
> +bool __kprobes
> +kprobe_single_step_handler(struct pt_regs *regs)
> +{
> + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
> +
> + if ((kcb->ss_ctx.ss_pending)
> + && (kcb->ss_ctx.match_addr == instruction_pointer(regs))) {
> + clear_ss_context(kcb); /* clear pending ss */
> +
> + kprobes_restore_local_irqflag(kcb, regs);
> +
> + post_kprobe_handler(kcb, regs);
> + return true;
> + }
> + return false;
> +}
> +
> +/*
> + * Provide a blacklist of symbols identifying ranges which cannot be kprobed.
> + * This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
> + */
> +int __init arch_populate_kprobe_blacklist(void)
> +{
> + int ret;
> +
> + ret = kprobe_add_area_blacklist((unsigned long)__irqentry_text_start,
> + (unsigned long)__irqentry_text_end);
> + return ret;
> +}
> +
> +void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs)
> +{
> + return (void *)kretprobe_trampoline_handler(regs, &kretprobe_trampoline, NULL);
> +}
> +
> +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
> + struct pt_regs *regs)
> +{
> + ri->ret_addr = (kprobe_opcode_t *)regs->ra;
> + ri->fp = NULL;
> + regs->ra = (unsigned long) &kretprobe_trampoline;
> +}
> +
> +int __kprobes arch_trampoline_kprobe(struct kprobe *p)
> +{
> + return 0;
> +}
> +
> +int __init arch_init_kprobes(void)
> +{
> + return 0;
> +}
> diff --git a/arch/riscv/kernel/probes/kprobes_trampoline.S b/arch/riscv/kernel/probes/kprobes_trampoline.S
> new file mode 100644
> index 00000000..6e85d02
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/kprobes_trampoline.S
> @@ -0,0 +1,93 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Author: Patrick Stählin <[email protected]>
> + */
> +#include <linux/linkage.h>
> +
> +#include <asm/asm.h>
> +#include <asm/asm-offsets.h>
> +
> + .text
> + .altmacro
> +
> + .macro save_all_base_regs
> + REG_S x1, PT_RA(sp)
> + REG_S x3, PT_GP(sp)
> + REG_S x4, PT_TP(sp)
> + REG_S x5, PT_T0(sp)
> + REG_S x6, PT_T1(sp)
> + REG_S x7, PT_T2(sp)
> + REG_S x8, PT_S0(sp)
> + REG_S x9, PT_S1(sp)
> + REG_S x10, PT_A0(sp)
> + REG_S x11, PT_A1(sp)
> + REG_S x12, PT_A2(sp)
> + REG_S x13, PT_A3(sp)
> + REG_S x14, PT_A4(sp)
> + REG_S x15, PT_A5(sp)
> + REG_S x16, PT_A6(sp)
> + REG_S x17, PT_A7(sp)
> + REG_S x18, PT_S2(sp)
> + REG_S x19, PT_S3(sp)
> + REG_S x20, PT_S4(sp)
> + REG_S x21, PT_S5(sp)
> + REG_S x22, PT_S6(sp)
> + REG_S x23, PT_S7(sp)
> + REG_S x24, PT_S8(sp)
> + REG_S x25, PT_S9(sp)
> + REG_S x26, PT_S10(sp)
> + REG_S x27, PT_S11(sp)
> + REG_S x28, PT_T3(sp)
> + REG_S x29, PT_T4(sp)
> + REG_S x30, PT_T5(sp)
> + REG_S x31, PT_T6(sp)
> + .endm
> +
> + .macro restore_all_base_regs
> + REG_L x3, PT_GP(sp)
> + REG_L x4, PT_TP(sp)
> + REG_L x5, PT_T0(sp)
> + REG_L x6, PT_T1(sp)
> + REG_L x7, PT_T2(sp)
> + REG_L x8, PT_S0(sp)
> + REG_L x9, PT_S1(sp)
> + REG_L x10, PT_A0(sp)
> + REG_L x11, PT_A1(sp)
> + REG_L x12, PT_A2(sp)
> + REG_L x13, PT_A3(sp)
> + REG_L x14, PT_A4(sp)
> + REG_L x15, PT_A5(sp)
> + REG_L x16, PT_A6(sp)
> + REG_L x17, PT_A7(sp)
> + REG_L x18, PT_S2(sp)
> + REG_L x19, PT_S3(sp)
> + REG_L x20, PT_S4(sp)
> + REG_L x21, PT_S5(sp)
> + REG_L x22, PT_S6(sp)
> + REG_L x23, PT_S7(sp)
> + REG_L x24, PT_S8(sp)
> + REG_L x25, PT_S9(sp)
> + REG_L x26, PT_S10(sp)
> + REG_L x27, PT_S11(sp)
> + REG_L x28, PT_T3(sp)
> + REG_L x29, PT_T4(sp)
> + REG_L x30, PT_T5(sp)
> + REG_L x31, PT_T6(sp)
> + .endm
> +
> +ENTRY(kretprobe_trampoline)
> + addi sp, sp, -(PT_SIZE_ON_STACK)
> + save_all_base_regs
> +
> + move a0, sp /* pt_regs */
> +
> + call trampoline_probe_handler
> +
> + /* use the result as the return-address */
> + move ra, a0
> +
> + restore_all_base_regs
> + addi sp, sp, PT_SIZE_ON_STACK
> +
> + ret
> +ENDPROC(kretprobe_trampoline)
> diff --git a/arch/riscv/kernel/probes/simulate-insn.c b/arch/riscv/kernel/probes/simulate-insn.c
> new file mode 100644
> index 00000000..2519ce2
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/simulate-insn.c
> @@ -0,0 +1,85 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +#include <linux/bitops.h>
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "decode-insn.h"
> +#include "simulate-insn.h"
> +
> +static inline bool rv_insn_reg_get_val(struct pt_regs *regs, u32 index,
> + unsigned long *ptr)
> +{
> + if (index == 0)
> + *ptr = 0;
> + else if (index <= 31)
> + *ptr = *((unsigned long *)regs + index);
> + else
> + return false;
> +
> + return true;
> +}
> +
> +static inline bool rv_insn_reg_set_val(struct pt_regs *regs, u32 index,
> + unsigned long val)
> +{
> + if (index == 0)
> + return false;
> + else if (index <= 31)
> + *((unsigned long *)regs + index) = val;
> + else
> + return false;
> +
> + return true;
> +}
> +
> +bool __kprobes simulate_jal(u32 opcode, unsigned long addr, struct pt_regs *regs)
> +{
> + /*
> + * 31 30 21 20 19 12 11 7 6 0
> + * imm [20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode
> + * 1 10 1 8 5 JAL/J
> + */
> + bool ret;
> + u32 imm;
> + u32 index = (opcode >> 7) & 0x1f;
> +
> + ret = rv_insn_reg_set_val(regs, index, addr + 4);
> + if (!ret)
> + return ret;
> +
> + imm = ((opcode >> 21) & 0x3ff) << 1;
> + imm |= ((opcode >> 20) & 0x1) << 11;
> + imm |= ((opcode >> 12) & 0xff) << 12;
> + imm |= ((opcode >> 31) & 0x1) << 20;
> +
> + instruction_pointer_set(regs, addr + sign_extend32((imm), 20));
> +
> + return ret;
> +}
> +
> +bool __kprobes simulate_jalr(u32 opcode, unsigned long addr, struct pt_regs *regs)
> +{
> + /*
> + * 31 20 19 15 14 12 11 7 6 0
> + * offset[11:0] | rs1 | 010 | rd | opcode
> + * 12 5 3 5 JALR/JR
> + */
> + bool ret;
> + unsigned long base_addr;
> + u32 imm = (opcode >> 20) & 0xfff;
> + u32 rd_index = (opcode >> 7) & 0x1f;
> + u32 rs1_index = (opcode >> 15) & 0x1f;
> +
> + ret = rv_insn_reg_set_val(regs, rd_index, addr + 4);
> + if (!ret)
> + return ret;
> +
> + ret = rv_insn_reg_get_val(regs, rs1_index, &base_addr);
> + if (!ret)
> + return ret;
> +
> + instruction_pointer_set(regs, (base_addr + sign_extend32((imm), 11))&~1);
> +
> + return ret;
> +}
> diff --git a/arch/riscv/kernel/probes/simulate-insn.h b/arch/riscv/kernel/probes/simulate-insn.h
> new file mode 100644
> index 00000000..a62d784
> --- /dev/null
> +++ b/arch/riscv/kernel/probes/simulate-insn.h
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#ifndef _RISCV_KERNEL_PROBES_SIMULATE_INSN_H
> +#define _RISCV_KERNEL_PROBES_SIMULATE_INSN_H
> +
> +#define __RISCV_INSN_FUNCS(name, mask, val) \
> +static __always_inline bool riscv_insn_is_##name(probe_opcode_t code) \
> +{ \
> + BUILD_BUG_ON(~(mask) & (val)); \
> + return (code & (mask)) == (val); \
> +} \
> +bool simulate_##name(u32 opcode, unsigned long addr, \
> + struct pt_regs *regs);
> +
> +#define RISCV_INSN_REJECTED(name, code) \
> + do { \
> + if (riscv_insn_is_##name(code)) { \
> + return INSN_REJECTED; \
> + } \
> + } while (0)
> +
> +__RISCV_INSN_FUNCS(system, 0x7f, 0x73)
> +__RISCV_INSN_FUNCS(fence, 0x7f, 0x0f)
> +
> +#define RISCV_INSN_SET_SIMULATE(name, code) \
> + do { \
> + if (riscv_insn_is_##name(code)) { \
> + api->handler = simulate_##name; \
> + return INSN_GOOD_NO_SLOT; \
> + } \
> + } while (0)
> +
> +__RISCV_INSN_FUNCS(c_j, 0xe003, 0xa001)
> +__RISCV_INSN_FUNCS(c_jr, 0xf007, 0x8002)
> +__RISCV_INSN_FUNCS(c_jal, 0xe003, 0x2001)
> +__RISCV_INSN_FUNCS(c_jalr, 0xf007, 0x9002)
> +__RISCV_INSN_FUNCS(c_beqz, 0xe003, 0xc001)
> +__RISCV_INSN_FUNCS(c_bnez, 0xe003, 0xe001)
> +__RISCV_INSN_FUNCS(c_ebreak, 0xffff, 0x9002)
> +
> +__RISCV_INSN_FUNCS(auipc, 0x7f, 0x17)
> +__RISCV_INSN_FUNCS(branch, 0x7f, 0x63)
> +
> +__RISCV_INSN_FUNCS(jal, 0x7f, 0x6f)
> +__RISCV_INSN_FUNCS(jalr, 0x707f, 0x67)
> +
> +#endif /* _RISCV_KERNEL_PROBES_SIMULATE_INSN_H */
> diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
> index ad14f44..19a788a 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -12,6 +12,7 @@
> #include <linux/signal.h>
> #include <linux/kdebug.h>
> #include <linux/uaccess.h>
> +#include <linux/kprobes.h>
> #include <linux/mm.h>
> #include <linux/module.h>
> #include <linux/irq.h>
> @@ -145,6 +146,14 @@ static inline unsigned long get_break_insn_length(unsigned long pc)
>
> asmlinkage __visible void do_trap_break(struct pt_regs *regs)
> {
> +#ifdef CONFIG_KPROBES
> + if (kprobe_single_step_handler(regs))
> + return;
> +
> + if (kprobe_breakpoint_handler(regs))
> + return;
> +#endif
> +
> if (user_mode(regs))
> force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
> #ifdef CONFIG_KGDB
> diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c
> index 716d64e..6afd59d 100644
> --- a/arch/riscv/mm/fault.c
> +++ b/arch/riscv/mm/fault.c
> @@ -13,6 +13,7 @@
> #include <linux/perf_event.h>
> #include <linux/signal.h>
> #include <linux/uaccess.h>
> +#include <linux/kprobes.h>
>
> #include <asm/ptrace.h>
> #include <asm/tlbflush.h>
> @@ -39,6 +40,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
> tsk = current;
> mm = tsk->mm;
>
> + if (kprobe_page_fault(regs, cause))
> + return;
> +
> /*
> * Fault-in kernel-space virtual memory on-demand.
> * The 'reference' page table is init_mm.pgd.
On Sat, 17 Oct 2020 00:06:09 PDT (-0700), [email protected] wrote:
> From: Patrick Stählin <[email protected]>
>
> Needed for kprobes support. Copied and adapted from arm64 code.
>
> Guo Ren fixup pt_regs type for linux-5.8-rc1.
>
> Signed-off-by: Patrick Stählin <[email protected]>
> Signed-off-by: Guo Ren <[email protected]>
> Reviewed-by: Pekka Enberg <[email protected]>
> Reviewed-by: Zong Li <[email protected]>
> Reviewed-by: Masami Hiramatsu <[email protected]>
> ---
> arch/riscv/Kconfig | 1 +
> arch/riscv/include/asm/ptrace.h | 29 ++++++++++++
> arch/riscv/kernel/ptrace.c | 99 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 129 insertions(+)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index b7821ac..e6424d8b 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -87,6 +87,7 @@ config RISCV
> select SPARSE_IRQ
> select SYSCTL_EXCEPTION_TRACE
> select THREAD_INFO_IN_TASK
> + select HAVE_REGS_AND_STACK_ACCESS_API
We alphabetize these now -- I'd usually just fix it, but there's a
Signed-off-by issue later in the patch set.
>
> config ARCH_MMAP_RND_BITS_MIN
> default 18 if 64BIT
> diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
> index ee49f80..23372bb 100644
> --- a/arch/riscv/include/asm/ptrace.h
> +++ b/arch/riscv/include/asm/ptrace.h
> @@ -8,6 +8,7 @@
>
> #include <uapi/asm/ptrace.h>
> #include <asm/csr.h>
> +#include <linux/compiler.h>
>
> #ifndef __ASSEMBLY__
>
> @@ -60,6 +61,7 @@ struct pt_regs {
>
> #define user_mode(regs) (((regs)->status & SR_PP) == 0)
>
> +#define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0)
>
> /* Helpers for working with the instruction pointer */
> static inline unsigned long instruction_pointer(struct pt_regs *regs)
> @@ -85,6 +87,12 @@ static inline void user_stack_pointer_set(struct pt_regs *regs,
> regs->sp = val;
> }
>
> +/* Valid only for Kernel mode traps. */
> +static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
> +{
> + return regs->sp;
> +}
> +
> /* Helpers for working with the frame pointer */
> static inline unsigned long frame_pointer(struct pt_regs *regs)
> {
> @@ -101,6 +109,27 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
> return regs->a0;
> }
>
> +extern int regs_query_register_offset(const char *name);
> +extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
> + unsigned int n);
> +
> +/**
> + * regs_get_register() - get register value from its offset
> + * @regs: pt_regs from which register value is gotten
> + * @offset: offset of the register.
> + *
> + * regs_get_register returns the value of a register whose offset from @regs.
> + * The @offset is the offset of the register in struct pt_regs.
> + * If @offset is bigger than MAX_REG_OFFSET, this returns 0.
> + */
> +static inline unsigned long regs_get_register(struct pt_regs *regs,
> + unsigned int offset)
> +{
> + if (unlikely(offset > MAX_REG_OFFSET))
> + return 0;
> +
> + return *(unsigned long *)((unsigned long)regs + offset);
> +}
> #endif /* __ASSEMBLY__ */
>
> #endif /* _ASM_RISCV_PTRACE_H */
> diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
> index 2d6395f..1a85305 100644
> --- a/arch/riscv/kernel/ptrace.c
> +++ b/arch/riscv/kernel/ptrace.c
> @@ -114,6 +114,105 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
> return &riscv_user_native_view;
> }
>
> +struct pt_regs_offset {
> + const char *name;
> + int offset;
> +};
> +
> +#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
> +#define REG_OFFSET_END {.name = NULL, .offset = 0}
> +
> +static const struct pt_regs_offset regoffset_table[] = {
> + REG_OFFSET_NAME(epc),
> + REG_OFFSET_NAME(ra),
> + REG_OFFSET_NAME(sp),
> + REG_OFFSET_NAME(gp),
> + REG_OFFSET_NAME(tp),
> + REG_OFFSET_NAME(t0),
> + REG_OFFSET_NAME(t1),
> + REG_OFFSET_NAME(t2),
> + REG_OFFSET_NAME(s0),
> + REG_OFFSET_NAME(s1),
> + REG_OFFSET_NAME(a0),
> + REG_OFFSET_NAME(a1),
> + REG_OFFSET_NAME(a2),
> + REG_OFFSET_NAME(a3),
> + REG_OFFSET_NAME(a4),
> + REG_OFFSET_NAME(a5),
> + REG_OFFSET_NAME(a6),
> + REG_OFFSET_NAME(a7),
> + REG_OFFSET_NAME(s2),
> + REG_OFFSET_NAME(s3),
> + REG_OFFSET_NAME(s4),
> + REG_OFFSET_NAME(s5),
> + REG_OFFSET_NAME(s6),
> + REG_OFFSET_NAME(s7),
> + REG_OFFSET_NAME(s8),
> + REG_OFFSET_NAME(s9),
> + REG_OFFSET_NAME(s10),
> + REG_OFFSET_NAME(s11),
> + REG_OFFSET_NAME(t3),
> + REG_OFFSET_NAME(t4),
> + REG_OFFSET_NAME(t5),
> + REG_OFFSET_NAME(t6),
> + REG_OFFSET_NAME(status),
> + REG_OFFSET_NAME(badaddr),
> + REG_OFFSET_NAME(cause),
> + REG_OFFSET_NAME(orig_a0),
> + REG_OFFSET_END,
> +};
> +
> +/**
> + * regs_query_register_offset() - query register offset from its name
> + * @name: the name of a register
> + *
> + * regs_query_register_offset() returns the offset of a register in struct
> + * pt_regs from its name. If the name is invalid, this returns -EINVAL;
> + */
> +int regs_query_register_offset(const char *name)
> +{
> + const struct pt_regs_offset *roff;
> +
> + for (roff = regoffset_table; roff->name != NULL; roff++)
> + if (!strcmp(roff->name, name))
> + return roff->offset;
> + return -EINVAL;
> +}
> +
> +/**
> + * regs_within_kernel_stack() - check the address in the stack
> + * @regs: pt_regs which contains kernel stack pointer.
> + * @addr: address which is checked.
> + *
> + * regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
> + * If @addr is within the kernel stack, it returns true. If not, returns false.
> + */
> +static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
> +{
> + return (addr & ~(THREAD_SIZE - 1)) ==
> + (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1));
> +}
> +
> +/**
> + * regs_get_kernel_stack_nth() - get Nth entry of the stack
> + * @regs: pt_regs which contains kernel stack pointer.
> + * @n: stack entry number.
> + *
> + * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
> + * is specified by @regs. If the @n th entry is NOT in the kernel stack,
> + * this returns 0.
> + */
> +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
> +{
> + unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
> +
> + addr += n;
> + if (regs_within_kernel_stack(regs, (unsigned long)addr))
> + return *addr;
> + else
> + return 0;
> +}
> +
> void ptrace_disable(struct task_struct *child)
> {
> clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
On Fri, Nov 6, 2020 at 9:03 AM Palmer Dabbelt <[email protected]> wrote:
>
> On Sat, 17 Oct 2020 00:06:09 PDT (-0700), [email protected] wrote:
> > From: Patrick Stählin <[email protected]>
> >
> > Needed for kprobes support. Copied and adapted from arm64 code.
> >
> > Guo Ren fixup pt_regs type for linux-5.8-rc1.
> >
> > Signed-off-by: Patrick Stählin <[email protected]>
> > Signed-off-by: Guo Ren <[email protected]>
> > Reviewed-by: Pekka Enberg <[email protected]>
> > Reviewed-by: Zong Li <[email protected]>
> > Reviewed-by: Masami Hiramatsu <[email protected]>
> > ---
> > arch/riscv/Kconfig | 1 +
> > arch/riscv/include/asm/ptrace.h | 29 ++++++++++++
> > arch/riscv/kernel/ptrace.c | 99 +++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 129 insertions(+)
> >
> > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> > index b7821ac..e6424d8b 100644
> > --- a/arch/riscv/Kconfig
> > +++ b/arch/riscv/Kconfig
> > @@ -87,6 +87,7 @@ config RISCV
> > select SPARSE_IRQ
> > select SYSCTL_EXCEPTION_TRACE
> > select THREAD_INFO_IN_TASK
> > + select HAVE_REGS_AND_STACK_ACCESS_API
>
> We alphabetize these now -- I'd usually just fix it, but there's a
> Signed-off-by issue later in the patch set.
ok, I'll fix it and rebase 5.10-rc2.
--
Best Regards
Guo Ren
ML: https://lore.kernel.org/linux-csky/
On Fri, Nov 6, 2020 at 9:02 AM Palmer Dabbelt <[email protected]> wrote:
>
> On Sat, 17 Oct 2020 00:06:13 PDT (-0700), [email protected] wrote:
> > From: Guo Ren <[email protected]>
> >
> > This patch enables "kprobe & kretprobe" to work with ftrace
> > interface. It utilized software breakpoint as single-step
> > mechanism.
> >
> > Some instructions which can't be single-step executed must be
> > simulated in kernel execution slot, such as: branch, jal, auipc,
> > la ...
> >
> > Some instructions should be rejected for probing and we use a
> > blacklist to filter, such as: ecall, ebreak, ...
> >
> > We use ebreak & c.ebreak to replace origin instruction and the
> > kprobe handler prepares an executable memory slot for out-of-line
> > execution with a copy of the original instruction being probed.
> > In execution slot we add ebreak behind original instruction to
> > simulate a single-setp mechanism.
> >
> > The patch is based on packi's work [1] and csky's work [2].
> > - The kprobes_trampoline.S is all from packi's patch
> > - The single-step mechanism is new designed for riscv without hw
> > single-step trap
> > - The simulation codes are from csky
> > - Frankly, all codes refer to other archs' implementation
> >
> > [1] https://lore.kernel.org/linux-riscv/[email protected]/
> > [2] https://lore.kernel.org/linux-csky/[email protected]/
> >
> > Signed-off-by: Guo Ren <[email protected]>
> > Co-Developed-by: Patrick Stählin <[email protected]>
>
> Checkpatch says
>
> WARNING: 'Co-developed-by:' is the preferred signature form
> #108:
> Co-Developed-by: Patrick Stählin <[email protected]>
>
> WARNING: Co-developed-by: must be immediately followed by Signed-off-by:
> #108:
> Co-Developed-by: Patrick Stählin <[email protected]>
> Acked-by: Masami Hiramatsu <[email protected]>
>
> There's some other checkpatch warnings throughout the patch set, but this is
> one I'm not supposed to fix up. Can one of you re-post the patches with the
> correct DCO?
Ok, I'll fix it up in V5 and rebase 5.10-rc2
--
Best Regards
Guo Ren
ML: https://lore.kernel.org/linux-csky/