Skip to content

Commit 19bc6cb

Browse files
seehearfeelchenhuacai
authored andcommitted
LoongArch: Add uprobes support
Uprobes is the user-space counterpart to kprobes, this patch adds uprobes support for LoongArch. Here is a simple example with CONFIG_UPROBE_EVENTS=y: # cat test.c #include <stdio.h> int add(int a, int b) { return a + b; } int main() { return add(2, 7); } # gcc test.c -o /tmp/test # nm /tmp/test | grep add 0000000120004194 T add # cd /sys/kernel/debug/tracing # echo > uprobe_events # echo "p:myuprobe /tmp/test:0x4194 %r4 %r5" > uprobe_events # echo "r:myuretprobe /tmp/test:0x4194 %r4" >> uprobe_events # echo 1 > events/uprobes/enable # echo 1 > tracing_on # /tmp/test # cat trace ... # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | test-1060 [001] DNZff 1015.770620: myuprobe: (0x120004194) arg1=0x2 arg2=0x7 test-1060 [001] DNZff 1015.770930: myuretprobe: (0x1200041f0 <- 0x120004194) arg1=0x9 Tested-by: Jeff Xie <[email protected]> Signed-off-by: Tiezhu Yang <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent 6e32036 commit 19bc6cb

File tree

5 files changed

+197
-5
lines changed

5 files changed

+197
-5
lines changed

arch/loongarch/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,9 @@ config ARCH_MMAP_RND_BITS_MIN
643643
config ARCH_MMAP_RND_BITS_MAX
644644
default 18
645645

646+
config ARCH_SUPPORTS_UPROBES
647+
def_bool y
648+
646649
menu "Power management options"
647650

648651
config ARCH_SUSPEND_POSSIBLE

arch/loongarch/include/asm/uprobes.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
#ifndef __ASM_LOONGARCH_UPROBES_H
3+
#define __ASM_LOONGARCH_UPROBES_H
4+
5+
#include <asm/inst.h>
6+
7+
typedef u32 uprobe_opcode_t;
8+
9+
#define MAX_UINSN_BYTES 8
10+
#define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES
11+
12+
#define UPROBE_SWBP_INSN larch_insn_gen_break(BRK_UPROBE_BP)
13+
#define UPROBE_SWBP_INSN_SIZE LOONGARCH_INSN_SIZE
14+
15+
#define UPROBE_XOLBP_INSN larch_insn_gen_break(BRK_UPROBE_XOLBP)
16+
17+
struct arch_uprobe {
18+
unsigned long resume_era;
19+
u32 insn[2];
20+
u32 ixol[2];
21+
bool simulate;
22+
};
23+
24+
struct arch_uprobe_task {
25+
unsigned long saved_trap_nr;
26+
};
27+
28+
#ifdef CONFIG_UPROBES
29+
bool uprobe_breakpoint_handler(struct pt_regs *regs);
30+
bool uprobe_singlestep_handler(struct pt_regs *regs);
31+
#else /* !CONFIG_UPROBES */
32+
static inline bool uprobe_breakpoint_handler(struct pt_regs *regs) { return false; }
33+
static inline bool uprobe_singlestep_handler(struct pt_regs *regs) { return false; }
34+
#endif /* CONFIG_UPROBES */
35+
36+
#endif /* __ASM_LOONGARCH_UPROBES_H */

arch/loongarch/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
5656

5757
obj-$(CONFIG_KPROBES) += kprobes.o
5858
obj-$(CONFIG_RETHOOK) += rethook.o rethook_trampoline.o
59+
obj-$(CONFIG_UPROBES) += uprobes.o
5960

6061
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
6162

arch/loongarch/kernel/traps.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <asm/tlb.h>
4848
#include <asm/types.h>
4949
#include <asm/unwind.h>
50+
#include <asm/uprobes.h>
5051

5152
#include "access-helper.h"
5253

@@ -689,7 +690,6 @@ asmlinkage void noinstr do_bp(struct pt_regs *regs)
689690
if (regs->csr_prmd & CSR_PRMD_PIE)
690691
local_irq_enable();
691692

692-
current->thread.trap_nr = read_csr_excode();
693693
if (__get_inst(&opcode, (u32 *)era, user))
694694
goto out_sigsegv;
695695

