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 | |
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>
-rw-r--r-- | arch/mips/kernel/ptrace.c | 129 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace32.c | 81 | ||||
-rw-r--r-- | arch/mips/kernel/scall64-n32.S | 2 | ||||
-rw-r--r-- | include/asm-mips/ptrace.h | 15 |
4 files changed, 222 insertions, 5 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; |
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 5c45a5880226..c28cdddd4c21 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c | |||
@@ -35,6 +35,12 @@ | |||
35 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
36 | #include <asm/bootinfo.h> | 36 | #include <asm/bootinfo.h> |
37 | 37 | ||
38 | int ptrace_getregs (struct task_struct *child, __s64 __user *data); | ||
39 | int ptrace_setregs (struct task_struct *child, __s64 __user *data); | ||
40 | |||
41 | int ptrace_getfpregs (struct task_struct *child, __u32 __user *data); | ||
42 | int ptrace_setfpregs (struct task_struct *child, __u32 __user *data); | ||
43 | |||
38 | /* | 44 | /* |
39 | * Tracing a 32-bit process with a 64-bit strace and vice versa will not | 45 | * Tracing a 32-bit process with a 64-bit strace and vice versa will not |
40 | * work. I don't know how to fix this. | 46 | * work. I don't know how to fix this. |
@@ -99,6 +105,35 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) | |||
99 | break; | 105 | break; |
100 | } | 106 | } |
101 | 107 | ||
108 | /* | ||
109 | * Read 4 bytes of the other process' storage | ||
110 | * data is a pointer specifying where the user wants the | ||
111 | * 4 bytes copied into | ||
112 | * addr is a pointer in the user's storage that contains an 8 byte | ||
113 | * address in the other process of the 4 bytes that is to be read | ||
114 | * (this is run in a 32-bit process looking at a 64-bit process) | ||
115 | * when I and D space are separate, these will need to be fixed. | ||
116 | */ | ||
117 | case PTRACE_PEEKTEXT_3264: | ||
118 | case PTRACE_PEEKDATA_3264: { | ||
119 | u32 tmp; | ||
120 | int copied; | ||
121 | u32 __user * addrOthers; | ||
122 | |||
123 | ret = -EIO; | ||
124 | |||
125 | /* Get the addr in the other process that we want to read */ | ||
126 | if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) | ||
127 | break; | ||
128 | |||
129 | copied = access_process_vm(child, (u64)addrOthers, &tmp, | ||
130 | sizeof(tmp), 0); | ||
131 | if (copied != sizeof(tmp)) | ||
132 | break; | ||
133 | ret = put_user(tmp, (u32 __user *) (unsigned long) data); | ||
134 | break; | ||
135 | } | ||
136 | |||
102 | /* Read the word at location addr in the USER area. */ | 137 | /* Read the word at location addr in the USER area. */ |
103 | case PTRACE_PEEKUSR: { | 138 | case PTRACE_PEEKUSR: { |
104 | struct pt_regs *regs; | 139 | struct pt_regs *regs; |
@@ -202,6 +237,31 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) | |||
202 | ret = -EIO; | 237 | ret = -EIO; |
203 | break; | 238 | break; |
204 | 239 | ||
240 | /* | ||
241 | * Write 4 bytes into the other process' storage | ||
242 | * data is the 4 bytes that the user wants written | ||
243 | * addr is a pointer in the user's storage that contains an | ||
244 | * 8 byte address in the other process where the 4 bytes | ||
245 | * that is to be written | ||
246 | * (this is run in a 32-bit process looking at a 64-bit process) | ||
247 | * when I and D space are separate, these will need to be fixed. | ||
248 | */ | ||
249 | case PTRACE_POKETEXT_3264: | ||
250 | case PTRACE_POKEDATA_3264: { | ||
251 | u32 __user * addrOthers; | ||
252 | |||
253 | /* Get the addr in the other process that we want to write into */ | ||
254 | ret = -EIO; | ||
255 | if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0) | ||
256 | break; | ||
257 | ret = 0; | ||
258 | if (access_process_vm(child, (u64)addrOthers, &data, | ||
259 | sizeof(data), 1) == sizeof(data)) | ||
260 | break; | ||
261 | ret = -EIO; | ||
262 | break; | ||
263 | } | ||
264 | |||
205 | case PTRACE_POKEUSR: { | 265 | case PTRACE_POKEUSR: { |
206 | struct pt_regs *regs; | 266 | struct pt_regs *regs; |
207 | ret = 0; | 267 | ret = 0; |
@@ -276,6 +336,22 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) | |||
276 | break; | 336 | break; |
277 | } | 337 | } |
278 | 338 | ||
339 | case PTRACE_GETREGS: | ||
340 | ret = ptrace_getregs (child, (__u64 __user *) (__u64) data); | ||
341 | break; | ||
342 | |||
343 | case PTRACE_SETREGS: | ||
344 | ret = ptrace_setregs (child, (__u64 __user *) (__u64) data); | ||
345 | break; | ||
346 | |||
347 | case PTRACE_GETFPREGS: | ||
348 | ret = ptrace_getfpregs (child, (__u32 __user *) (__u64) data); | ||
349 | break; | ||
350 | |||
351 | case PTRACE_SETFPREGS: | ||
352 | ret = ptrace_setfpregs (child, (__u32 __user *) (__u64) data); | ||
353 | break; | ||
354 | |||
279 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | 355 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ |
280 | case PTRACE_CONT: { /* restart after signal. */ | 356 | case PTRACE_CONT: { /* restart after signal. */ |
281 | ret = -EIO; | 357 | ret = -EIO; |
@@ -320,6 +396,11 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) | |||
320 | (unsigned int __user *) (unsigned long) data); | 396 | (unsigned int __user *) (unsigned long) data); |
321 | break; | 397 | break; |
322 | 398 | ||
399 | case PTRACE_GET_THREAD_AREA_3264: | ||
400 | ret = put_user(child->thread_info->tp_value, | ||
401 | (unsigned long __user *) (unsigned long) data); | ||
402 | break; | ||
403 | |||
323 | default: | 404 | default: |
324 | ret = ptrace_request(child, request, addr, data); | 405 | ret = ptrace_request(child, request, addr, data); |
325 | break; | 406 | break; |
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index b32c566e0a24..7e66eb823bf6 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S | |||
@@ -216,7 +216,7 @@ EXPORT(sysn32_call_table) | |||
216 | PTR compat_sys_getrusage | 216 | PTR compat_sys_getrusage |
217 | PTR sys32_sysinfo | 217 | PTR sys32_sysinfo |
218 | PTR compat_sys_times | 218 | PTR compat_sys_times |
219 | PTR sys_ptrace | 219 | PTR sys32_ptrace |
220 | PTR sys_getuid /* 6100 */ | 220 | PTR sys_getuid /* 6100 */ |
221 | PTR sys_syslog | 221 | PTR sys_syslog |
222 | PTR sys_getgid | 222 | PTR sys_getgid |
diff --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h index 8441a5ae96e3..95c5839ac465 100644 --- a/include/asm-mips/ptrace.h +++ b/include/asm-mips/ptrace.h | |||
@@ -48,10 +48,10 @@ struct pt_regs { | |||
48 | }; | 48 | }; |
49 | 49 | ||
50 | /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ | 50 | /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ |
51 | /* #define PTRACE_GETREGS 12 */ | 51 | #define PTRACE_GETREGS 12 |
52 | /* #define PTRACE_SETREGS 13 */ | 52 | #define PTRACE_SETREGS 13 |
53 | /* #define PTRACE_GETFPREGS 14 */ | 53 | #define PTRACE_GETFPREGS 14 |
54 | /* #define PTRACE_SETFPREGS 15 */ | 54 | #define PTRACE_SETFPREGS 15 |
55 | /* #define PTRACE_GETFPXREGS 18 */ | 55 | /* #define PTRACE_GETFPXREGS 18 */ |
56 | /* #define PTRACE_SETFPXREGS 19 */ | 56 | /* #define PTRACE_SETFPXREGS 19 */ |
57 | 57 | ||
@@ -60,6 +60,13 @@ struct pt_regs { | |||
60 | #define PTRACE_GET_THREAD_AREA 25 | 60 | #define PTRACE_GET_THREAD_AREA 25 |
61 | #define PTRACE_SET_THREAD_AREA 26 | 61 | #define PTRACE_SET_THREAD_AREA 26 |
62 | 62 | ||
63 | /* Calls to trace a 64bit program from a 32bit program. */ | ||
64 | #define PTRACE_PEEKTEXT_3264 0xc0 | ||
65 | #define PTRACE_PEEKDATA_3264 0xc1 | ||
66 | #define PTRACE_POKETEXT_3264 0xc2 | ||
67 | #define PTRACE_POKEDATA_3264 0xc3 | ||
68 | #define PTRACE_GET_THREAD_AREA_3264 0xc4 | ||
69 | |||
63 | #ifdef __KERNEL__ | 70 | #ifdef __KERNEL__ |
64 | 71 | ||
65 | #include <linux/linkage.h> | 72 | #include <linux/linkage.h> |