Skip to content

Commit c22b0bc

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

File tree

13 files changed

+773
-0
lines changed

13 files changed

+773
-0
lines changed

arch/riscv/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ config RISCV
7171
select HAVE_GCC_PLUGINS
7272
select HAVE_GENERIC_VDSO if MMU && 64BIT
7373
select HAVE_IRQ_TIME_ACCOUNTING
74+
select HAVE_KPROBES
75+
select HAVE_KRETPROBES
7476
select HAVE_PCI
7577
select HAVE_PERF_EVENTS
7678
select HAVE_PERF_REGS

arch/riscv/include/asm/kprobes.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,44 @@
1111

1212
#include <asm-generic/kprobes.h>
1313

14+
#ifdef CONFIG_KPROBES
15+
#include <linux/types.h>
16+
#include <linux/ptrace.h>
17+
#include <linux/percpu.h>
18+
19+
#define __ARCH_WANT_KPROBES_INSN_SLOT
20+
#define MAX_INSN_SIZE 2
21+
22+
#define flush_insn_slot(p) do { } while (0)
23+
#define kretprobe_blacklist_size 0
24+
25+
#include <asm/probes.h>
26+
27+
struct prev_kprobe {
28+
struct kprobe *kp;
29+
unsigned int status;
30+
};
31+
32+
/* Single step context for kprobe */
33+
struct kprobe_step_ctx {
34+
unsigned long ss_pending;
35+
unsigned long match_addr;
36+
};
37+
38+
/* per-cpu kprobe control block */
39+
struct kprobe_ctlblk {
40+
unsigned int kprobe_status;
41+
unsigned long saved_status;
42+
struct prev_kprobe prev_kprobe;
43+
struct kprobe_step_ctx ss_ctx;
44+
};
45+
46+
void arch_remove_kprobe(struct kprobe *p);
47+
int kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr);
48+
bool kprobe_breakpoint_handler(struct pt_regs *regs);
49+
bool kprobe_single_step_handler(struct pt_regs *regs);
50+
void kretprobe_trampoline(void);
51+
void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
52+
53+
#endif /* CONFIG_KPROBES */
1454
#endif /* _ASM_RISCV_KPROBES_H */

arch/riscv/include/asm/probes.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef _ASM_RISCV_PROBES_H
4+
#define _ASM_RISCV_PROBES_H
5+
6+
typedef u32 probe_opcode_t;
7+
typedef bool (probes_handler_t) (u32 opcode, unsigned long addr, struct pt_regs *);
8+
9+
/* architecture specific copy of original instruction */
10+
struct arch_probe_insn {
11+
probe_opcode_t *insn;
12+
probes_handler_t *handler;
13+
/* restore address after simulation */
14+
unsigned long restore;
15+
};
16+
17+
#ifdef CONFIG_KPROBES
18+
typedef u32 kprobe_opcode_t;
19+
struct arch_specific_insn {
20+
struct arch_probe_insn api;
21+
};
22+
#endif
23+
24+
#endif /* _ASM_RISCV_PROBES_H */

arch/riscv/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ obj-y += riscv_ksyms.o
3030
obj-y += stacktrace.o
3131
obj-y += cacheinfo.o
3232
obj-y += patch.o
33+
obj-y += probes/
3334
obj-$(CONFIG_MMU) += vdso.o vdso/
3435

3536
obj-$(CONFIG_RISCV_M_MODE) += traps_misaligned.o

arch/riscv/kernel/probes/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o simulate-insn.o
3+
obj-$(CONFIG_KPROBES) += kprobes_trampoline.o
4+
CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
3+
#include <linux/kernel.h>
4+
#include <linux/kprobes.h>
5+
#include <linux/module.h>
6+
#include <linux/kallsyms.h>
7+
#include <asm/sections.h>
8+
9+
#include "decode-insn.h"
10+
#include "simulate-insn.h"
11+
12+
/* Return:
13+
* INSN_REJECTED If instruction is one not allowed to kprobe,
14+
* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
15+
*/
16+
enum probe_insn __kprobes
17+
riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *api)
18+
{
19+
probe_opcode_t insn = le32_to_cpu(*addr);
20+
21+
/*
22+
* Reject instructions list:
23+
*/
24+
RISCV_INSN_REJECTED(system, insn);
25+
RISCV_INSN_REJECTED(fence, insn);
26+
27+
/*
28+
* Simulate instructions list:
29+
* TODO: the REJECTED ones below need to be implemented
30+
*/
31+
#ifdef CONFIG_RISCV_ISA_C
32+
RISCV_INSN_REJECTED(c_j, insn);
33+
RISCV_INSN_REJECTED(c_jr, insn);
34+
RISCV_INSN_REJECTED(c_jal, insn);
35+
RISCV_INSN_REJECTED(c_jalr, insn);
36+
RISCV_INSN_REJECTED(c_beqz, insn);
37+
RISCV_INSN_REJECTED(c_bnez, insn);
38+
RISCV_INSN_REJECTED(c_ebreak, insn);
39+
#endif
40+
41+
RISCV_INSN_REJECTED(auipc, insn);
42+
RISCV_INSN_REJECTED(branch, insn);
43+
44+
RISCV_INSN_SET_SIMULATE(jal, insn);
45+
RISCV_INSN_SET_SIMULATE(jalr, insn);
46+
47+
return INSN_GOOD;
48+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* SPDX-License-Identifier: GPL-2.0+ */
2+
3+
#ifndef _RISCV_KERNEL_KPROBES_DECODE_INSN_H
4+
#define _RISCV_KERNEL_KPROBES_DECODE_INSN_H
5+
6+
#include <asm/sections.h>
7+
#include <asm/kprobes.h>
8+
9+
enum probe_insn {
10+
INSN_REJECTED,
11+
INSN_GOOD_NO_SLOT,
12+
INSN_GOOD,
13+
};
14+
15+
enum probe_insn __kprobes
16+
riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *asi);
17+
18+
#endif /* _RISCV_KERNEL_KPROBES_DECODE_INSN_H */

0 commit comments

Comments
 (0)