1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* ptrace.c: Sparc process tracing support. |
3 | * |
4 | * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) |
5 | * |
6 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, |
7 | * and David Mosberger. |
8 | * |
9 | * Added Linux support -miguel (weird, eh?, the original code was meant |
10 | * to emulate SunOS). |
11 | */ |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/sched.h> |
15 | #include <linux/mm.h> |
16 | #include <linux/errno.h> |
17 | #include <linux/ptrace.h> |
18 | #include <linux/user.h> |
19 | #include <linux/smp.h> |
20 | #include <linux/security.h> |
21 | #include <linux/signal.h> |
22 | #include <linux/regset.h> |
23 | #include <linux/elf.h> |
24 | |
25 | #include <linux/uaccess.h> |
26 | #include <asm/cacheflush.h> |
27 | |
28 | #include "kernel.h" |
29 | |
30 | /* #define ALLOW_INIT_TRACING */ |
31 | |
32 | /* |
33 | * Called by kernel/ptrace.c when detaching.. |
34 | * |
35 | * Make sure single step bits etc are not set. |
36 | */ |
37 | void ptrace_disable(struct task_struct *child) |
38 | { |
39 | /* nothing to do */ |
40 | } |
41 | |
42 | enum sparc_regset { |
43 | REGSET_GENERAL, |
44 | REGSET_FP, |
45 | }; |
46 | |
47 | static int regwindow32_get(struct task_struct *target, |
48 | const struct pt_regs *regs, |
49 | u32 *uregs) |
50 | { |
51 | unsigned long reg_window = regs->u_regs[UREG_I6]; |
52 | int size = 16 * sizeof(u32); |
53 | |
54 | if (target == current) { |
55 | if (copy_from_user(to: uregs, from: (void __user *)reg_window, n: size)) |
56 | return -EFAULT; |
57 | } else { |
58 | if (access_process_vm(tsk: target, addr: reg_window, buf: uregs, len: size, |
59 | gup_flags: FOLL_FORCE) != size) |
60 | return -EFAULT; |
61 | } |
62 | return 0; |
63 | } |
64 | |
65 | static int regwindow32_set(struct task_struct *target, |
66 | const struct pt_regs *regs, |
67 | u32 *uregs) |
68 | { |
69 | unsigned long reg_window = regs->u_regs[UREG_I6]; |
70 | int size = 16 * sizeof(u32); |
71 | |
72 | if (target == current) { |
73 | if (copy_to_user(to: (void __user *)reg_window, from: uregs, n: size)) |
74 | return -EFAULT; |
75 | } else { |
76 | if (access_process_vm(tsk: target, addr: reg_window, buf: uregs, len: size, |
77 | gup_flags: FOLL_FORCE | FOLL_WRITE) != size) |
78 | return -EFAULT; |
79 | } |
80 | return 0; |
81 | } |
82 | |
83 | static int genregs32_get(struct task_struct *target, |
84 | const struct user_regset *regset, |
85 | struct membuf to) |
86 | { |
87 | const struct pt_regs *regs = target->thread.kregs; |
88 | u32 uregs[16]; |
89 | |
90 | if (target == current) |
91 | flush_user_windows(); |
92 | |
93 | membuf_write(s: &to, v: regs->u_regs, size: 16 * sizeof(u32)); |
94 | if (!to.left) |
95 | return 0; |
96 | if (regwindow32_get(target, regs, uregs)) |
97 | return -EFAULT; |
98 | membuf_write(s: &to, v: uregs, size: 16 * sizeof(u32)); |
99 | membuf_store(&to, regs->psr); |
100 | membuf_store(&to, regs->pc); |
101 | membuf_store(&to, regs->npc); |
102 | membuf_store(&to, regs->y); |
103 | return membuf_zero(s: &to, size: 2 * sizeof(u32)); |
104 | } |
105 | |
106 | static int genregs32_set(struct task_struct *target, |
107 | const struct user_regset *regset, |
108 | unsigned int pos, unsigned int count, |
109 | const void *kbuf, const void __user *ubuf) |
110 | { |
111 | struct pt_regs *regs = target->thread.kregs; |
112 | u32 uregs[16]; |
113 | u32 psr; |
114 | int ret; |
115 | |
116 | if (target == current) |
117 | flush_user_windows(); |
118 | |
119 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
120 | data: regs->u_regs, |
121 | start_pos: 0, end_pos: 16 * sizeof(u32)); |
122 | if (ret || !count) |
123 | return ret; |
124 | |
125 | if (regwindow32_get(target, regs, uregs)) |
126 | return -EFAULT; |
127 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
128 | data: uregs, |
129 | start_pos: 16 * sizeof(u32), end_pos: 32 * sizeof(u32)); |
130 | if (ret) |
131 | return ret; |
132 | if (regwindow32_set(target, regs, uregs)) |
133 | return -EFAULT; |
134 | if (!count) |
135 | return 0; |
136 | |
137 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
138 | data: &psr, |
139 | start_pos: 32 * sizeof(u32), end_pos: 33 * sizeof(u32)); |
140 | if (ret) |
141 | return ret; |
142 | regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | |
143 | (psr & (PSR_ICC | PSR_SYSCALL)); |
144 | if (!count) |
145 | return 0; |
146 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
147 | data: ®s->pc, |
148 | start_pos: 33 * sizeof(u32), end_pos: 34 * sizeof(u32)); |
149 | if (ret || !count) |
150 | return ret; |
151 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
152 | data: ®s->npc, |
153 | start_pos: 34 * sizeof(u32), end_pos: 35 * sizeof(u32)); |
154 | if (ret || !count) |
155 | return ret; |
156 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
157 | data: ®s->y, |
158 | start_pos: 35 * sizeof(u32), end_pos: 36 * sizeof(u32)); |
159 | if (ret || !count) |
160 | return ret; |
161 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, start_pos: 36 * sizeof(u32), |
162 | end_pos: 38 * sizeof(u32)); |
163 | return 0; |
164 | } |
165 | |
166 | static int fpregs32_get(struct task_struct *target, |
167 | const struct user_regset *regset, |
168 | struct membuf to) |
169 | { |
170 | #if 0 |
171 | if (target == current) |
172 | save_and_clear_fpu(); |
173 | #endif |
174 | |
175 | membuf_write(s: &to, v: target->thread.float_regs, size: 32 * sizeof(u32)); |
176 | membuf_zero(s: &to, size: sizeof(u32)); |
177 | membuf_write(s: &to, v: &target->thread.fsr, size: sizeof(u32)); |
178 | membuf_store(&to, (u32)((1 << 8) | (8 << 16))); |
179 | return membuf_zero(s: &to, size: 64 * sizeof(u32)); |
180 | } |
181 | |
182 | static int fpregs32_set(struct task_struct *target, |
183 | const struct user_regset *regset, |
184 | unsigned int pos, unsigned int count, |
185 | const void *kbuf, const void __user *ubuf) |
186 | { |
187 | unsigned long *fpregs = target->thread.float_regs; |
188 | int ret; |
189 | |
190 | #if 0 |
191 | if (target == current) |
192 | save_and_clear_fpu(); |
193 | #endif |
194 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
195 | data: fpregs, |
196 | start_pos: 0, end_pos: 32 * sizeof(u32)); |
197 | if (!ret) |
198 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
199 | start_pos: 32 * sizeof(u32), |
200 | end_pos: 33 * sizeof(u32)); |
201 | if (!ret) |
202 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
203 | data: &target->thread.fsr, |
204 | start_pos: 33 * sizeof(u32), |
205 | end_pos: 34 * sizeof(u32)); |
206 | if (!ret) |
207 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
208 | start_pos: 34 * sizeof(u32), end_pos: -1); |
209 | return ret; |
210 | } |
211 | |
212 | static const struct user_regset sparc32_regsets[] = { |
213 | /* Format is: |
214 | * G0 --> G7 |
215 | * O0 --> O7 |
216 | * L0 --> L7 |
217 | * I0 --> I7 |
218 | * PSR, PC, nPC, Y, WIM, TBR |
219 | */ |
220 | [REGSET_GENERAL] = { |
221 | .core_note_type = NT_PRSTATUS, |
222 | .n = 38, |
223 | .size = sizeof(u32), .align = sizeof(u32), |
224 | .regset_get = genregs32_get, .set = genregs32_set |
225 | }, |
226 | /* Format is: |
227 | * F0 --> F31 |
228 | * empty 32-bit word |
229 | * FSR (32--bit word) |
230 | * FPU QUEUE COUNT (8-bit char) |
231 | * FPU QUEUE ENTRYSIZE (8-bit char) |
232 | * FPU ENABLED (8-bit char) |
233 | * empty 8-bit char |
234 | * FPU QUEUE (64 32-bit ints) |
235 | */ |
236 | [REGSET_FP] = { |
237 | .core_note_type = NT_PRFPREG, |
238 | .n = 99, |
239 | .size = sizeof(u32), .align = sizeof(u32), |
240 | .regset_get = fpregs32_get, .set = fpregs32_set |
241 | }, |
242 | }; |
243 | |
244 | static int getregs_get(struct task_struct *target, |
245 | const struct user_regset *regset, |
246 | struct membuf to) |
247 | { |
248 | const struct pt_regs *regs = target->thread.kregs; |
249 | |
250 | if (target == current) |
251 | flush_user_windows(); |
252 | |
253 | membuf_store(&to, regs->psr); |
254 | membuf_store(&to, regs->pc); |
255 | membuf_store(&to, regs->npc); |
256 | membuf_store(&to, regs->y); |
257 | return membuf_write(s: &to, v: regs->u_regs + 1, size: 15 * sizeof(u32)); |
258 | } |
259 | |
260 | static int setregs_set(struct task_struct *target, |
261 | const struct user_regset *regset, |
262 | unsigned int pos, unsigned int count, |
263 | const void *kbuf, const void __user *ubuf) |
264 | { |
265 | struct pt_regs *regs = target->thread.kregs; |
266 | u32 v[4]; |
267 | int ret; |
268 | |
269 | if (target == current) |
270 | flush_user_windows(); |
271 | |
272 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
273 | data: v, |
274 | start_pos: 0, end_pos: 4 * sizeof(u32)); |
275 | if (ret) |
276 | return ret; |
277 | regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | |
278 | (v[0] & (PSR_ICC | PSR_SYSCALL)); |
279 | regs->pc = v[1]; |
280 | regs->npc = v[2]; |
281 | regs->y = v[3]; |
282 | return user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
283 | data: regs->u_regs + 1, |
284 | start_pos: 4 * sizeof(u32) , end_pos: 19 * sizeof(u32)); |
285 | } |
286 | |
287 | static int getfpregs_get(struct task_struct *target, |
288 | const struct user_regset *regset, |
289 | struct membuf to) |
290 | { |
291 | #if 0 |
292 | if (target == current) |
293 | save_and_clear_fpu(); |
294 | #endif |
295 | membuf_write(s: &to, v: &target->thread.float_regs, size: 32 * sizeof(u32)); |
296 | membuf_write(s: &to, v: &target->thread.fsr, size: sizeof(u32)); |
297 | return membuf_zero(s: &to, size: 35 * sizeof(u32)); |
298 | } |
299 | |
300 | static int setfpregs_set(struct task_struct *target, |
301 | const struct user_regset *regset, |
302 | unsigned int pos, unsigned int count, |
303 | const void *kbuf, const void __user *ubuf) |
304 | { |
305 | unsigned long *fpregs = target->thread.float_regs; |
306 | int ret; |
307 | |
308 | #if 0 |
309 | if (target == current) |
310 | save_and_clear_fpu(); |
311 | #endif |
312 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
313 | data: fpregs, |
314 | start_pos: 0, end_pos: 32 * sizeof(u32)); |
315 | if (ret) |
316 | return ret; |
317 | return user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
318 | data: &target->thread.fsr, |
319 | start_pos: 32 * sizeof(u32), |
320 | end_pos: 33 * sizeof(u32)); |
321 | } |
322 | |
323 | static const struct user_regset ptrace32_regsets[] = { |
324 | [REGSET_GENERAL] = { |
325 | .n = 19, .size = sizeof(u32), |
326 | .regset_get = getregs_get, .set = setregs_set, |
327 | }, |
328 | [REGSET_FP] = { |
329 | .n = 68, .size = sizeof(u32), |
330 | .regset_get = getfpregs_get, .set = setfpregs_set, |
331 | }, |
332 | }; |
333 | |
334 | static const struct user_regset_view ptrace32_view = { |
335 | .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) |
336 | }; |
337 | |
338 | static const struct user_regset_view user_sparc32_view = { |
339 | .name = "sparc" , .e_machine = EM_SPARC, |
340 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) |
341 | }; |
342 | |
343 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
344 | { |
345 | return &user_sparc32_view; |
346 | } |
347 | |
348 | struct fps { |
349 | unsigned long regs[32]; |
350 | unsigned long fsr; |
351 | unsigned long flags; |
352 | unsigned long ; |
353 | unsigned long fpqd; |
354 | struct fq { |
355 | unsigned long *insnaddr; |
356 | unsigned long insn; |
357 | } fpq[16]; |
358 | }; |
359 | |
360 | long arch_ptrace(struct task_struct *child, long request, |
361 | unsigned long addr, unsigned long data) |
362 | { |
363 | unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; |
364 | void __user *addr2p; |
365 | struct pt_regs __user *pregs; |
366 | struct fps __user *fps; |
367 | int ret; |
368 | |
369 | addr2p = (void __user *) addr2; |
370 | pregs = (struct pt_regs __user *) addr; |
371 | fps = (struct fps __user *) addr; |
372 | |
373 | switch(request) { |
374 | case PTRACE_GETREGS: { |
375 | ret = copy_regset_to_user(target: child, view: &ptrace32_view, |
376 | setno: REGSET_GENERAL, offset: 0, |
377 | size: 19 * sizeof(u32), |
378 | data: pregs); |
379 | break; |
380 | } |
381 | |
382 | case PTRACE_SETREGS: { |
383 | ret = copy_regset_from_user(target: child, view: &ptrace32_view, |
384 | setno: REGSET_GENERAL, offset: 0, |
385 | size: 19 * sizeof(u32), |
386 | data: pregs); |
387 | break; |
388 | } |
389 | |
390 | case PTRACE_GETFPREGS: { |
391 | ret = copy_regset_to_user(target: child, view: &ptrace32_view, |
392 | setno: REGSET_FP, offset: 0, |
393 | size: 68 * sizeof(u32), |
394 | data: fps); |
395 | break; |
396 | } |
397 | |
398 | case PTRACE_SETFPREGS: { |
399 | ret = copy_regset_from_user(target: child, view: &ptrace32_view, |
400 | setno: REGSET_FP, offset: 0, |
401 | size: 33 * sizeof(u32), |
402 | data: fps); |
403 | break; |
404 | } |
405 | |
406 | case PTRACE_READTEXT: |
407 | case PTRACE_READDATA: |
408 | ret = ptrace_readdata(tsk: child, src: addr, dst: addr2p, len: data); |
409 | |
410 | if (ret == data) |
411 | ret = 0; |
412 | else if (ret >= 0) |
413 | ret = -EIO; |
414 | break; |
415 | |
416 | case PTRACE_WRITETEXT: |
417 | case PTRACE_WRITEDATA: |
418 | ret = ptrace_writedata(tsk: child, src: addr2p, dst: addr, len: data); |
419 | |
420 | if (ret == data) |
421 | ret = 0; |
422 | else if (ret >= 0) |
423 | ret = -EIO; |
424 | break; |
425 | |
426 | default: |
427 | if (request == PTRACE_SPARC_DETACH) |
428 | request = PTRACE_DETACH; |
429 | ret = ptrace_request(child, request, addr, data); |
430 | break; |
431 | } |
432 | |
433 | return ret; |
434 | } |
435 | |
436 | asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) |
437 | { |
438 | int ret = 0; |
439 | |
440 | if (test_thread_flag(TIF_SYSCALL_TRACE)) { |
441 | if (syscall_exit_p) |
442 | ptrace_report_syscall_exit(regs, step: 0); |
443 | else |
444 | ret = ptrace_report_syscall_entry(regs); |
445 | } |
446 | |
447 | return ret; |
448 | } |
449 | |