@@ -711,18 +711,17 @@ asmlinkage void noinstr do_bp(struct pt_regs *regs)
711711
else
712712
break;
713713
case BRK_UPROBE_BP:
714-
if (notify_die(DIE_UPROBE, "Uprobe", regs, bcode,
715-
current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
714+
if (uprobe_breakpoint_handler(regs))
716715
goto out;
717716
else
718717
break;
719718
case BRK_UPROBE_XOLBP:
720-
if (notify_die(DIE_UPROBE_XOL, "Uprobe_XOL", regs, bcode,
721-
current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
719+
if (uprobe_singlestep_handler(regs))
722720
goto out;
723721
else
724722
break;
725723
default:
724+
current->thread.trap_nr = read_csr_excode();
726725
if (notify_die(DIE_TRAP, "Break", regs, bcode,
727726
current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
728727
goto out;

arch/loongarch/kernel/uprobes.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <linux/highmem.h>
3+
#include <linux/ptrace.h>
4+
#include <linux/sched.h>
5+
#include <linux/uprobes.h>
6+
#include <asm/cacheflush.h>
7+
8+
#define UPROBE_TRAP_NR UINT_MAX
9+
10+
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe,
11+
struct mm_struct *mm, unsigned long addr)
12+
{
13+
int idx;
14+
union loongarch_instruction insn;
15+
16+
if (addr & 0x3)
17+
return -EILSEQ;
18+
19+
for (idx = ARRAY_SIZE(auprobe->insn) - 1; idx >= 0; idx--) {
20+
insn.word = auprobe->insn[idx];
21+
if (insns_not_supported(insn))
22+
return -EINVAL;
23+
}
24+
25+
if (insns_need_simulation(insn)) {
26+
auprobe->ixol[0] = larch_insn_gen_nop();
27+
auprobe->simulate = true;
28+
} else {
29+
auprobe->ixol[0] = auprobe->insn[0];
30+
auprobe->simulate = false;
31+
}
32+
33+
auprobe->ixol[1] = UPROBE_XOLBP_INSN;
34+
35+
return 0;
36+
}
37+
38+
int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
39+
{
40+
struct uprobe_task *utask = current->utask;
41+
42+
utask->autask.saved_trap_nr = current->thread.trap_nr;
43+
current->thread.trap_nr = UPROBE_TRAP_NR;
44+
instruction_pointer_set(regs, utask->xol_vaddr);
45+
user_enable_single_step(current);
46+
47+
return 0;
48+
}
49+
50+
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
51+
{
52+
struct uprobe_task *utask = current->utask;
53+
54+
WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
55+
current->thread.trap_nr = utask->autask.saved_trap_nr;
56+
57+
if (auprobe->simulate)
58+
instruction_pointer_set(regs, auprobe->resume_era);
59+
else
60+
instruction_pointer_set(regs, utask->vaddr + LOONGARCH_INSN_SIZE);
61+
62+
user_disable_single_step(current);
63+
64+
return 0;
65+
}
66+
67+
void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
68+
{
69+
struct uprobe_task *utask = current->utask;
70+
71+
current->thread.trap_nr = utask->autask.saved_trap_nr;
72+
instruction_pointer_set(regs, utask->vaddr);
73+
user_disable_single_step(current);
74+
}
75+
76+
bool arch_uprobe_xol_was_trapped(struct task_struct *t)
77+
{
78+
if (t->thread.trap_nr != UPROBE_TRAP_NR)
79+
return true;
80+
81+
return false;
82+
}
83+
84+
bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
85+
{
86+
union loongarch_instruction insn;
87+
88+
if (!auprobe->simulate)
89+
return false;
90+
91+
insn.word = auprobe->insn[0];
92+
arch_simulate_insn(insn, regs);
93+
auprobe->resume_era = regs->csr_era;
94+
95+
return true;
96+
}
97+
98+
unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
99+
struct pt_regs *regs)
100+
{
101+
unsigned long ra = regs->regs[1];
102+
103+
regs->regs[1] = trampoline_vaddr;
104+
105+
return ra;
106+
}
107+
108+
bool arch_uretprobe_is_alive(struct return_instance *ret,
109+
enum rp_check ctx, struct pt_regs *regs)
110+
{
111+
if (ctx == RP_CHECK_CHAIN_CALL)
112+
return regs->regs[3] <= ret->stack;
113+
else
114+
return regs->regs[3] < ret->stack;
115+
}
116+
117+
int arch_uprobe_exception_notify(struct notifier_block *self,
118+
unsigned long val, void *data)
119+
{
120+
return NOTIFY_DONE;
121+
}
122+
123+
bool uprobe_breakpoint_handler(struct pt_regs *regs)
124+
{
125+
if (uprobe_pre_sstep_notifier(regs))
126+
return true;
127+
128+
return false;
129+
}
130+
131+
bool uprobe_singlestep_handler(struct pt_regs *regs)
132+
{
133+
if (uprobe_post_sstep_notifier(regs))
134+
return true;
135+
136+
return false;
137+
}
138+
139+
unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
140+
{
141+
return instruction_pointer(regs);
142+
}
143+
144+
void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
145+
void *src, unsigned long len)
146+
{
147+
void *kaddr = kmap_local_page(page);
148+
void *dst = kaddr + (vaddr & ~PAGE_MASK);
149+
150+
memcpy(dst, src, len);
151+
flush_icache_range((unsigned long)dst, (unsigned long)dst + len);
152+
kunmap_local(kaddr);
153+
}

0 commit comments

Comments
 (0)