Linux 6.7-rc7
[linux-modified.git] / arch / loongarch / kernel / uprobes.c
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 }