Skip to content

Commit 7478408

Browse files
guoren83palmer-dabbelt
authored andcommitted
riscv: Add uprobes supported
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]> Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 829adda commit 7478408

File tree

9 files changed

+253
-1
lines changed

9 files changed

+253
-1
lines changed

arch/riscv/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ config ARCH_SELECT_MEMORY_MODEL
160160
config ARCH_WANT_GENERAL_HUGETLB
161161
def_bool y
162162

163+
config ARCH_SUPPORTS_UPROBES
164+
def_bool y
165+
163166
config SYS_SUPPORTS_HUGETLBFS
164167
depends on MMU
165168
def_bool y

arch/riscv/include/asm/processor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct thread_struct {
3434
unsigned long sp; /* Kernel mode stack */
3535
unsigned long s[12]; /* s[0]: frame pointer */
3636
struct __riscv_d_ext_state fstate;
37+
unsigned long bad_cause;
3738
};
3839

3940
#define INIT_THREAD { \

arch/riscv/include/asm/thread_info.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct thread_info {
7575
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing */
7676
#define TIF_SECCOMP 8 /* syscall secure computing */
7777
#define TIF_NOTIFY_SIGNAL 9 /* signal notifications exist */
78+
#define TIF_UPROBE 10 /* uprobe breakpoint or singlestep */
7879

7980
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
8081
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
@@ -84,10 +85,11 @@ struct thread_info {
8485
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
8586
#define _TIF_SECCOMP (1 << TIF_SECCOMP)
8687
#define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL)
88+
#define _TIF_UPROBE (1 << TIF_UPROBE)
8789

8890
#define _TIF_WORK_MASK \
8991
(_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \
90-
_TIF_NOTIFY_SIGNAL)
92+
_TIF_NOTIFY_SIGNAL | _TIF_UPROBE)
9193

9294
#define _TIF_SYSCALL_WORK \
9395
(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT | \

arch/riscv/include/asm/uprobes.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
#ifndef _ASM_RISCV_UPROBES_H
4+
#define _ASM_RISCV_UPROBES_H
5+
6+
#include <asm/probes.h>
7+
#include <asm/patch.h>
8+
#include <asm/bug.h>
9+
10+
#define MAX_UINSN_BYTES 8
11+
12+
#ifdef CONFIG_RISCV_ISA_C
13+
#define UPROBE_SWBP_INSN __BUG_INSN_16
14+
#define UPROBE_SWBP_INSN_SIZE 2
15+
#else
16+
#define UPROBE_SWBP_INSN __BUG_INSN_32
17+
#define UPROBE_SWBP_INSN_SIZE 4
18+
#endif
19+
#define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES
20+
21+
typedef u32 uprobe_opcode_t;
22+
23+
struct arch_uprobe_task {
24+
unsigned long saved_cause;
25+
};
26+
27+
struct arch_uprobe {
28+
union {
29+
u8 insn[MAX_UINSN_BYTES];
30+
u8 ixol[MAX_UINSN_BYTES];
31+
};
32+
struct arch_probe_insn api;
33+
unsigned long insn_size;
34+
bool simulate;
35+
};
36+
37+
bool uprobe_breakpoint_handler(struct pt_regs *regs);
38+
bool uprobe_single_step_handler(struct pt_regs *regs);
39+
40+
#endif /* _ASM_RISCV_UPROBES_H */

arch/riscv/kernel/probes/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o simulate-insn.o
33
obj-$(CONFIG_KPROBES) += kprobes_trampoline.o
44
obj-$(CONFIG_KPROBES_ON_FTRACE) += ftrace.o
5+
obj-$(CONFIG_UPROBES) += uprobes.o decode-insn.o simulate-insn.o
56
CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)

arch/riscv/kernel/probes/uprobes.c

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/highmem.h>
4+
#include <linux/ptrace.h>
5+
#include <linux/uprobes.h>
6+
7+
#include "decode-insn.h"
8+
9+
#define UPROBE_TRAP_NR UINT_MAX
10+
11+
bool is_swbp_insn(uprobe_opcode_t *insn)
12+
{
13+
#ifdef CONFIG_RISCV_ISA_C
14+
return (*insn & 0xffff) == UPROBE_SWBP_INSN;
15+
#else
16+
return *insn == UPROBE_SWBP_INSN;
17+
#endif
18+
}
19+
20+
unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
21+
{
22+
return instruction_pointer(regs);
23+
}
24+
25+
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
26+
unsigned long addr)
27+
{
28+
probe_opcode_t opcode;
29+
30+
opcode = *(probe_opcode_t *)(&auprobe->insn[0]);
31+
32+
auprobe->insn_size = GET_INSN_LENGTH(opcode);
33+
34+
switch (riscv_probe_decode_insn(&opcode, &auprobe->api)) {
35+
case INSN_REJECTED:
36+
return -EINVAL;
37+
38+
case INSN_GOOD_NO_SLOT:
39+
auprobe->simulate = true;
40+
break;
41+
42+
case INSN_GOOD:
43+
auprobe->simulate = false;
44+
break;
45+
46+
default:
47+
return -EINVAL;
48+
}
49+
50+
return 0;
51+
}
52+
53+
int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
54+
{
55+
struct uprobe_task *utask = current->utask;
56+
57+
utask->autask.saved_cause = current->thread.bad_cause;
58+
current->thread.bad_cause = UPROBE_TRAP_NR;
59+
60+
instruction_pointer_set(regs, utask->xol_vaddr);
61+
62+
regs->status &= ~SR_SPIE;
63+
64+
return 0;
65+
}
66+
67+
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
68+
{
69+
struct uprobe_task *utask = current->utask;
70+
71+
WARN_ON_ONCE(current->thread.bad_cause != UPROBE_TRAP_NR);
72+
73+
instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size);
74+
75+
regs->status |= SR_SPIE;
76+
77+
return 0;
78+
}
79+
80+
bool arch_uprobe_xol_was_trapped(struct task_struct *t)
81+
{
82+
if (t->thread.bad_cause != UPROBE_TRAP_NR)
83+
return true;
84+
85+
return false;
86+
}
87+
88+
bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
89+
{
90+
probe_opcode_t insn;
91+
unsigned long addr;
92+
93+
if (!auprobe->simulate)
94+
return false;
95+
96+
insn = *(probe_opcode_t *)(&auprobe->insn[0]);
97+
addr = instruction_pointer(regs);
98+
99+
if (auprobe->api.handler)
100+
auprobe->api.handler(insn, addr, regs);
101+
102+
return true;
103+
}
104+
105+
void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
106+
{
107+
struct uprobe_task *utask = current->utask;
108+
109+
/*
110+
* Task has received a fatal signal, so reset back to probbed
111+
* address.
112+
*/
113+
instruction_pointer_set(regs, utask->vaddr);
114+
115+
regs->status &= ~SR_SPIE;
116+
}
117+
118+
bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
119+
struct pt_regs *regs)
120+
{
121+
if (ctx == RP_CHECK_CHAIN_CALL)
122+
return regs->sp <= ret->stack;
123+
else
124+
return regs->sp < ret->stack;
125+
}
126+
127+
unsigned long
128+
arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
129+
struct pt_regs *regs)
130+
{
131+
unsigned long ra;
132+
133+
ra = regs->ra;
134+
135+
regs->ra = trampoline_vaddr;
136+
137+
return ra;
138+
}
139+
140+
int arch_uprobe_exception_notify(struct notifier_block *self,
141+
unsigned long val, void *data)
142+
{
143+
return NOTIFY_DONE;
144+
}
145+
146+
bool uprobe_breakpoint_handler(struct pt_regs *regs)
147+
{
148+
if (uprobe_pre_sstep_notifier(regs))
149+
return true;
150+
151+
return false;
152+
}
153+
154+
bool uprobe_single_step_handler(struct pt_regs *regs)
155+
{
156+
if (uprobe_post_sstep_notifier(regs))
157+
return true;
158+
159+
return false;
160+
}
161+
162+
void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
163+
void *src, unsigned long len)
164+
{
165+
/* Initialize the slot */
166+
void *kaddr = kmap_atomic(page);
167+
void *dst = kaddr + (vaddr & ~PAGE_MASK);
168+
169+
memcpy(dst, src, len);
170+
171+
/* Add ebreak behind opcode to simulate singlestep */
172+
if (vaddr) {
173+
dst += GET_INSN_LENGTH(*(probe_opcode_t *)src);
174+
*(uprobe_opcode_t *)dst = __BUG_INSN_32;
175+
}
176+
177+
kunmap_atomic(kaddr);
178+
179+
/*
180+
* We probably need flush_icache_user_page() but it needs vma.
181+
* This should work on most of architectures by default. If
182+
* architecture needs to do something different it can define
183+
* its own version of the function.
184+
*/
185+
flush_dcache_page(page);
186+
}

