ptrace32.c 7.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 1992 Ross Biro
 * Copyright (C) Linus Torvalds
 * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
 * Copyright (C) 1996 David S. Miller
 * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
 * Copyright (C) 1999 MIPS Technologies, Inc.
 * Copyright (C) 2000 Ulf Carlsson
 *
 * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
 * binaries.
 */
#include <linux/compiler.h>
T
Thomas Bogendoerfer 已提交
18
#include <linux/compat.h>
L
Linus Torvalds 已提交
19 20
#include <linux/kernel.h>
#include <linux/sched.h>
21
#include <linux/sched/task_stack.h>
L
Linus Torvalds 已提交
22 23 24 25 26 27 28
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/smp.h>
#include <linux/security.h>

#include <asm/cpu.h>
29
#include <asm/dsp.h>
L
Linus Torvalds 已提交
30 31
#include <asm/fpu.h>
#include <asm/mipsregs.h>
32
#include <asm/mipsmtregs.h>
L
Linus Torvalds 已提交
33 34
#include <asm/pgtable.h>
#include <asm/page.h>
A
Alex Smith 已提交
35
#include <asm/reg.h>
36
#include <asm/syscall.h>
37
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
38 39 40 41 42 43
#include <asm/bootinfo.h>

/*
 * Tracing a 32-bit process with a 64-bit strace and vice versa will not
 * work.  I don't know how to fix this.
 */
T
Thomas Bogendoerfer 已提交
44 45
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
			compat_ulong_t caddr, compat_ulong_t cdata)
L
Linus Torvalds 已提交
46
{
T
Thomas Bogendoerfer 已提交
47 48
	int addr = caddr;
	int data = cdata;
L
Linus Torvalds 已提交
49 50 51 52
	int ret;

	switch (request) {

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	/*
	 * Read 4 bytes of the other process' storage
	 *  data is a pointer specifying where the user wants the
	 *	4 bytes copied into
	 *  addr is a pointer in the user's storage that contains an 8 byte
	 *	address in the other process of the 4 bytes that is to be read
	 * (this is run in a 32-bit process looking at a 64-bit process)
	 * when I and D space are separate, these will need to be fixed.
	 */
	case PTRACE_PEEKTEXT_3264:
	case PTRACE_PEEKDATA_3264: {
		u32 tmp;
		int copied;
		u32 __user * addrOthers;

		ret = -EIO;

		/* Get the addr in the other process that we want to read */
		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
			break;

74
		copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
75
				sizeof(tmp), FOLL_FORCE);
76 77 78 79 80 81
		if (copied != sizeof(tmp))
			break;
		ret = put_user(tmp, (u32 __user *) (unsigned long) data);
		break;
	}

L
Linus Torvalds 已提交
82 83 84
	/* Read the word at location addr in the USER area. */
	case PTRACE_PEEKUSR: {
		struct pt_regs *regs;
P
Paul Burton 已提交
85
		union fpureg *fregs;
L
Linus Torvalds 已提交
86 87
		unsigned int tmp;

A
Al Viro 已提交
88
		regs = task_pt_regs(child);
L
Linus Torvalds 已提交
89 90 91 92 93 94 95
		ret = 0;  /* Default return value. */

		switch (addr) {
		case 0 ... 31:
			tmp = regs->regs[addr];
			break;
		case FPR_BASE ... FPR_BASE + 31:
96 97 98 99 100 101
			if (!tsk_used_math(child)) {
				/* FP not yet used */
				tmp = -1;
				break;
			}
			fregs = get_fpu_regs(child);
102
			if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
L
Linus Torvalds 已提交
103 104 105 106 107
				/*
				 * The odd registers are actually the high
				 * order bits of the values stored in the even
				 * registers - unless we're using r2k_switch.S.
				 */
P
Paul Burton 已提交
108 109
				tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE],
						addr & 1);
110
				break;
L
Linus Torvalds 已提交
111
			}
112
			tmp = get_fpr64(&fregs[addr - FPR_BASE], 0);
L
Linus Torvalds 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
			break;
		case PC:
			tmp = regs->cp0_epc;
			break;
		case CAUSE:
			tmp = regs->cp0_cause;
			break;
		case BADVADDR:
			tmp = regs->cp0_badvaddr;
			break;
		case MMHI:
			tmp = regs->hi;
			break;
		case MMLO:
			tmp = regs->lo;
			break;
		case FPC_CSR:
130
			tmp = child->thread.fpu.fcr31;
L
Linus Torvalds 已提交
131
			break;
132 133
		case FPC_EIR:
			/* implementation / version register */
134
			tmp = boot_cpu_data.fpu_id;
L
Linus Torvalds 已提交
135
			break;
136 137 138
		case DSP_BASE ... DSP_BASE + 5: {
			dspreg_t *dregs;

139 140 141
			if (!cpu_has_dsp) {
				tmp = 0;
				ret = -EIO;
T
Thomas Bogendoerfer 已提交
142
				goto out;
143
			}
144
			dregs = __get_dsp_regs(child);
R
Ralf Baechle 已提交
145
			tmp = (unsigned long) (dregs[addr - DSP_BASE]);
146
			break;
147
		}
148 149 150 151
		case DSP_CONTROL:
			if (!cpu_has_dsp) {
				tmp = 0;
				ret = -EIO;
T
Thomas Bogendoerfer 已提交
152
				goto out;
153 154 155
			}
			tmp = child->thread.dsp.dspcontrol;
			break;
L
Linus Torvalds 已提交
156 157 158
		default:
			tmp = 0;
			ret = -EIO;
T
Thomas Bogendoerfer 已提交
159
			goto out;
L
Linus Torvalds 已提交
160
		}
