blob: 9d30e10970ac5b12d522fe30834f270ddf560571 [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>
Roland McGrath0deef2c2007-12-20 03:59:04 -080027#include <linux/regset.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/user.h>
29#include <linux/security.h>
Jesper Juhl7ed20e12005-05-01 08:59:14 -070030#include <linux/signal.h>
Roland McGrath1d48d712007-12-20 03:58:49 -080031#include <linux/compat.h>
Andreas Schwabe4cc5892008-04-20 02:25:13 +100032#include <linux/elf.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#include <asm/uaccess.h>
35#include <asm/page.h>
36#include <asm/pgtable.h>
37#include <asm/system.h>
Paul Mackerras21a62902005-11-19 20:47:22 +110038
Andreas Schwabe4cc5892008-04-20 02:25:13 +100039#include "ppc32.h"
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041/*
42 * does not yet catch signals sent when the child dies.
43 * in exit.c or in signal.c.
44 */
45
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100046/*
47 * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
48 * we mark them as obsolete now, they will be removed in a future version
49 */
50static long compat_ptrace_old(struct task_struct *child, long request,
51 long addr, long data)
52{
Roland McGrath0deef2c2007-12-20 03:59:04 -080053 switch (request) {
54 case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
55 return copy_regset_to_user(child,
56 task_user_regset_view(current), 0,
57 0, 32 * sizeof(compat_long_t),
58 compat_ptr(data));
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100059
Roland McGrath0deef2c2007-12-20 03:59:04 -080060 case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
61 return copy_regset_from_user(child,
62 task_user_regset_view(current), 0,
63 0, 32 * sizeof(compat_long_t),
64 compat_ptr(data));
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100065 }
66
Roland McGrath0deef2c2007-12-20 03:59:04 -080067 return -EPERM;
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +100068}
69
Andreas Schwabe4cc5892008-04-20 02:25:13 +100070static int compat_ptrace_getsiginfo(struct task_struct *child, compat_siginfo_t __user *data)
71{
72 siginfo_t lastinfo;
73 int error = -ESRCH;
74
75 read_lock(&tasklist_lock);
76 if (likely(child->sighand != NULL)) {
77 error = -EINVAL;
78 spin_lock_irq(&child->sighand->siglock);
79 if (likely(child->last_siginfo != NULL)) {
80 lastinfo = *child->last_siginfo;
81 error = 0;
82 }
83 spin_unlock_irq(&child->sighand->siglock);
84 }
85 read_unlock(&tasklist_lock);
86 if (!error)
87 return copy_siginfo_to_user32(data, &lastinfo);
88 return error;
89}
90
Roland McGrath81e695c2007-12-20 03:58:55 -080091long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
92 compat_ulong_t caddr, compat_ulong_t cdata)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093{
Roland McGrath81e695c2007-12-20 03:58:55 -080094 unsigned long addr = caddr;
95 unsigned long data = cdata;
Christoph Hellwig6b9c7ed2006-01-08 01:02:33 -080096 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 switch (request) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 /*
100 * Read 4 bytes of the other process' storage
101 * data is a pointer specifying where the user wants the
102 * 4 bytes copied into
103 * addr is a pointer in the user's storage that contains an 8 byte
104 * address in the other process of the 4 bytes that is to be read
105 * (this is run in a 32-bit process looking at a 64-bit process)
106 * when I and D space are separate, these will need to be fixed.
107 */
108 case PPC_PTRACE_PEEKTEXT_3264:
109 case PPC_PTRACE_PEEKDATA_3264: {
110 u32 tmp;
111 int copied;
112 u32 __user * addrOthers;
113
114 ret = -EIO;
115
116 /* Get the addr in the other process that we want to read */
117 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
118 break;
119
120 copied = access_process_vm(child, (u64)addrOthers, &tmp,
121 sizeof(tmp), 0);
122 if (copied != sizeof(tmp))
123 break;
124 ret = put_user(tmp, (u32 __user *)data);
125 break;
126 }
127
128 /* Read a register (specified by ADDR) out of the "user area" */
129 case PTRACE_PEEKUSR: {
130 int index;
131 unsigned long tmp;
132
133 ret = -EIO;
134 /* convert to index and check */
135 index = (unsigned long) addr >> 2;
136 if ((addr & 3) || (index > PT_FPSCR32))
137 break;
138
Roland McGrathfabca2c2007-09-25 09:50:52 +1000139 CHECK_FULL_REGS(child->thread.regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 if (index < PT_FPR0) {
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000141 tmp = ptrace_get_reg(child, index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 } else {
143 flush_fp_to_thread(child);
144 /*
145 * the user space code considers the floating point
146 * to be an array of unsigned int (32 bits) - the
147 * index passed in is based on this assumption.
148 */
149 tmp = ((unsigned int *)child->thread.fpr)[index - PT_FPR0];
150 }
151 ret = put_user((unsigned int)tmp, (u32 __user *)data);
152 break;
153 }
154
155 /*
156 * Read 4 bytes out of the other process' pt_regs area
157 * data is a pointer specifying where the user wants the
158 * 4 bytes copied into
159 * addr is the offset into the other process' pt_regs structure
160 * that is to be read
161 * (this is run in a 32-bit process looking at a 64-bit process)
162 */
163 case PPC_PTRACE_PEEKUSR_3264: {
164 u32 index;
165 u32 reg32bits;
166 u64 tmp;
167 u32 numReg;
168 u32 part;
169
170 ret = -EIO;
171 /* Determine which register the user wants */
172 index = (u64)addr >> 2;
173 numReg = index / 2;
174 /* Determine which part of the register the user wants */
175 if (index % 2)
176 part = 1; /* want the 2nd half of the register (right-most). */
177 else
178 part = 0; /* want the 1st half of the register (left-most). */
179
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000180 /* Validate the input - check to see if address is on the wrong boundary
181 * or beyond the end of the user area
182 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 if ((addr & 3) || numReg > PT_FPSCR)
184 break;
185
Roland McGrathfabca2c2007-09-25 09:50:52 +1000186 CHECK_FULL_REGS(child->thread.regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 if (numReg >= PT_FPR0) {
188 flush_fp_to_thread(child);
189 tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0];
190 } else { /* register within PT_REGS struct */
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000191 tmp = ptrace_get_reg(child, numReg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 }
193 reg32bits = ((u32*)&tmp)[part];
194 ret = put_user(reg32bits, (u32 __user *)data);
195 break;
196 }
197
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 /*
199 * Write 4 bytes into the other process' storage
200 * data is the 4 bytes that the user wants written
201 * addr is a pointer in the user's storage that contains an
202 * 8 byte address in the other process where the 4 bytes
203 * that is to be written
204 * (this is run in a 32-bit process looking at a 64-bit process)
205 * when I and D space are separate, these will need to be fixed.
206 */
207 case PPC_PTRACE_POKETEXT_3264:
208 case PPC_PTRACE_POKEDATA_3264: {
209 u32 tmp = data;
210 u32 __user * addrOthers;
211
212 /* Get the addr in the other process that we want to write into */
213 ret = -EIO;
214 if (get_user(addrOthers, (u32 __user * __user *)addr) != 0)
215 break;
216 ret = 0;
217 if (access_process_vm(child, (u64)addrOthers, &tmp,
218 sizeof(tmp), 1) == sizeof(tmp))
219 break;
220 ret = -EIO;
221 break;
222 }
223
224 /* write the word at location addr in the USER area */
225 case PTRACE_POKEUSR: {
226 unsigned long index;
227
228 ret = -EIO;
229 /* convert to index and check */
230 index = (unsigned long) addr >> 2;
231 if ((addr & 3) || (index > PT_FPSCR32))
232 break;
233
Roland McGrathfabca2c2007-09-25 09:50:52 +1000234 CHECK_FULL_REGS(child->thread.regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 if (index < PT_FPR0) {
Benjamin Herrenschmidt865418d2007-06-04 15:15:44 +1000236 ret = ptrace_put_reg(child, index, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 } else {
238 flush_fp_to_thread(child);
239 /*
240 * the user space code considers the floating point
241 * to be an array of unsigned int (32 bits) - the
242 * index passed in is based on this assumption.
243 */
244 ((unsigned int *)child->thread.fpr)[index - PT_FPR0] = data;
245 ret = 0;
246 }
247 break;
248 }
249
250 /*
251 * Write 4 bytes into the other process' pt_regs area
252 * data is the 4 bytes that the user wants written
253 * addr is the offset into the other process' pt_regs structure
254 * that is to be written into
255 * (this is run in a 32-bit process looking at a 64-bit process)
256 */
257 case PPC_PTRACE_POKEUSR_3264: {
258 u32 index;
259 u32 numReg;
260
261 ret = -EIO;
262 /* Determine which register the user wants */
263 index = (u64)addr >> 2;
264 numReg = index / 2;
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 /*
267 * Validate the input - check to see if address is on the
268 * wrong boundary or beyond the end of the user area
269 */
270 if ((addr & 3) || (numReg > PT_FPSCR))
271 break;
Roland McGrathfabca2c2007-09-25 09:50:52 +1000272 CHECK_FULL_REGS(child->thread.regs);
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000273 if (numReg < PT_FPR0) {
274 unsigned long freg = ptrace_get_reg(child, numReg);
275 if (index % 2)
276 freg = (freg & ~0xfffffffful) | (data & 0xfffffffful);
277 else
278 freg = (freg & 0xfffffffful) | (data << 32);
279 ret = ptrace_put_reg(child, numReg, freg);
280 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 flush_fp_to_thread(child);
Benjamin Herrenschmidt912000e2007-06-04 15:15:46 +1000282 ((unsigned int *)child->thread.regs)[index] = data;
283 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 break;
286 }
287
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000288 case PTRACE_GET_DEBUGREG: {
289 ret = -EINVAL;
290 /* We only support one DABR and no IABRS at the moment */
291 if (addr > 0)
292 break;
293 ret = put_user(child->thread.dabr, (u32 __user *)data);
294 break;
295 }
296
Roland McGrath0deef2c2007-12-20 03:59:04 -0800297 case PTRACE_GETREGS: /* Get all pt_regs from the child. */
298 return copy_regset_to_user(
299 child, task_user_regset_view(current), 0,
300 0, PT_REGS_COUNT * sizeof(compat_long_t),
301 compat_ptr(data));
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000302
Roland McGrath0deef2c2007-12-20 03:59:04 -0800303 case PTRACE_SETREGS: /* Set all gp regs in the child. */
304 return copy_regset_from_user(
305 child, task_user_regset_view(current), 0,
306 0, PT_REGS_COUNT * sizeof(compat_long_t),
307 compat_ptr(data));
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000308
Andreas Schwabe4cc5892008-04-20 02:25:13 +1000309 case PTRACE_GETSIGINFO:
310 return compat_ptrace_getsiginfo(child, compat_ptr(data));
311
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000312 case PTRACE_GETFPREGS:
313 case PTRACE_SETFPREGS:
Robert Jennings962bca72005-09-10 16:01:07 +1000314 case PTRACE_GETVRREGS:
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000315 case PTRACE_SETVRREGS:
316 case PTRACE_GETREGS64:
317 case PTRACE_SETREGS64:
318 case PPC_PTRACE_GETFPREGS:
319 case PPC_PTRACE_SETFPREGS:
320 case PTRACE_KILL:
321 case PTRACE_SINGLESTEP:
322 case PTRACE_DETACH:
323 case PTRACE_SET_DEBUGREG:
324 case PTRACE_SYSCALL:
325 case PTRACE_CONT:
326 ret = arch_ptrace(child, request, addr, data);
Robert Jennings962bca72005-09-10 16:01:07 +1000327 break;
328
Benjamin Herrenschmidte17666b2007-06-04 15:15:43 +1000329 /* Old reverse args ptrace callss */
330 case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
331 case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
332 ret = compat_ptrace_old(child, request, addr, data);
Robert Jennings962bca72005-09-10 16:01:07 +1000333 break;
Robert Jennings962bca72005-09-10 16:01:07 +1000334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 default:
Roland McGrath1d48d712007-12-20 03:58:49 -0800336 ret = compat_ptrace_request(child, request, addr, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 break;
338 }
Roland McGrath81e695c2007-12-20 03:58:55 -0800339
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 return ret;
341}