diff options
Diffstat (limited to 'arch/cris/arch-v32/kernel/ptrace.c')
-rw-r--r-- | arch/cris/arch-v32/kernel/ptrace.c | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c new file mode 100644 index 000000000000..208489da2a87 --- /dev/null +++ b/arch/cris/arch-v32/kernel/ptrace.c | |||
@@ -0,0 +1,597 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000-2003, Axis Communications AB. | ||
3 | */ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/sched.h> | ||
7 | #include <linux/mm.h> | ||
8 | #include <linux/smp.h> | ||
9 | #include <linux/smp_lock.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/ptrace.h> | ||
12 | #include <linux/user.h> | ||
13 | #include <linux/signal.h> | ||
14 | #include <linux/security.h> | ||
15 | |||
16 | #include <asm/uaccess.h> | ||
17 | #include <asm/page.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/system.h> | ||
20 | #include <asm/processor.h> | ||
21 | #include <asm/arch/hwregs/supp_reg.h> | ||
22 | |||
23 | /* | ||
24 | * Determines which bits in CCS the user has access to. | ||
25 | * 1 = access, 0 = no access. | ||
26 | */ | ||
27 | #define CCS_MASK 0x00087c00 /* SXNZVC */ | ||
28 | |||
29 | #define SBIT_USER (1 << (S_CCS_BITNR + CCS_SHIFT)) | ||
30 | |||
31 | static int put_debugreg(long pid, unsigned int regno, long data); | ||
32 | static long get_debugreg(long pid, unsigned int regno); | ||
33 | static unsigned long get_pseudo_pc(struct task_struct *child); | ||
34 | void deconfigure_bp(long pid); | ||
35 | |||
36 | extern unsigned long cris_signal_return_page; | ||
37 | |||
38 | /* | ||
39 | * Get contents of register REGNO in task TASK. | ||
40 | */ | ||
41 | long get_reg(struct task_struct *task, unsigned int regno) | ||
42 | { | ||
43 | /* USP is a special case, it's not in the pt_regs struct but | ||
44 | * in the tasks thread struct | ||
45 | */ | ||
46 | unsigned long ret; | ||
47 | |||
48 | if (regno <= PT_EDA) | ||
49 | ret = ((unsigned long *)user_regs(task->thread_info))[regno]; | ||
50 | else if (regno == PT_USP) | ||
51 | ret = task->thread.usp; | ||
52 | else if (regno == PT_PPC) | ||
53 | ret = get_pseudo_pc(task); | ||
54 | else if (regno <= PT_MAX) | ||
55 | ret = get_debugreg(task->pid, regno); | ||
56 | else | ||
57 | ret = 0; | ||
58 | |||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Write contents of register REGNO in task TASK. | ||
64 | */ | ||
65 | int put_reg(struct task_struct *task, unsigned int regno, unsigned long data) | ||
66 | { | ||
67 | if (regno <= PT_EDA) | ||
68 | ((unsigned long *)user_regs(task->thread_info))[regno] = data; | ||
69 | else if (regno == PT_USP) | ||
70 | task->thread.usp = data; | ||
71 | else if (regno == PT_PPC) { | ||
72 | /* Write pseudo-PC to ERP only if changed. */ | ||
73 | if (data != get_pseudo_pc(task)) | ||
74 | ((unsigned long *)user_regs(task->thread_info))[PT_ERP] = data; | ||
75 | } else if (regno <= PT_MAX) | ||
76 | return put_debugreg(task->pid, regno, data); | ||
77 | else | ||
78 | return -1; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Called by kernel/ptrace.c when detaching. | ||
84 | * | ||
85 | * Make sure the single step bit is not set. | ||
86 | */ | ||
87 | void | ||
88 | ptrace_disable(struct task_struct *child) | ||
89 | { | ||
90 | unsigned long tmp; | ||
91 | |||
92 | /* Deconfigure SPC and S-bit. */ | ||
93 | tmp = get_reg(child, PT_CCS) & ~SBIT_USER; | ||
94 | put_reg(child, PT_CCS, tmp); | ||
95 | put_reg(child, PT_SPC, 0); | ||
96 | |||
97 | /* Deconfigure any watchpoints associated with the child. */ | ||
98 | deconfigure_bp(child->pid); | ||
99 | } | ||
100 | |||
101 | |||
102 | asmlinkage int | ||
103 | sys_ptrace(long request, long pid, long addr, long data) | ||
104 | { | ||
105 | struct task_struct *child; | ||
106 | int ret; | ||
107 | unsigned long __user *datap = (unsigned long __user *)data; | ||
108 | |||
109 | lock_kernel(); | ||
110 | ret = -EPERM; | ||
111 | |||
112 | if (request == PTRACE_TRACEME) { | ||
113 | /* are we already being traced? */ | ||
114 | if (current->ptrace & PT_PTRACED) | ||
115 | goto out; | ||
116 | ret = security_ptrace(current->parent, current); | ||
117 | if (ret) | ||
118 | goto out; | ||
119 | /* set the ptrace bit in the process flags. */ | ||
120 | current->ptrace |= PT_PTRACED; | ||
121 | ret = 0; | ||
122 | goto out; | ||
123 | } | ||
124 | |||
125 | ret = -ESRCH; | ||
126 | read_lock(&tasklist_lock); | ||
127 | child = find_task_by_pid(pid); | ||
128 | |||
129 | if (child) | ||
130 | get_task_struct(child); | ||
131 | |||
132 | read_unlock(&tasklist_lock); | ||
133 | |||
134 | if (!child) | ||
135 | goto out; | ||
136 | |||
137 | ret = -EPERM; | ||
138 | |||
139 | if (pid == 1) /* Leave the init process alone! */ | ||
140 | goto out_tsk; | ||
141 | |||
142 | if (request == PTRACE_ATTACH) { | ||
143 | ret = ptrace_attach(child); | ||
144 | goto out_tsk; | ||
145 | } | ||
146 | |||
147 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
148 | if (ret < 0) | ||
149 | goto out_tsk; | ||
150 | |||
151 | switch (request) { | ||
152 | /* Read word at location address. */ | ||
153 | case PTRACE_PEEKTEXT: | ||
154 | case PTRACE_PEEKDATA: { | ||
155 | unsigned long tmp; | ||
156 | int copied; | ||
157 | |||
158 | ret = -EIO; | ||
159 | |||
160 | /* The signal trampoline page is outside the normal user-addressable | ||
161 | * space but still accessible. This is hack to make it possible to | ||
162 | * access the signal handler code in GDB. | ||
163 | */ | ||
164 | if ((addr & PAGE_MASK) == cris_signal_return_page) { | ||
165 | /* The trampoline page is globally mapped, no page table to traverse.*/ | ||
166 | tmp = *(unsigned long*)addr; | ||
167 | } else { | ||
168 | copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); | ||
169 | |||
170 | if (copied != sizeof(tmp)) | ||
171 | break; | ||
172 | } | ||
173 | |||
174 | ret = put_user(tmp,datap); | ||
175 | break; | ||
176 | } | ||
177 | |||
178 | /* Read the word at location address in the USER area. */ | ||
179 | case PTRACE_PEEKUSR: { | ||
180 | unsigned long tmp; | ||
181 | |||
182 | ret = -EIO; | ||
183 | if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) | ||
184 | break; | ||
185 | |||
186 | tmp = get_reg(child, addr >> 2); | ||
187 | ret = put_user(tmp, datap); | ||
188 | break; | ||
189 | } | ||
190 | |||
191 | /* Write the word at location address. */ | ||
192 | case PTRACE_POKETEXT: | ||
193 | case PTRACE_POKEDATA: | ||
194 | ret = 0; | ||
195 | |||
196 | if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) | ||
197 | break; | ||
198 | |||
199 | ret = -EIO; | ||
200 | break; | ||
201 | |||
202 | /* Write the word at location address in the USER area. */ | ||
203 | case PTRACE_POKEUSR: | ||
204 | ret = -EIO; | ||
205 | if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) | ||
206 | break; | ||
207 | |||
208 | addr >>= 2; | ||
209 | |||
210 | if (addr == PT_CCS) { | ||
211 | /* don't allow the tracing process to change stuff like | ||
212 | * interrupt enable, kernel/user bit, dma enables etc. | ||
213 | */ | ||
214 | data &= CCS_MASK; | ||
215 | data |= get_reg(child, PT_CCS) & ~CCS_MASK; | ||
216 | } | ||
217 | if (put_reg(child, addr, data)) | ||
218 | break; | ||
219 | ret = 0; | ||
220 | break; | ||
221 | |||
222 | case PTRACE_SYSCALL: | ||
223 | case PTRACE_CONT: | ||
224 | ret = -EIO; | ||
225 | |||
226 | if (!valid_signal(data)) | ||
227 | break; | ||
228 | |||
229 | /* Continue means no single-step. */ | ||
230 | put_reg(child, PT_SPC, 0); | ||
231 | |||
232 | if (!get_debugreg(child->pid, PT_BP_CTRL)) { | ||
233 | unsigned long tmp; | ||
234 | /* If no h/w bp configured, disable S bit. */ | ||
235 | tmp = get_reg(child, PT_CCS) & ~SBIT_USER; | ||
236 | put_reg(child, PT_CCS, tmp); | ||
237 | } | ||
238 | |||
239 | if (request == PTRACE_SYSCALL) { | ||
240 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
241 | } | ||
242 | else { | ||
243 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
244 | } | ||
245 | |||
246 | child->exit_code = data; | ||
247 | |||
248 | /* TODO: make sure any pending breakpoint is killed */ | ||
249 | wake_up_process(child); | ||
250 | ret = 0; | ||
251 | |||
252 | break; | ||
253 | |||
254 | /* Make the child exit by sending it a sigkill. */ | ||
255 | case PTRACE_KILL: | ||
256 | ret = 0; | ||
257 | |||
258 | if (child->exit_state == EXIT_ZOMBIE) | ||
259 | break; | ||
260 | |||
261 | child->exit_code = SIGKILL; | ||
262 | |||
263 | /* Deconfigure single-step and h/w bp. */ | ||
264 | ptrace_disable(child); | ||
265 | |||
266 | /* TODO: make sure any pending breakpoint is killed */ | ||
267 | wake_up_process(child); | ||
268 | break; | ||
269 | |||
270 | /* Set the trap flag. */ | ||
271 | case PTRACE_SINGLESTEP: { | ||
272 | unsigned long tmp; | ||
273 | ret = -EIO; | ||
274 | |||
275 | /* Set up SPC if not set already (in which case we have | ||
276 | no other choice but to trust it). */ | ||
277 | if (!get_reg(child, PT_SPC)) { | ||
278 | /* In case we're stopped in a delay slot. */ | ||
279 | tmp = get_reg(child, PT_ERP) & ~1; | ||
280 | put_reg(child, PT_SPC, tmp); | ||
281 | } | ||
282 | tmp = get_reg(child, PT_CCS) | SBIT_USER; | ||
283 | put_reg(child, PT_CCS, tmp); | ||
284 | |||
285 | if (!valid_signal(data)) | ||
286 | break; | ||
287 | |||
288 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
289 | |||
290 | /* TODO: set some clever breakpoint mechanism... */ | ||
291 | |||
292 | child->exit_code = data; | ||
293 | wake_up_process(child); | ||
294 | ret = 0; | ||
295 | break; | ||
296 | |||
297 | } | ||
298 | case PTRACE_DETACH: | ||
299 | ret = ptrace_detach(child, data); | ||
300 | break; | ||
301 | |||
302 | /* Get all GP registers from the child. */ | ||
303 | case PTRACE_GETREGS: { | ||
304 | int i; | ||
305 | unsigned long tmp; | ||
306 | |||
307 | for (i = 0; i <= PT_MAX; i++) { | ||
308 | tmp = get_reg(child, i); | ||
309 | |||
310 | if (put_user(tmp, datap)) { | ||
311 | ret = -EFAULT; | ||
312 | goto out_tsk; | ||
313 | } | ||
314 | |||
315 | datap++; | ||
316 | } | ||
317 | |||
318 | ret = 0; | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | /* Set all GP registers in the child. */ | ||
323 | case PTRACE_SETREGS: { | ||
324 | int i; | ||
325 | unsigned long tmp; | ||
326 | |||
327 | for (i = 0; i <= PT_MAX; i++) { | ||
328 | if (get_user(tmp, datap)) { | ||
329 | ret = -EFAULT; | ||
330 | goto out_tsk; | ||
331 | } | ||
332 | |||
333 | if (i == PT_CCS) { | ||
334 | tmp &= CCS_MASK; | ||
335 | tmp |= get_reg(child, PT_CCS) & ~CCS_MASK; | ||
336 | } | ||
337 | |||
338 | put_reg(child, i, tmp); | ||
339 | datap++; | ||
340 | } | ||
341 | |||
342 | ret = 0; | ||
343 | break; | ||
344 | } | ||
345 | |||
346 | default: | ||
347 | ret = ptrace_request(child, request, addr, data); | ||
348 | break; | ||
349 | } | ||
350 | out_tsk: | ||
351 | put_task_struct(child); | ||
352 | out: | ||
353 | unlock_kernel(); | ||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | void do_syscall_trace(void) | ||
358 | { | ||
359 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
360 | return; | ||
361 | |||
362 | if (!(current->ptrace & PT_PTRACED)) | ||
363 | return; | ||
364 | |||
365 | /* the 0x80 provides a way for the tracing parent to distinguish | ||
366 | between a syscall stop and SIGTRAP delivery */ | ||
367 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
368 | ? 0x80 : 0)); | ||
369 | |||
370 | /* | ||
371 | * This isn't the same as continuing with a signal, but it will do for | ||
372 | * normal use. | ||
373 | */ | ||
374 | if (current->exit_code) { | ||
375 | send_sig(current->exit_code, current, 1); | ||
376 | current->exit_code = 0; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | /* Returns the size of an instruction that has a delay slot. */ | ||
381 | |||
382 | static int insn_size(struct task_struct *child, unsigned long pc) | ||
383 | { | ||
384 | unsigned long opcode; | ||
385 | int copied; | ||
386 | int opsize = 0; | ||
387 | |||
388 | /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */ | ||
389 | copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0); | ||
390 | if (copied != sizeof(opcode)) | ||
391 | return 0; | ||
392 | |||
393 | switch ((opcode & 0x0f00) >> 8) { | ||
394 | case 0x0: | ||
395 | case 0x9: | ||
396 | case 0xb: | ||
397 | opsize = 2; | ||
398 | break; | ||
399 | case 0xe: | ||
400 | case 0xf: | ||
401 | opsize = 6; | ||
402 | break; | ||
403 | case 0xd: | ||
404 | /* Could be 4 or 6; check more bits. */ | ||
405 | if ((opcode & 0xff) == 0xff) | ||
406 | opsize = 4; | ||
407 | else | ||
408 | opsize = 6; | ||
409 | break; | ||
410 | default: | ||
411 | panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n", | ||
412 | opcode, pc); | ||
413 | } | ||
414 | |||
415 | return opsize; | ||
416 | } | ||
417 | |||
418 | static unsigned long get_pseudo_pc(struct task_struct *child) | ||
419 | { | ||
420 | /* Default value for PC is ERP. */ | ||
421 | unsigned long pc = get_reg(child, PT_ERP); | ||
422 | |||
423 | if (pc & 0x1) { | ||
424 | unsigned long spc = get_reg(child, PT_SPC); | ||
425 | /* Delay slot bit set. Report as stopped on proper | ||
426 | instruction. */ | ||
427 | if (spc) { | ||
428 | /* Rely on SPC if set. FIXME: We might want to check | ||
429 | that EXS indicates we stopped due to a single-step | ||
430 | exception. */ | ||
431 | pc = spc; | ||
432 | } else { | ||
433 | /* Calculate the PC from the size of the instruction | ||
434 | that the delay slot we're in belongs to. */ | ||
435 | pc += insn_size(child, pc & ~1) - 1; | ||
436 | } | ||
437 | } | ||
438 | return pc; | ||
439 | } | ||
440 | |||
441 | static long bp_owner = 0; | ||
442 | |||
443 | /* Reachable from exit_thread in signal.c, so not static. */ | ||
444 | void deconfigure_bp(long pid) | ||
445 | { | ||
446 | int bp; | ||
447 | |||
448 | /* Only deconfigure if the pid is the owner. */ | ||
449 | if (bp_owner != pid) | ||
450 | return; | ||
451 | |||
452 | for (bp = 0; bp < 6; bp++) { | ||
453 | unsigned long tmp; | ||
454 | /* Deconfigure start and end address (also gets rid of ownership). */ | ||
455 | put_debugreg(pid, PT_BP + 3 + (bp * 2), 0); | ||
456 | put_debugreg(pid, PT_BP + 4 + (bp * 2), 0); | ||
457 | |||
458 | /* Deconfigure relevant bits in control register. */ | ||
459 | tmp = get_debugreg(pid, PT_BP_CTRL) & ~(3 << (2 + (bp * 4))); | ||
460 | put_debugreg(pid, PT_BP_CTRL, tmp); | ||
461 | } | ||
462 | /* No owner now. */ | ||
463 | bp_owner = 0; | ||
464 | } | ||
465 | |||
466 | static int put_debugreg(long pid, unsigned int regno, long data) | ||
467 | { | ||
468 | int ret = 0; | ||
469 | register int old_srs; | ||
470 | |||
471 | #ifdef CONFIG_ETRAX_KGDB | ||
472 | /* Ignore write, but pretend it was ok if value is 0 | ||
473 | (we don't want POKEUSR/SETREGS failing unnessecarily). */ | ||
474 | return (data == 0) ? ret : -1; | ||
475 | #endif | ||
476 | |||
477 | /* Simple owner management. */ | ||
478 | if (!bp_owner) | ||
479 | bp_owner = pid; | ||
480 | else if (bp_owner != pid) { | ||
481 | /* Ignore write, but pretend it was ok if value is 0 | ||
482 | (we don't want POKEUSR/SETREGS failing unnessecarily). */ | ||
483 | return (data == 0) ? ret : -1; | ||
484 | } | ||
485 | |||
486 | /* Remember old SRS. */ | ||
487 | SPEC_REG_RD(SPEC_REG_SRS, old_srs); | ||
488 | /* Switch to BP bank. */ | ||
489 | SUPP_BANK_SEL(BANK_BP); | ||
490 | |||
491 | switch (regno - PT_BP) { | ||
492 | case 0: | ||
493 | SUPP_REG_WR(0, data); break; | ||
494 | case 1: | ||
495 | case 2: | ||
496 | if (data) | ||
497 | ret = -1; | ||
498 | break; | ||
499 | case 3: | ||
500 | SUPP_REG_WR(3, data); break; | ||
501 | case 4: | ||
502 | SUPP_REG_WR(4, data); break; | ||
503 | case 5: | ||
504 | SUPP_REG_WR(5, data); break; | ||
505 | case 6: | ||
506 | SUPP_REG_WR(6, data); break; | ||
507 | case 7: | ||
508 | SUPP_REG_WR(7, data); break; | ||
509 | case 8: | ||
510 | SUPP_REG_WR(8, data); break; | ||
511 | case 9: | ||
512 | SUPP_REG_WR(9, data); break; | ||
513 | case 10: | ||
514 | SUPP_REG_WR(10, data); break; | ||
515 | case 11: | ||
516 | SUPP_REG_WR(11, data); break; | ||
517 | case 12: | ||
518 | SUPP_REG_WR(12, data); break; | ||
519 | case 13: | ||
520 | SUPP_REG_WR(13, data); break; | ||
521 | case 14: | ||
522 | SUPP_REG_WR(14, data); break; | ||
523 | default: | ||
524 | ret = -1; | ||
525 | break; | ||
526 | } | ||
527 | |||
528 | /* Restore SRS. */ | ||
529 | SPEC_REG_WR(SPEC_REG_SRS, old_srs); | ||
530 | /* Just for show. */ | ||
531 | NOP(); | ||
532 | NOP(); | ||
533 | NOP(); | ||
534 | |||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | static long get_debugreg(long pid, unsigned int regno) | ||
539 | { | ||
540 | register int old_srs; | ||
541 | register long data; | ||
542 | |||
543 | if (pid != bp_owner) { | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | /* Remember old SRS. */ | ||
548 | SPEC_REG_RD(SPEC_REG_SRS, old_srs); | ||
549 | /* Switch to BP bank. */ | ||
550 | SUPP_BANK_SEL(BANK_BP); | ||
551 | |||
552 | switch (regno - PT_BP) { | ||
553 | case 0: | ||
554 | SUPP_REG_RD(0, data); break; | ||
555 | case 1: | ||
556 | case 2: | ||
557 | /* error return value? */ | ||
558 | data = 0; | ||
559 | break; | ||
560 | case 3: | ||
561 | SUPP_REG_RD(3, data); break; | ||
562 | case 4: | ||
563 | SUPP_REG_RD(4, data); break; | ||
564 | case 5: | ||
565 | SUPP_REG_RD(5, data); break; | ||
566 | case 6: | ||
567 | SUPP_REG_RD(6, data); break; | ||
568 | case 7: | ||
569 | SUPP_REG_RD(7, data); break; | ||
570 | case 8: | ||
571 | SUPP_REG_RD(8, data); break; | ||
572 | case 9: | ||
573 | SUPP_REG_RD(9, data); break; | ||
574 | case 10: | ||
575 | SUPP_REG_RD(10, data); break; | ||
576 | case 11: | ||
577 | SUPP_REG_RD(11, data); break; | ||
578 | case 12: | ||
579 | SUPP_REG_RD(12, data); break; | ||
580 | case 13: | ||
581 | SUPP_REG_RD(13, data); break; | ||
582 | case 14: | ||
583 | SUPP_REG_RD(14, data); break; | ||
584 | default: | ||
585 | /* error return value? */ | ||
586 | data = 0; | ||
587 | } | ||
588 | |||
589 | /* Restore SRS. */ | ||
590 | SPEC_REG_WR(SPEC_REG_SRS, old_srs); | ||
591 | /* Just for show. */ | ||
592 | NOP(); | ||
593 | NOP(); | ||
594 | NOP(); | ||
595 | |||
596 | return data; | ||
597 | } | ||