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
10int 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
38int 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, val: utask->xol_vaddr);
45 user_enable_single_step(current);
46
47 return 0;
48}
49
50int 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, val: auprobe->resume_era);
59 else
60 instruction_pointer_set(regs, val: utask->vaddr + LOONGARCH_INSN_SIZE);
61
62 user_disable_single_step(current);
63
64 return 0;
65}
66
67void 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, val: utask->vaddr);
73 user_disable_single_step(current);
74}
75
76bool 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
84bool 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
98unsigned 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
108bool 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
117int arch_uprobe_exception_notify(struct notifier_block *self,
118 unsigned long val, void *data)
119{
120 return NOTIFY_DONE;
121}
122
123bool uprobe_breakpoint_handler(struct pt_regs *regs)
124{
125 if (uprobe_pre_sstep_notifier(regs))
126 return true;
127
128 return false;
129}
130
131bool uprobe_singlestep_handler(struct pt_regs *regs)
132{
133 if (uprobe_post_sstep_notifier(regs))
134 return true;
135
136 return false;
137}
138
139unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
140{
141 return instruction_pointer(regs);
142}
143
144void 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(start: (unsigned long)dst, end: (unsigned long)dst + len);
152 kunmap_local(kaddr);
153}
154

source code of linux/arch/loongarch/kernel/uprobes.c