1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2010 Tilera Corporation. All Rights Reserved. |
4 | * Copyright 2015 Regents of the University of California |
5 | * Copyright 2017 SiFive |
6 | * |
7 | * Copied from arch/tile/kernel/ptrace.c |
8 | */ |
9 | |
10 | #include <asm/vector.h> |
11 | #include <asm/ptrace.h> |
12 | #include <asm/syscall.h> |
13 | #include <asm/thread_info.h> |
14 | #include <asm/switch_to.h> |
15 | #include <linux/audit.h> |
16 | #include <linux/compat.h> |
17 | #include <linux/ptrace.h> |
18 | #include <linux/elf.h> |
19 | #include <linux/regset.h> |
20 | #include <linux/sched.h> |
21 | #include <linux/sched/task_stack.h> |
22 | |
23 | enum riscv_regset { |
24 | REGSET_X, |
25 | #ifdef CONFIG_FPU |
26 | REGSET_F, |
27 | #endif |
28 | #ifdef CONFIG_RISCV_ISA_V |
29 | REGSET_V, |
30 | #endif |
31 | }; |
32 | |
33 | static int riscv_gpr_get(struct task_struct *target, |
34 | const struct user_regset *regset, |
35 | struct membuf to) |
36 | { |
37 | return membuf_write(s: &to, task_pt_regs(target), |
38 | size: sizeof(struct user_regs_struct)); |
39 | } |
40 | |
41 | static int riscv_gpr_set(struct task_struct *target, |
42 | const struct user_regset *regset, |
43 | unsigned int pos, unsigned int count, |
44 | const void *kbuf, const void __user *ubuf) |
45 | { |
46 | struct pt_regs *regs; |
47 | |
48 | regs = task_pt_regs(target); |
49 | return user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, data: regs, start_pos: 0, end_pos: -1); |
50 | } |
51 | |
52 | #ifdef CONFIG_FPU |
53 | static int riscv_fpr_get(struct task_struct *target, |
54 | const struct user_regset *regset, |
55 | struct membuf to) |
56 | { |
57 | struct __riscv_d_ext_state *fstate = &target->thread.fstate; |
58 | |
59 | if (target == current) |
60 | fstate_save(current, task_pt_regs(current)); |
61 | |
62 | membuf_write(&to, fstate, offsetof(struct __riscv_d_ext_state, fcsr)); |
63 | membuf_store(&to, fstate->fcsr); |
64 | return membuf_zero(&to, 4); // explicitly pad |
65 | } |
66 | |
67 | static int riscv_fpr_set(struct task_struct *target, |
68 | const struct user_regset *regset, |
69 | unsigned int pos, unsigned int count, |
70 | const void *kbuf, const void __user *ubuf) |
71 | { |
72 | int ret; |
73 | struct __riscv_d_ext_state *fstate = &target->thread.fstate; |
74 | |
75 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, |
76 | offsetof(struct __riscv_d_ext_state, fcsr)); |
77 | if (!ret) { |
78 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, |
79 | offsetof(struct __riscv_d_ext_state, fcsr) + |
80 | sizeof(fstate->fcsr)); |
81 | } |
82 | |
83 | return ret; |
84 | } |
85 | #endif |
86 | |
87 | #ifdef CONFIG_RISCV_ISA_V |
88 | static int riscv_vr_get(struct task_struct *target, |
89 | const struct user_regset *regset, |
90 | struct membuf to) |
91 | { |
92 | struct __riscv_v_ext_state *vstate = &target->thread.vstate; |
93 | struct __riscv_v_regset_state ptrace_vstate; |
94 | |
95 | if (!riscv_v_vstate_query(task_pt_regs(target))) |
96 | return -EINVAL; |
97 | |
98 | /* |
99 | * Ensure the vector registers have been saved to the memory before |
100 | * copying them to membuf. |
101 | */ |
102 | if (target == current) { |
103 | get_cpu_vector_context(); |
104 | riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current)); |
105 | put_cpu_vector_context(); |
106 | } |
107 | |
108 | ptrace_vstate.vstart = vstate->vstart; |
109 | ptrace_vstate.vl = vstate->vl; |
110 | ptrace_vstate.vtype = vstate->vtype; |
111 | ptrace_vstate.vcsr = vstate->vcsr; |
112 | ptrace_vstate.vlenb = vstate->vlenb; |
113 | |
114 | /* Copy vector header from vstate. */ |
115 | membuf_write(&to, &ptrace_vstate, sizeof(struct __riscv_v_regset_state)); |
116 | |
117 | /* Copy all the vector registers from vstate. */ |
118 | return membuf_write(&to, vstate->datap, riscv_v_vsize); |
119 | } |
120 | |
121 | static int riscv_vr_set(struct task_struct *target, |
122 | const struct user_regset *regset, |
123 | unsigned int pos, unsigned int count, |
124 | const void *kbuf, const void __user *ubuf) |
125 | { |
126 | int ret; |
127 | struct __riscv_v_ext_state *vstate = &target->thread.vstate; |
128 | struct __riscv_v_regset_state ptrace_vstate; |
129 | |
130 | if (!riscv_v_vstate_query(task_pt_regs(target))) |
131 | return -EINVAL; |
132 | |
133 | /* Copy rest of the vstate except datap */ |
134 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0, |
135 | sizeof(struct __riscv_v_regset_state)); |
136 | if (unlikely(ret)) |
137 | return ret; |
138 | |
139 | if (vstate->vlenb != ptrace_vstate.vlenb) |
140 | return -EINVAL; |
141 | |
142 | vstate->vstart = ptrace_vstate.vstart; |
143 | vstate->vl = ptrace_vstate.vl; |
144 | vstate->vtype = ptrace_vstate.vtype; |
145 | vstate->vcsr = ptrace_vstate.vcsr; |
146 | |
147 | /* Copy all the vector registers. */ |
148 | pos = 0; |
149 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap, |
150 | 0, riscv_v_vsize); |
151 | return ret; |
152 | } |
153 | #endif |
154 | |
155 | static const struct user_regset riscv_user_regset[] = { |
156 | [REGSET_X] = { |
157 | .core_note_type = NT_PRSTATUS, |
158 | .n = ELF_NGREG, |
159 | .size = sizeof(elf_greg_t), |
160 | .align = sizeof(elf_greg_t), |
161 | .regset_get = riscv_gpr_get, |
162 | .set = riscv_gpr_set, |
163 | }, |
164 | #ifdef CONFIG_FPU |
165 | [REGSET_F] = { |
166 | .core_note_type = NT_PRFPREG, |
167 | .n = ELF_NFPREG, |
168 | .size = sizeof(elf_fpreg_t), |
169 | .align = sizeof(elf_fpreg_t), |
170 | .regset_get = riscv_fpr_get, |
171 | .set = riscv_fpr_set, |
172 | }, |
173 | #endif |
174 | #ifdef CONFIG_RISCV_ISA_V |
175 | [REGSET_V] = { |
176 | .core_note_type = NT_RISCV_VECTOR, |
177 | .align = 16, |
178 | .n = ((32 * RISCV_MAX_VLENB) + |
179 | sizeof(struct __riscv_v_regset_state)) / sizeof(__u32), |
180 | .size = sizeof(__u32), |
181 | .regset_get = riscv_vr_get, |
182 | .set = riscv_vr_set, |
183 | }, |
184 | #endif |
185 | }; |
186 | |
187 | static const struct user_regset_view riscv_user_native_view = { |
188 | .name = "riscv" , |
189 | .e_machine = EM_RISCV, |
190 | .regsets = riscv_user_regset, |
191 | .n = ARRAY_SIZE(riscv_user_regset), |
192 | }; |
193 | |
194 | struct pt_regs_offset { |
195 | const char *name; |
196 | int offset; |
197 | }; |
198 | |
199 | #define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)} |
200 | #define REG_OFFSET_END {.name = NULL, .offset = 0} |
201 | |
202 | static const struct pt_regs_offset regoffset_table[] = { |
203 | REG_OFFSET_NAME(epc), |
204 | REG_OFFSET_NAME(ra), |
205 | REG_OFFSET_NAME(sp), |
206 | REG_OFFSET_NAME(gp), |
207 | REG_OFFSET_NAME(tp), |
208 | REG_OFFSET_NAME(t0), |
209 | REG_OFFSET_NAME(t1), |
210 | REG_OFFSET_NAME(t2), |
211 | REG_OFFSET_NAME(s0), |
212 | REG_OFFSET_NAME(s1), |
213 | REG_OFFSET_NAME(a0), |
214 | REG_OFFSET_NAME(a1), |
215 | REG_OFFSET_NAME(a2), |
216 | REG_OFFSET_NAME(a3), |
217 | REG_OFFSET_NAME(a4), |
218 | REG_OFFSET_NAME(a5), |
219 | REG_OFFSET_NAME(a6), |
220 | REG_OFFSET_NAME(a7), |
221 | REG_OFFSET_NAME(s2), |
222 | REG_OFFSET_NAME(s3), |
223 | REG_OFFSET_NAME(s4), |
224 | REG_OFFSET_NAME(s5), |
225 | REG_OFFSET_NAME(s6), |
226 | REG_OFFSET_NAME(s7), |
227 | REG_OFFSET_NAME(s8), |
228 | REG_OFFSET_NAME(s9), |
229 | REG_OFFSET_NAME(s10), |
230 | REG_OFFSET_NAME(s11), |
231 | REG_OFFSET_NAME(t3), |
232 | REG_OFFSET_NAME(t4), |
233 | REG_OFFSET_NAME(t5), |
234 | REG_OFFSET_NAME(t6), |
235 | REG_OFFSET_NAME(status), |
236 | REG_OFFSET_NAME(badaddr), |
237 | REG_OFFSET_NAME(cause), |
238 | REG_OFFSET_NAME(orig_a0), |
239 | REG_OFFSET_END, |
240 | }; |
241 | |
242 | /** |
243 | * regs_query_register_offset() - query register offset from its name |
244 | * @name: the name of a register |
245 | * |
246 | * regs_query_register_offset() returns the offset of a register in struct |
247 | * pt_regs from its name. If the name is invalid, this returns -EINVAL; |
248 | */ |
249 | int regs_query_register_offset(const char *name) |
250 | { |
251 | const struct pt_regs_offset *roff; |
252 | |
253 | for (roff = regoffset_table; roff->name != NULL; roff++) |
254 | if (!strcmp(roff->name, name)) |
255 | return roff->offset; |
256 | return -EINVAL; |
257 | } |
258 | |
259 | /** |
260 | * regs_within_kernel_stack() - check the address in the stack |
261 | * @regs: pt_regs which contains kernel stack pointer. |
262 | * @addr: address which is checked. |
263 | * |
264 | * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). |
265 | * If @addr is within the kernel stack, it returns true. If not, returns false. |
266 | */ |
267 | static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) |
268 | { |
269 | return (addr & ~(THREAD_SIZE - 1)) == |
270 | (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)); |
271 | } |
272 | |
273 | /** |
274 | * regs_get_kernel_stack_nth() - get Nth entry of the stack |
275 | * @regs: pt_regs which contains kernel stack pointer. |
276 | * @n: stack entry number. |
277 | * |
278 | * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which |
279 | * is specified by @regs. If the @n th entry is NOT in the kernel stack, |
280 | * this returns 0. |
281 | */ |
282 | unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) |
283 | { |
284 | unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); |
285 | |
286 | addr += n; |
287 | if (regs_within_kernel_stack(regs, addr: (unsigned long)addr)) |
288 | return *addr; |
289 | else |
290 | return 0; |
291 | } |
292 | |
293 | void ptrace_disable(struct task_struct *child) |
294 | { |
295 | } |
296 | |
297 | long arch_ptrace(struct task_struct *child, long request, |
298 | unsigned long addr, unsigned long data) |
299 | { |
300 | long ret = -EIO; |
301 | |
302 | switch (request) { |
303 | default: |
304 | ret = ptrace_request(child, request, addr, data); |
305 | break; |
306 | } |
307 | |
308 | return ret; |
309 | } |
310 | |
311 | #ifdef CONFIG_COMPAT |
312 | static int compat_riscv_gpr_get(struct task_struct *target, |
313 | const struct user_regset *regset, |
314 | struct membuf to) |
315 | { |
316 | struct compat_user_regs_struct cregs; |
317 | |
318 | regs_to_cregs(&cregs, task_pt_regs(target)); |
319 | |
320 | return membuf_write(&to, &cregs, |
321 | sizeof(struct compat_user_regs_struct)); |
322 | } |
323 | |
324 | static int compat_riscv_gpr_set(struct task_struct *target, |
325 | const struct user_regset *regset, |
326 | unsigned int pos, unsigned int count, |
327 | const void *kbuf, const void __user *ubuf) |
328 | { |
329 | int ret; |
330 | struct compat_user_regs_struct cregs; |
331 | |
332 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, data: &cregs, start_pos: 0, end_pos: -1); |
333 | |
334 | cregs_to_regs(&cregs, task_pt_regs(target)); |
335 | |
336 | return ret; |
337 | } |
338 | |
339 | static const struct user_regset compat_riscv_user_regset[] = { |
340 | [REGSET_X] = { |
341 | .core_note_type = NT_PRSTATUS, |
342 | .n = ELF_NGREG, |
343 | .size = sizeof(compat_elf_greg_t), |
344 | .align = sizeof(compat_elf_greg_t), |
345 | .regset_get = compat_riscv_gpr_get, |
346 | .set = compat_riscv_gpr_set, |
347 | }, |
348 | #ifdef CONFIG_FPU |
349 | [REGSET_F] = { |
350 | .core_note_type = NT_PRFPREG, |
351 | .n = ELF_NFPREG, |
352 | .size = sizeof(elf_fpreg_t), |
353 | .align = sizeof(elf_fpreg_t), |
354 | .regset_get = riscv_fpr_get, |
355 | .set = riscv_fpr_set, |
356 | }, |
357 | #endif |
358 | }; |
359 | |
360 | static const struct user_regset_view compat_riscv_user_native_view = { |
361 | .name = "riscv" , |
362 | .e_machine = EM_RISCV, |
363 | .regsets = compat_riscv_user_regset, |
364 | .n = ARRAY_SIZE(compat_riscv_user_regset), |
365 | }; |
366 | |
367 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, |
368 | compat_ulong_t caddr, compat_ulong_t cdata) |
369 | { |
370 | long ret = -EIO; |
371 | |
372 | switch (request) { |
373 | default: |
374 | ret = compat_ptrace_request(child, request, addr: caddr, data: cdata); |
375 | break; |
376 | } |
377 | |
378 | return ret; |
379 | } |
380 | #else |
381 | static const struct user_regset_view compat_riscv_user_native_view = {}; |
382 | #endif /* CONFIG_COMPAT */ |
383 | |
384 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
385 | { |
386 | if (is_compat_thread(&task->thread_info)) |
387 | return &compat_riscv_user_native_view; |
388 | else |
389 | return &riscv_user_native_view; |
390 | } |
391 | |