diff options
author | Daniel Jacobowitz <dan@debian.org> | 2005-09-28 18:11:15 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2005-10-29 14:32:29 -0400 |
commit | ea3d710fe572f0af4d242701973f7363b2146429 (patch) | |
tree | b8c7138296d4e19b7101b51bfeb6677d27185bc7 /arch/mips/kernel/ptrace.c | |
parent | 9043f7e95d104795fcb03a2f762524babcd49da5 (diff) |
Revise MIPS 64-bit ptrace interface
Change the N32 debugging ABI to something more sane, and add support
for o32 and n32 debuggers to trace n64 programs.
Signed-off-by: Daniel Jacobowitz <dan@codesourcery.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/ptrace.c')
-rw-r--r-- | arch/mips/kernel/ptrace.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 74283369a1e3..122433f835e3 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <asm/system.h> | 38 | #include <asm/system.h> |
39 | #include <asm/uaccess.h> | 39 | #include <asm/uaccess.h> |
40 | #include <asm/bootinfo.h> | 40 | #include <asm/bootinfo.h> |
41 | #include <asm/reg.h> | ||
41 | 42 | ||
42 | /* | 43 | /* |
43 | * Called by kernel/ptrace.c when detaching.. | 44 | * Called by kernel/ptrace.c when detaching.. |
@@ -49,6 +50,118 @@ void ptrace_disable(struct task_struct *child) | |||
49 | /* Nothing to do.. */ | 50 | /* Nothing to do.. */ |
50 | } | 51 | } |
51 | 52 | ||
53 | /* | ||
54 | * Read a general register set. We always use the 64-bit format, even | ||
55 | * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. | ||
56 | * Registers are sign extended to fill the available space. | ||
57 | */ | ||
58 | int ptrace_getregs (struct task_struct *child, __s64 __user *data) | ||
59 | { | ||
60 | struct pt_regs *regs; | ||
61 | int i; | ||
62 | |||
63 | if (!access_ok(VERIFY_WRITE, data, 38 * 8)) | ||
64 | return -EIO; | ||
65 | |||
66 | regs = (struct pt_regs *) ((unsigned long) child->thread_info + | ||
67 | THREAD_SIZE - 32 - sizeof(struct pt_regs)); | ||
68 | |||
69 | for (i = 0; i < 32; i++) | ||
70 | __put_user (regs->regs[i], data + i); | ||
71 | __put_user (regs->lo, data + EF_LO - EF_R0); | ||
72 | __put_user (regs->hi, data + EF_HI - EF_R0); | ||
73 | __put_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0); | ||
74 | __put_user (regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0); | ||
75 | __put_user (regs->cp0_status, data + EF_CP0_STATUS - EF_R0); | ||
76 | __put_user (regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * Write a general register set. As for PTRACE_GETREGS, we always use | ||
83 | * the 64-bit format. On a 32-bit kernel only the lower order half | ||
84 | * (according to endianness) will be used. | ||
85 | */ | ||
86 | int ptrace_setregs (struct task_struct *child, __s64 __user *data) | ||
87 | { | ||
88 | struct pt_regs *regs; | ||
89 | int i; | ||
90 | |||
91 | if (!access_ok(VERIFY_READ, data, 38 * 8)) | ||
92 | return -EIO; | ||
93 | |||
94 | regs = (struct pt_regs *) ((unsigned long) child->thread_info + | ||
95 | THREAD_SIZE - 32 - sizeof(struct pt_regs)); | ||
96 | |||
97 | for (i = 0; i < 32; i++) | ||
98 | __get_user (regs->regs[i], data + i); | ||
99 | __get_user (regs->lo, data + EF_LO - EF_R0); | ||
100 | __get_user (regs->hi, data + EF_HI - EF_R0); | ||
101 | __get_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0); | ||
102 | |||
103 | /* badvaddr, status, and cause may not be written. */ | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | int ptrace_getfpregs (struct task_struct *child, __u32 __user *data) | ||
109 | { | ||
110 | int i; | ||
111 | |||
112 | if (!access_ok(VERIFY_WRITE, data, 33 * 8)) | ||
113 | return -EIO; | ||
114 | |||
115 | if (tsk_used_math(child)) { | ||
116 | fpureg_t *fregs = get_fpu_regs(child); | ||
117 | for (i = 0; i < 32; i++) | ||
118 | __put_user (fregs[i], i + (__u64 __user *) data); | ||
119 | } else { | ||
120 | for (i = 0; i < 32; i++) | ||
121 | __put_user ((__u64) -1, i + (__u64 __user *) data); | ||
122 | } | ||
123 | |||
124 | if (cpu_has_fpu) { | ||
125 | unsigned int flags, tmp; | ||
126 | |||
127 | __put_user (child->thread.fpu.hard.fcr31, data + 64); | ||
128 | |||
129 | flags = read_c0_status(); | ||
130 | __enable_fpu(); | ||
131 | __asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp)); | ||
132 | write_c0_status(flags); | ||
133 | __put_user (tmp, data + 65); | ||
134 | } else { | ||
135 | __put_user (child->thread.fpu.soft.fcr31, data + 64); | ||
136 | __put_user ((__u32) 0, data + 65); | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | int ptrace_setfpregs (struct task_struct *child, __u32 __user *data) | ||
143 | { | ||
144 | fpureg_t *fregs; | ||
145 | int i; | ||
146 | |||
147 | if (!access_ok(VERIFY_READ, data, 33 * 8)) | ||
148 | return -EIO; | ||
149 | |||
150 | fregs = get_fpu_regs(child); | ||
151 | |||
152 | for (i = 0; i < 32; i++) | ||
153 | __get_user (fregs[i], i + (__u64 __user *) data); | ||
154 | |||
155 | if (cpu_has_fpu) | ||
156 | __get_user (child->thread.fpu.hard.fcr31, data + 64); | ||
157 | else | ||
158 | __get_user (child->thread.fpu.soft.fcr31, data + 64); | ||
159 | |||
160 | /* FIR may not be written. */ | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
52 | asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | 165 | asmlinkage int sys_ptrace(long request, long pid, long addr, long data) |
53 | { | 166 | { |
54 | struct task_struct *child; | 167 | struct task_struct *child; |
@@ -300,6 +413,22 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | |||
300 | break; | 413 | break; |
301 | } | 414 | } |
302 | 415 | ||
416 | case PTRACE_GETREGS: | ||
417 | ret = ptrace_getregs (child, (__u64 __user *) data); | ||
418 | break; | ||
419 | |||
420 | case PTRACE_SETREGS: | ||
421 | ret = ptrace_setregs (child, (__u64 __user *) data); | ||
422 | break; | ||
423 | |||
424 | case PTRACE_GETFPREGS: | ||
425 | ret = ptrace_getfpregs (child, (__u32 __user *) data); | ||
426 | break; | ||
427 | |||
428 | case PTRACE_SETFPREGS: | ||
429 | ret = ptrace_setfpregs (child, (__u32 __user *) data); | ||
430 | break; | ||
431 | |||
303 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | 432 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ |
304 | case PTRACE_CONT: { /* restart after signal. */ | 433 | case PTRACE_CONT: { /* restart after signal. */ |
305 | ret = -EIO; | 434 | ret = -EIO; |