161
		ret = put_user(tmp, (unsigned __user *) (unsigned long) data);
L
Linus Torvalds 已提交
162 163 164
		break;
	}

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	/*
	 * Write 4 bytes into the other process' storage
	 *  data is the 4 bytes that the user wants written
	 *  addr is a pointer in the user's storage that contains an
	 *	8 byte address in the other process where the 4 bytes
	 *	that is to be written
	 * (this is run in a 32-bit process looking at a 64-bit process)
	 * when I and D space are separate, these will need to be fixed.
	 */
	case PTRACE_POKETEXT_3264:
	case PTRACE_POKEDATA_3264: {
		u32 __user * addrOthers;

		/* Get the addr in the other process that we want to write into */
		ret = -EIO;
		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
			break;
		ret = 0;
183
		if (ptrace_access_vm(child, (u64)addrOthers, &data,
184 185
					sizeof(data),
					FOLL_FORCE | FOLL_WRITE) == sizeof(data))
186 187 188 189 190
			break;
		ret = -EIO;
		break;
	}

L
Linus Torvalds 已提交
191 192 193
	case PTRACE_POKEUSR: {
		struct pt_regs *regs;
		ret = 0;
A
Al Viro 已提交
194
		regs = task_pt_regs(child);
L
Linus Torvalds 已提交
195 196 197 198

		switch (addr) {
		case 0 ... 31:
			regs->regs[addr] = data;
199 200 201 202 203 204
			/* System call number may have been changed */
			if (addr == 2)
				mips_syscall_update_nr(child, regs);
			else if (addr == 4 &&
				 mips_syscall_is_indirect(child, regs))
				mips_syscall_update_nr(child, regs);
L
Linus Torvalds 已提交
205 206
			break;
		case FPR_BASE ... FPR_BASE + 31: {
P
Paul Burton 已提交
207
			union fpureg *fregs = get_fpu_regs(child);
L
Linus Torvalds 已提交
208 209 210

			if (!tsk_used_math(child)) {
				/* FP not yet used  */
211 212 213
				memset(&child->thread.fpu, ~0,
				       sizeof(child->thread.fpu));
				child->thread.fpu.fcr31 = 0;
L
Linus Torvalds 已提交
214
			}
215
			if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
216 217 218 219 220
				/*
				 * The odd registers are actually the high
				 * order bits of the values stored in the even
				 * registers - unless we're using r2k_switch.S.
				 */
P
Paul Burton 已提交
221 222
				set_fpr32(&fregs[(addr & ~1) - FPR_BASE],
					  addr & 1, data);
223
				break;
L
Linus Torvalds 已提交
224
			}
P
Paul Burton 已提交
225
			set_fpr64(&fregs[addr - FPR_BASE], 0, data);
L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233 234 235 236 237
			break;
		}
		case PC:
			regs->cp0_epc = data;
			break;
		case MMHI:
			regs->hi = data;
			break;
		case MMLO:
			regs->lo = data;
			break;
		case FPC_CSR:
238
			child->thread.fpu.fcr31 = data;
L
Linus Torvalds 已提交
239
			break;
240 241 242
		case DSP_BASE ... DSP_BASE + 5: {
			dspreg_t *dregs;

243 244 245 246 247
			if (!cpu_has_dsp) {
				ret = -EIO;
				break;
			}

248
			dregs = __get_dsp_regs(child);
249 250
			dregs[addr - DSP_BASE] = data;
			break;
251
		}
252 253 254 255 256 257 258
		case DSP_CONTROL:
			if (!cpu_has_dsp) {
				ret = -EIO;
				break;
			}
			child->thread.dsp.dspcontrol = data;
			break;
L
Linus Torvalds 已提交
259 260 261 262 263 264 265 266
		default:
			/* The rest are not allowed. */
			ret = -EIO;
			break;
		}
		break;
		}

267
	case PTRACE_GETREGS:
268 269
		ret = ptrace_getregs(child,
				(struct user_pt_regs __user *) (__u64) data);
270 271 272
		break;

	case PTRACE_SETREGS:
273 274
		ret = ptrace_setregs(child,
				(struct user_pt_regs __user *) (__u64) data);
275 276 277
		break;

	case PTRACE_GETFPREGS:
278
		ret = ptrace_getfpregs(child, (__u32 __user *) (__u64) data);
279 280 281
		break;

	case PTRACE_SETFPREGS:
282
		ret = ptrace_setfpregs(child, (__u32 __user *) (__u64) data);
283 284
		break;

R
Ralf Baechle 已提交
285
	case PTRACE_GET_THREAD_AREA:
A
Al Viro 已提交
286
		ret = put_user(task_thread_info(child)->tp_value,
R
Ralf Baechle 已提交
287 288 289
				(unsigned int __user *) (unsigned long) data);
		break;

290
	case PTRACE_GET_THREAD_AREA_3264:
A
Al Viro 已提交
291
		ret = put_user(task_thread_info(child)->tp_value,
292 293 294
				(unsigned long __user *) (unsigned long) data);
		break;

295 296 297 298 299 300 301 302 303 304
	case PTRACE_GET_WATCH_REGS:
		ret = ptrace_get_watch_regs(child,
			(struct pt_watch_regs __user *) (unsigned long) addr);
		break;

	case PTRACE_SET_WATCH_REGS:
		ret = ptrace_set_watch_regs(child,
			(struct pt_watch_regs __user *) (unsigned long) addr);
		break;

L
Linus Torvalds 已提交
305
	default:
306
		ret = compat_ptrace_request(child, request, addr, data);
L
Linus Torvalds 已提交
307 308 309 310 311
		break;
	}
out:
	return ret;
}