arch/riscv/kernel/signal.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ static void do_signal(struct pt_regs *regs)
309309
asmlinkage __visible void do_notify_resume(struct pt_regs *regs,
310310
unsigned long thread_info_flags)
311311
{
312+
if (thread_info_flags & _TIF_UPROBE)
313+
uprobe_notify_resume(regs);
314+
312315
/* Handle pending signal delivery */
313316
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
314317
do_signal(regs);

arch/riscv/kernel/traps.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
7676
static void do_trap_error(struct pt_regs *regs, int signo, int code,
7777
unsigned long addr, const char *str)
7878
{
79+
current->thread.bad_cause = regs->cause;
80+
7981
if (user_mode(regs)) {
8082
do_trap(regs, signo, code, addr);
8183
} else {
@@ -153,6 +155,14 @@ asmlinkage __visible void do_trap_break(struct pt_regs *regs)
153155
if (kprobe_breakpoint_handler(regs))
154156
return;
155157
#endif
158+
#ifdef CONFIG_UPROBES
159+
if (uprobe_single_step_handler(regs))
160+
return;
161+
162+
if (uprobe_breakpoint_handler(regs))
163+
return;
164+
#endif
165+
current->thread.bad_cause = regs->cause;
156166

157167
if (user_mode(regs))
158168
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);

arch/riscv/mm/fault.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
240240
* in an atomic region, then we must not take the fault.
241241
*/
242242
if (unlikely(faulthandler_disabled() || !mm)) {
243+
tsk->thread.bad_cause = cause;
243244
no_context(regs, addr);
244245
return;
245246
}
@@ -262,16 +263,19 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
262263
mmap_read_lock(mm);
263264
vma = find_vma(mm, addr);
264265
if (unlikely(!vma)) {
266+
tsk->thread.bad_cause = cause;
265267
bad_area(regs, mm, code, addr);
266268
return;
267269
}
268270
if (likely(vma->vm_start <= addr))
269271
goto good_area;
270272
if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
273+
tsk->thread.bad_cause = cause;
271274
bad_area(regs, mm, code, addr);
272275
return;
273276
}
274277
if (unlikely(expand_stack(vma, addr))) {
278+
tsk->thread.bad_cause = cause;
275279
bad_area(regs, mm, code, addr);
276280
return;
277281
}
@@ -284,6 +288,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
284288
code = SEGV_ACCERR;
285289

286290
if (unlikely(access_error(cause, vma))) {
291+
tsk->thread.bad_cause = cause;
287292
bad_area(regs, mm, code, addr);
288293
return;
289294
}
@@ -317,6 +322,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
317322
mmap_read_unlock(mm);
318323

319324
if (unlikely(fault & VM_FAULT_ERROR)) {
325+
tsk->thread.bad_cause = cause;
320326
mm_fault_error(regs, addr, fault);
321327
return;
322328
}

0 commit comments

Comments
 (0)