-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <xiehuan09@gmail.com> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
- Loading branch information
Tiezhu Yang
authored and
Huacai Chen
committed
Jun 29, 2023
1 parent
6e32036
commit 19bc6cb
Showing
5 changed files
with
197 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
#ifndef __ASM_LOONGARCH_UPROBES_H | ||
#define __ASM_LOONGARCH_UPROBES_H | ||
|
||
#include <asm/inst.h> | ||
|
||
typedef u32 uprobe_opcode_t; | ||
|
||
#define MAX_UINSN_BYTES 8 | ||
#define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES | ||
|
||
#define UPROBE_SWBP_INSN larch_insn_gen_break(BRK_UPROBE_BP) | ||
#define UPROBE_SWBP_INSN_SIZE LOONGARCH_INSN_SIZE | ||
|
||
#define UPROBE_XOLBP_INSN larch_insn_gen_break(BRK_UPROBE_XOLBP) | ||
|
||
struct arch_uprobe { | ||
unsigned long resume_era; | ||
u32 insn[2]; | ||
u32 ixol[2]; | ||
bool simulate; | ||
}; | ||
|
||
struct arch_uprobe_task { | ||
unsigned long saved_trap_nr; | ||
}; | ||
|
||
#ifdef CONFIG_UPROBES | ||
bool uprobe_breakpoint_handler(struct pt_regs *regs); | ||
bool uprobe_singlestep_handler(struct pt_regs *regs); | ||
#else /* !CONFIG_UPROBES */ | ||
static inline bool uprobe_breakpoint_handler(struct pt_regs *regs) { return false; } | ||
static inline bool uprobe_singlestep_handler(struct pt_regs *regs) { return false; } | ||
#endif /* CONFIG_UPROBES */ | ||
|
||
#endif /* __ASM_LOONGARCH_UPROBES_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
#include <linux/highmem.h> | ||
#include <linux/ptrace.h> | ||
#include <linux/sched.h> | ||
#include <linux/uprobes.h> | ||
#include <asm/cacheflush.h> | ||
|
||
#define UPROBE_TRAP_NR UINT_MAX | ||
|
||
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, | ||
struct mm_struct *mm, unsigned long addr) | ||
{ | ||
int idx; | ||
union loongarch_instruction insn; | ||
|
||
if (addr & 0x3) | ||
return -EILSEQ; | ||
|
||
for (idx = ARRAY_SIZE(auprobe->insn) - 1; idx >= 0; idx--) { | ||
insn.word = auprobe->insn[idx]; | ||
if (insns_not_supported(insn)) | ||
return -EINVAL; | ||
} | ||
|
||
if (insns_need_simulation(insn)) { | ||
auprobe->ixol[0] = larch_insn_gen_nop(); | ||
auprobe->simulate = true; | ||
} else { | ||
auprobe->ixol[0] = auprobe->insn[0]; | ||
auprobe->simulate = false; | ||
} | ||
|
||
auprobe->ixol[1] = UPROBE_XOLBP_INSN; | ||
|
||
return 0; | ||
} | ||
|
||
int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
{ | ||
struct uprobe_task *utask = current->utask; | ||
|
||
utask->autask.saved_trap_nr = current->thread.trap_nr; | ||
current->thread.trap_nr = UPROBE_TRAP_NR; | ||
instruction_pointer_set(regs, utask->xol_vaddr); | ||
user_enable_single_step(current); | ||
|
||
return 0; | ||
} | ||
|
||
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
{ | ||
struct uprobe_task *utask = current->utask; | ||
|
||
WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); | ||
current->thread.trap_nr = utask->autask.saved_trap_nr; | ||
|
||
if (auprobe->simulate) | ||
instruction_pointer_set(regs, auprobe->resume_era); | ||
else | ||
instruction_pointer_set(regs, utask->vaddr + LOONGARCH_INSN_SIZE); | ||
|
||
user_disable_single_step(current); | ||
|
||
return 0; | ||
} | ||
|
||
void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
{ | ||
struct uprobe_task *utask = current->utask; | ||
|
||
current->thread.trap_nr = utask->autask.saved_trap_nr; | ||
instruction_pointer_set(regs, utask->vaddr); | ||
user_disable_single_step(current); | ||
} | ||
|
||
bool arch_uprobe_xol_was_trapped(struct task_struct *t) | ||
{ | ||
if (t->thread.trap_nr != UPROBE_TRAP_NR) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
{ | ||
union loongarch_instruction insn; | ||
|
||
if (!auprobe->simulate) | ||
return false; | ||
|
||
insn.word = auprobe->insn[0]; | ||
arch_simulate_insn(insn, regs); | ||
auprobe->resume_era = regs->csr_era; | ||
|
||
return true; | ||
} | ||
|
||
unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, | ||
struct pt_regs *regs) | ||
{ | ||
unsigned long ra = regs->regs[1]; | ||
|
||
regs->regs[1] = trampoline_vaddr; | ||
|
||
return ra; | ||
} | ||
|
||
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->regs[3] <= ret->stack; | ||
else | ||
return regs->regs[3] < ret->stack; | ||
} | ||
|
||
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_singlestep_handler(struct pt_regs *regs) | ||
{ | ||
if (uprobe_post_sstep_notifier(regs)) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) | ||
{ | ||
return instruction_pointer(regs); | ||
} | ||
|
||
void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, | ||
void *src, unsigned long len) | ||
{ | ||
void *kaddr = kmap_local_page(page); | ||
void *dst = kaddr + (vaddr & ~PAGE_MASK); | ||
|
||
memcpy(dst, src, len); | ||
flush_icache_range((unsigned long)dst, (unsigned long)dst + len); | ||
kunmap_local(kaddr); | ||
} |