diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/kernel/uprobes.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/kernel/uprobes.c')
-rw-r--r-- | arch/mips/kernel/uprobes.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/arch/mips/kernel/uprobes.c b/arch/mips/kernel/uprobes.c new file mode 100644 index 000000000..6dbe4eab0 --- /dev/null +++ b/arch/mips/kernel/uprobes.c | |||
@@ -0,0 +1,262 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/highmem.h> | ||
3 | #include <linux/kdebug.h> | ||
4 | #include <linux/types.h> | ||
5 | #include <linux/notifier.h> | ||
6 | #include <linux/sched.h> | ||
7 | #include <linux/uprobes.h> | ||
8 | |||
9 | #include <asm/branch.h> | ||
10 | #include <asm/cpu-features.h> | ||
11 | #include <asm/ptrace.h> | ||
12 | |||
13 | #include "probes-common.h" | ||
14 | |||
15 | static inline int insn_has_delay_slot(const union mips_instruction insn) | ||
16 | { | ||
17 | return __insn_has_delay_slot(insn); | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. | ||
22 | * @mm: the probed address space. | ||
23 | * @arch_uprobe: the probepoint information. | ||
24 | * @addr: virtual address at which to install the probepoint | ||
25 | * Return 0 on success or a -ve number on error. | ||
26 | */ | ||
27 | int arch_uprobe_analyze_insn(struct arch_uprobe *aup, | ||
28 | struct mm_struct *mm, unsigned long addr) | ||
29 | { | ||
30 | union mips_instruction inst; | ||
31 | |||
32 | /* | ||
33 | * For the time being this also blocks attempts to use uprobes with | ||
34 | * MIPS16 and microMIPS. | ||
35 | */ | ||
36 | if (addr & 0x03) | ||
37 | return -EINVAL; | ||
38 | |||
39 | inst.word = aup->insn[0]; | ||
40 | |||
41 | if (__insn_is_compact_branch(inst)) { | ||
42 | pr_notice("Uprobes for compact branches are not supported\n"); | ||
43 | return -EINVAL; | ||
44 | } | ||
45 | |||
46 | aup->ixol[0] = aup->insn[insn_has_delay_slot(inst)]; | ||
47 | aup->ixol[1] = UPROBE_BRK_UPROBE_XOL; /* NOP */ | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * is_trap_insn - check if the instruction is a trap variant | ||
54 | * @insn: instruction to be checked. | ||
55 | * Returns true if @insn is a trap variant. | ||
56 | * | ||
57 | * This definition overrides the weak definition in kernel/events/uprobes.c. | ||
58 | * and is needed for the case where an architecture has multiple trap | ||
59 | * instructions (like PowerPC or MIPS). We treat BREAK just like the more | ||
60 | * modern conditional trap instructions. | ||
61 | */ | ||
62 | bool is_trap_insn(uprobe_opcode_t *insn) | ||
63 | { | ||
64 | union mips_instruction inst; | ||
65 | |||
66 | inst.word = *insn; | ||
67 | |||
68 | switch (inst.i_format.opcode) { | ||
69 | case spec_op: | ||
70 | switch (inst.r_format.func) { | ||
71 | case break_op: | ||
72 | case teq_op: | ||
73 | case tge_op: | ||
74 | case tgeu_op: | ||
75 | case tlt_op: | ||
76 | case tltu_op: | ||
77 | case tne_op: | ||
78 | return 1; | ||
79 | } | ||
80 | break; | ||
81 | |||
82 | case bcond_op: /* Yes, really ... */ | ||
83 | switch (inst.u_format.rt) { | ||
84 | case teqi_op: | ||
85 | case tgei_op: | ||
86 | case tgeiu_op: | ||
87 | case tlti_op: | ||
88 | case tltiu_op: | ||
89 | case tnei_op: | ||
90 | return 1; | ||
91 | } | ||
92 | break; | ||
93 | } | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | #define UPROBE_TRAP_NR ULONG_MAX | ||
99 | |||
100 | /* | ||
101 | * arch_uprobe_pre_xol - prepare to execute out of line. | ||
102 | * @auprobe: the probepoint information. | ||
103 | * @regs: reflects the saved user state of current task. | ||
104 | */ | ||
105 | int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs) | ||
106 | { | ||
107 | struct uprobe_task *utask = current->utask; | ||
108 | |||
109 | /* | ||
110 | * Now find the EPC where to resume after the breakpoint has been | ||
111 | * dealt with. This may require emulation of a branch. | ||
112 | */ | ||
113 | aup->resume_epc = regs->cp0_epc + 4; | ||
114 | if (insn_has_delay_slot((union mips_instruction) aup->insn[0])) { | ||
115 | __compute_return_epc_for_insn(regs, | ||
116 | (union mips_instruction) aup->insn[0]); | ||
117 | aup->resume_epc = regs->cp0_epc; | ||
118 | } | ||
119 | utask->autask.saved_trap_nr = current->thread.trap_nr; | ||
120 | current->thread.trap_nr = UPROBE_TRAP_NR; | ||
121 | regs->cp0_epc = current->utask->xol_vaddr; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs) | ||
127 | { | ||
128 | struct uprobe_task *utask = current->utask; | ||
129 | |||
130 | current->thread.trap_nr = utask->autask.saved_trap_nr; | ||
131 | regs->cp0_epc = aup->resume_epc; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * If xol insn itself traps and generates a signal(Say, | ||
138 | * SIGILL/SIGSEGV/etc), then detect the case where a singlestepped | ||
139 | * instruction jumps back to its own address. It is assumed that anything | ||
140 | * like do_page_fault/do_trap/etc sets thread.trap_nr != -1. | ||
141 | * | ||
142 | * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr, | ||
143 | * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to | ||
144 | * UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol(). | ||
145 | */ | ||
146 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk) | ||
147 | { | ||
148 | if (tsk->thread.trap_nr != UPROBE_TRAP_NR) | ||
149 | return true; | ||
150 | |||
151 | return false; | ||
152 | } | ||
153 | |||
154 | int arch_uprobe_exception_notify(struct notifier_block *self, | ||
155 | unsigned long val, void *data) | ||
156 | { | ||
157 | struct die_args *args = data; | ||
158 | struct pt_regs *regs = args->regs; | ||
159 | |||
160 | /* regs == NULL is a kernel bug */ | ||
161 | if (WARN_ON(!regs)) | ||
162 | return NOTIFY_DONE; | ||
163 | |||
164 | /* We are only interested in userspace traps */ | ||
165 | if (!user_mode(regs)) | ||
166 | return NOTIFY_DONE; | ||
167 | |||
168 | switch (val) { | ||
169 | case DIE_UPROBE: | ||
170 | if (uprobe_pre_sstep_notifier(regs)) | ||
171 | return NOTIFY_STOP; | ||
172 | break; | ||
173 | case DIE_UPROBE_XOL: | ||
174 | if (uprobe_post_sstep_notifier(regs)) | ||
175 | return NOTIFY_STOP; | ||
176 | default: | ||
177 | break; | ||
178 | } | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * This function gets called when XOL instruction either gets trapped or | ||
185 | * the thread has a fatal signal. Reset the instruction pointer to its | ||
186 | * probed address for the potential restart or for post mortem analysis. | ||
187 | */ | ||
188 | void arch_uprobe_abort_xol(struct arch_uprobe *aup, | ||
189 | struct pt_regs *regs) | ||
190 | { | ||
191 | struct uprobe_task *utask = current->utask; | ||
192 | |||
193 | instruction_pointer_set(regs, utask->vaddr); | ||
194 | } | ||
195 | |||
196 | unsigned long arch_uretprobe_hijack_return_addr( | ||
197 | unsigned long trampoline_vaddr, struct pt_regs *regs) | ||
198 | { | ||
199 | unsigned long ra; | ||
200 | |||
201 | ra = regs->regs[31]; | ||
202 | |||
203 | /* Replace the return address with the trampoline address */ | ||
204 | regs->regs[31] = trampoline_vaddr; | ||
205 | |||
206 | return ra; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * set_swbp - store breakpoint at a given address. | ||
211 | * @auprobe: arch specific probepoint information. | ||
212 | * @mm: the probed process address space. | ||
213 | * @vaddr: the virtual address to insert the opcode. | ||
214 | * | ||
215 | * For mm @mm, store the breakpoint instruction at @vaddr. | ||
216 | * Return 0 (success) or a negative errno. | ||
217 | * | ||
218 | * This version overrides the weak version in kernel/events/uprobes.c. | ||
219 | * It is required to handle MIPS16 and microMIPS. | ||
220 | */ | ||
221 | int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, | ||
222 | unsigned long vaddr) | ||
223 | { | ||
224 | return uprobe_write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN); | ||
225 | } | ||
226 | |||
227 | void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, | ||
228 | void *src, unsigned long len) | ||
229 | { | ||
230 | unsigned long kaddr, kstart; | ||
231 | |||
232 | /* Initialize the slot */ | ||
233 | kaddr = (unsigned long)kmap_atomic(page); | ||
234 | kstart = kaddr + (vaddr & ~PAGE_MASK); | ||
235 | memcpy((void *)kstart, src, len); | ||
236 | flush_icache_range(kstart, kstart + len); | ||
237 | kunmap_atomic((void *)kaddr); | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs | ||
242 | * @regs: Reflects the saved state of the task after it has hit a breakpoint | ||
243 | * instruction. | ||
244 | * Return the address of the breakpoint instruction. | ||
245 | * | ||
246 | * This overrides the weak version in kernel/events/uprobes.c. | ||
247 | */ | ||
248 | unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) | ||
249 | { | ||
250 | return instruction_pointer(regs); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * See if the instruction can be emulated. | ||
255 | * Returns true if instruction was emulated, false otherwise. | ||
256 | * | ||
257 | * For now we always emulate so this function just returns 0. | ||
258 | */ | ||
259 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
260 | { | ||
261 | return 0; | ||
262 | } | ||