blob: 0f6eea086a2372205ea53b9842fb734e1eb85896 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Paul Mackerrasb1239232005-10-20 09:11:29 +10002 * ptrace for 32-bit processes running on a 64-bit kernel.
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * PowerPC version
5 * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
6 *
7 * Derived from "arch/m68k/kernel/ptrace.c"
8 * Copyright (C) 1994 by Hamish Macdonald
9 * Taken from linux/kernel/ptrace.c and modified for M680x0.
10 * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
11 *
12 * Modified by Cort Dougan (cort@hq.fsmlabs.com)
Paul Mackerrasb1239232005-10-20 09:11:29 +100013 * and Paul Mackerras (paulus@samba.org).
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 *
15 * This file is subject to the terms and conditions of the GNU General
Paul Mackerrasb1239232005-10-20 09:11:29 +100016 * Public License. See the file COPYING in the main directory of
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 * this archive for more details.
18 */
19
20#include <linux/kernel.h>
21#include <linux/sched.h>
22#include <linux/mm.h>
23#include <linux/smp.h>
24#include <linux/smp_lock.h>
25#include <linux/errno.h>
26#include <linux/ptrace.h>
27#include <linux/user.h>
28#include <linux/security.h>
Jesper Juhl7ed20e12005-05-01 08:59:14 -070029#include <linux/signal.h>
Roland McGrath1d48d712007-12-20 03:58:49 -080030#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include <asm/uaccess.h>
33#include <asm/page.h>
34#include <asm/pgtable.h>
35#include <asm/system.h>
Paul Mackerras21a62902005-11-19 20:47:22 +110036
Linus Torvalds1da177e2005-04-16 15:20:36 -070037/*
38 * does not yet catch signals sent when the child dies.
39 * in exit.c or in signal.c.
40 */
41
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100042/*
43 * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
44 * we mark them as obsolete now, they will be removed in a future version
45 */
46static long compat_ptrace_old(struct task_struct *child, long request,
47 long addr, long data)
48{
49 int ret = -EPERM;
50
51 switch(request) {
52 case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
53 int i;
54 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
55 unsigned int __user *tmp = (unsigned int __user *)addr;
56
Roland McGrathfabca2c2007-09-25 09:50:52 +100057 CHECK_FULL_REGS(child->thread.regs);
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100058 for (i = 0; i < 32; i++) {
59 ret = put_user(*reg, tmp);
60 if (ret)
61 break;
62 reg++;
63 tmp++;
64 }
65 break;
66 }
67
68 case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
69 int i;
70 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
71 unsigned int __user *tmp = (unsigned int __user *)addr;
72
Roland McGrathfabca2c2007-09-25 09:50:52 +100073 CHECK_FULL_REGS(child->thread.regs);
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100074 for (i = 0; i < 32; i++) {
75 ret = get_user(*reg, tmp);
76 if (ret)
77 break;
78 reg++;
79 tmp++;
80 }
81 break;
82 }
83
84 }
85 return ret;
86}
87
Roland McGrath81e695c2007-12-20 03:58:55 -080088long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
89 compat_ulong_t caddr, compat_ulong_t cdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
Roland McGrath81e695c2007-12-20 03:58:55 -080091 unsigned long addr = caddr;
92 unsigned long data = cdata;
Christoph Hellwig6b9c7ed2006-01-08 01:02:33 -080093 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 switch (request) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 /*
97 * Read 4 bytes of the other process' storage
98 * data is a pointer specifying where the user wants the
99 * 4 bytes copied into
100 * addr is a pointer in the user's storage that contains an 8 byte
101 * address in the other process of the 4 bytes that is to be read
102 * (this is run in a 32-bit process looking at a 64-bit process)
103 * when I and D space are separate, these will need to be fixed.
104 */
105 case PPC_PTRACE_PEEKTEXT_3264:
106 case PPC_PTRACE_PEEKDATA_3264: {
107 u32 tmp;
108 int copied;
109 u32 __user * addrOthers;
110
111 ret = -EIO;
112
113 /* Get the addr in the other process that we want to read */
114 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
115 break;
116
117 copied = access_process_vm(child, (u64)addrOthers, &tmp,
118 sizeof(tmp), 0);
119 if (copied != sizeof(tmp))
120 break;
121 ret = put_user(tmp, (u32 __user *)data);
122 break;
123 }
124
125 /* Read a register (specified by ADDR) out of the "user area" */
126 case PTRACE_PEEKUSR: {
127 int index;
128 unsigned long tmp;
129
130 ret = -EIO;
131 /* convert to index and check */
132 index = (unsigned long) addr >> 2;
133 if ((addr & 3) || (index > PT_FPSCR32))
134 break;
135
Roland McGrathfabca2c2007-09-25 09:50:52 +1000136 CHECK_FULL_REGS(child->thread.regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 if (index < PT_FPR0) {
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000138 tmp = ptrace_get_reg(child, index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 } else {
140 flush_fp_to_thread(child);
141 /*
142 * the user space code considers the floating point
143 * to be an array of unsigned int (32 bits) - the
144 * index passed in is based on this assumption.
145 */
146 tmp = ((unsigned int *)child->thread.fpr)[index - PT_FPR0];
147 }
148 ret = put_user((unsigned int)tmp, (u32 __user *)data);
149 break;
150 }
151
152 /*
153 * Read 4 bytes out of the other process' pt_regs area
154 * data is a pointer specifying where the user wants the
155 * 4 bytes copied into
156 * addr is the offset into the other process' pt_regs structure
157 * that is to be read
158 * (this is run in a 32-bit process looking at a 64-bit process)
159 */
160 case PPC_PTRACE_PEEKUSR_3264: {
161 u32 index;
162 u32 reg32bits;
163 u64 tmp;
164 u32 numReg;
165 u32 part;
166
167 ret = -EIO;
168 /* Determine which register the user wants */
169 index = (u64)addr >> 2;
170 numReg = index / 2;
171 /* Determine which part of the register the user wants */
172 if (index % 2)
173 part = 1; /* want the 2nd half of the register (right-most). */
174 else
175 part = 0; /* want the 1st half of the register (left-most). */
176
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000177 /* Validate the input - check to see if address is on the wrong boundary
178 * or beyond the end of the user area
179 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 if ((addr & 3) || numReg > PT_FPSCR)
181 break;
182
Roland McGrathfabca2c2007-09-25 09:50:52 +1000183 CHECK_FULL_REGS(child->thread.regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 if (numReg >= PT_FPR0) {
185 flush_fp_to_thread(child);
186 tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0];
187 } else { /* register within PT_REGS struct */
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000188 tmp = ptrace_get_reg(child, numReg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
190 reg32bits = ((u32*)&tmp)[part];
191 ret = put_user(reg32bits, (u32 __user *)data);
192 break;
193 }
194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 /*
196 * Write 4 bytes into the other process' storage
197 * data is the 4 bytes that the user wants written
198 * addr is a pointer in the user's storage that contains an
199 * 8 byte address in the other process where the 4 bytes
200 * that is to be written
201 * (this is run in a 32-bit process looking at a 64-bit process)
202 * when I and D space are separate, these will need to be fixed.
203 */
204 case PPC_PTRACE_POKETEXT_3264:
205 case PPC_PTRACE_POKEDATA_3264: {
206 u32 tmp = data;
207 u32 __user * addrOthers;
208
209 /* Get the addr in the other process that we want to write into */
210 ret = -EIO;
211 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
212 break;
213 ret = 0;
214 if (access_process_vm(child, (u64)addrOthers, &tmp,
215 sizeof(tmp), 1) == sizeof(tmp))
216 break;
217 ret = -EIO;
218 break;
219 }
220
221 /* write the word at location addr in the USER area */
222 case PTRACE_POKEUSR: {
223 unsigned long index;
224
225 ret = -EIO;
226 /* convert to index and check */
227 index = (unsigned long) addr >> 2;
228 if ((addr & 3) || (index > PT_FPSCR32))
229 break;
230
Roland McGrathfabca2c2007-09-25 09:50:52 +1000231 CHECK_FULL_REGS(child->thread.regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 if (index < PT_FPR0) {
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000233 ret = ptrace_put_reg(child, index, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 } else {
235 flush_fp_to_thread(child);
236 /*
237 * the user space code considers the floating point
238 * to be an array of unsigned int (32 bits) - the
239 * index passed in is based on this assumption.
240 */
241 ((unsigned int *)child->thread.fpr)[index - PT_FPR0] = data;
242 ret = 0;
243 }
244 break;
245 }
246
247 /*
248 * Write 4 bytes into the other process' pt_regs area
249 * data is the 4 bytes that the user wants written
250 * addr is the offset into the other process' pt_regs structure
251 * that is to be written into
252 * (this is run in a 32-bit process looking at a 64-bit process)
253 */
254 case PPC_PTRACE_POKEUSR_3264: {
255 u32 index;
256 u32 numReg;
257
258 ret = -EIO;
259 /* Determine which register the user wants */
260 index = (u64)addr >> 2;
261 numReg = index / 2;
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 /*
264 * Validate the input - check to see if address is on the
265 * wrong boundary or beyond the end of the user area
266 */
267 if ((addr & 3) || (numReg > PT_FPSCR))
268 break;
Roland McGrathfabca2c2007-09-25 09:50:52 +1000269 CHECK_FULL_REGS(child->thread.regs);
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000270 if (numReg < PT_FPR0) {
271 unsigned long freg = ptrace_get_reg(child, numReg);
272 if (index % 2)
273 freg = (freg & ~0xfffffffful) | (data & 0xfffffffful);
274 else
275 freg = (freg & 0xfffffffful) | (data << 32);
276 ret = ptrace_put_reg(child, numReg, freg);
277 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 flush_fp_to_thread(child);
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000279 ((unsigned int *)child->thread.regs)[index] = data;
280 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 break;
283 }
284
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000285 case PTRACE_GET_DEBUGREG: {
286 ret = -EINVAL;
287 /* We only support one DABR and no IABRS at the moment */
288 if (addr > 0)
289 break;
290 ret = put_user(child->thread.dabr, (u32 __user *)data);
291 break;
292 }
293
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000294 case PTRACE_GETREGS: { /* Get all pt_regs from the child. */
295 int ui;
296 if (!access_ok(VERIFY_WRITE, (void __user *)data,
297 PT_REGS_COUNT * sizeof(int))) {
298 ret = -EIO;
299 break;
300 }
Roland McGrathfabca2c2007-09-25 09:50:52 +1000301 CHECK_FULL_REGS(child->thread.regs);
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000302 ret = 0;
303 for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000304 ret |= __put_user(ptrace_get_reg(child, ui),
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000305 (unsigned int __user *) data);
306 data += sizeof(int);
307 }
308 break;
309 }
310
311 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
312 unsigned long tmp;
313 int ui;
314 if (!access_ok(VERIFY_READ, (void __user *)data,
315 PT_REGS_COUNT * sizeof(int))) {
316 ret = -EIO;
317 break;
318 }
Roland McGrathfabca2c2007-09-25 09:50:52 +1000319 CHECK_FULL_REGS(child->thread.regs);
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000320 ret = 0;
321 for (ui = 0; ui < PT_REGS_COUNT; ui ++) {
322 ret = __get_user(tmp, (unsigned int __user *) data);
323 if (ret)
324 break;
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000325 ptrace_put_reg(child, ui, tmp);
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000326 data += sizeof(int);
327 }
328 break;
329 }
330
331 case PTRACE_GETFPREGS:
332 case PTRACE_SETFPREGS:
Robert Jennings962bca72005-09-10 16:01:07 +1000333 case PTRACE_GETVRREGS:
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000334 case PTRACE_SETVRREGS:
335 case PTRACE_GETREGS64:
336 case PTRACE_SETREGS64:
337 case PPC_PTRACE_GETFPREGS:
338 case PPC_PTRACE_SETFPREGS:
339 case PTRACE_KILL:
340 case PTRACE_SINGLESTEP:
341 case PTRACE_DETACH:
342 case PTRACE_SET_DEBUGREG:
343 case PTRACE_SYSCALL:
344 case PTRACE_CONT:
345 ret = arch_ptrace(child, request, addr, data);
Robert Jennings962bca72005-09-10 16:01:07 +1000346 break;
347
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000348 /* Old reverse args ptrace callss */
349 case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
350 case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
351 ret = compat_ptrace_old(child, request, addr, data);
Robert Jennings962bca72005-09-10 16:01:07 +1000352 break;
Robert Jennings962bca72005-09-10 16:01:07 +1000353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 default:
Roland McGrath1d48d712007-12-20 03:58:49 -0800355 ret = compat_ptrace_request(child, request, addr, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 break;
357 }
Roland McGrath81e695c2007-12-20 03:58:55 -0800358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 return ret;
360}