xref: /linux/arch/mips/kernel/ptrace32.c (revision e31cf2f4)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * This file is subject to the terms and conditions of the GNU General Public
31da177e4SLinus Torvalds  * License.  See the file "COPYING" in the main directory of this archive
41da177e4SLinus Torvalds  * for more details.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1992 Ross Biro
71da177e4SLinus Torvalds  * Copyright (C) Linus Torvalds
81da177e4SLinus Torvalds  * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
91da177e4SLinus Torvalds  * Copyright (C) 1996 David S. Miller
101da177e4SLinus Torvalds  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
111da177e4SLinus Torvalds  * Copyright (C) 1999 MIPS Technologies, Inc.
121da177e4SLinus Torvalds  * Copyright (C) 2000 Ulf Carlsson
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
151da177e4SLinus Torvalds  * binaries.
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds #include <linux/compiler.h>
185d9a76cdSThomas Bogendoerfer #include <linux/compat.h>
191da177e4SLinus Torvalds #include <linux/kernel.h>
201da177e4SLinus Torvalds #include <linux/sched.h>
2168db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
221da177e4SLinus Torvalds #include <linux/mm.h>
231da177e4SLinus Torvalds #include <linux/errno.h>
241da177e4SLinus Torvalds #include <linux/ptrace.h>
251da177e4SLinus Torvalds #include <linux/smp.h>
261da177e4SLinus Torvalds #include <linux/security.h>
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <asm/cpu.h>
29e50c0a8fSRalf Baechle #include <asm/dsp.h>
301da177e4SLinus Torvalds #include <asm/fpu.h>
311da177e4SLinus Torvalds #include <asm/mipsregs.h>
32101b3531SRalf Baechle #include <asm/mipsmtregs.h>
331da177e4SLinus Torvalds #include <asm/page.h>
3460be939cSAlex Smith #include <asm/reg.h>
35de8cd0dcSJames Hogan #include <asm/syscall.h>
367c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
371da177e4SLinus Torvalds #include <asm/bootinfo.h>
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds /*
401da177e4SLinus Torvalds  * Tracing a 32-bit process with a 64-bit strace and vice versa will not
411da177e4SLinus Torvalds  * work.  I don't know how to fix this.
421da177e4SLinus Torvalds  */
compat_arch_ptrace(struct task_struct * child,compat_long_t request,compat_ulong_t caddr,compat_ulong_t cdata)435d9a76cdSThomas Bogendoerfer long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
445d9a76cdSThomas Bogendoerfer 			compat_ulong_t caddr, compat_ulong_t cdata)
451da177e4SLinus Torvalds {
465d9a76cdSThomas Bogendoerfer 	int addr = caddr;
475d9a76cdSThomas Bogendoerfer 	int data = cdata;
481da177e4SLinus Torvalds 	int ret;
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	switch (request) {
511da177e4SLinus Torvalds 
52ea3d710fSDaniel Jacobowitz 	/*
53ea3d710fSDaniel Jacobowitz 	 * Read 4 bytes of the other process' storage
54ea3d710fSDaniel Jacobowitz 	 *  data is a pointer specifying where the user wants the
55ea3d710fSDaniel Jacobowitz 	 *	4 bytes copied into
56ea3d710fSDaniel Jacobowitz 	 *  addr is a pointer in the user's storage that contains an 8 byte
57ea3d710fSDaniel Jacobowitz 	 *	address in the other process of the 4 bytes that is to be read
58ea3d710fSDaniel Jacobowitz 	 * (this is run in a 32-bit process looking at a 64-bit process)
59ea3d710fSDaniel Jacobowitz 	 * when I and D space are separate, these will need to be fixed.
60ea3d710fSDaniel Jacobowitz 	 */
61ea3d710fSDaniel Jacobowitz 	case PTRACE_PEEKTEXT_3264:
62ea3d710fSDaniel Jacobowitz 	case PTRACE_PEEKDATA_3264: {
63ea3d710fSDaniel Jacobowitz 		u32 tmp;
64ea3d710fSDaniel Jacobowitz 		int copied;
65ea3d710fSDaniel Jacobowitz 		u32 __user * addrOthers;
66ea3d710fSDaniel Jacobowitz 
67ea3d710fSDaniel Jacobowitz 		ret = -EIO;
68ea3d710fSDaniel Jacobowitz 
69ea3d710fSDaniel Jacobowitz 		/* Get the addr in the other process that we want to read */
70ea3d710fSDaniel Jacobowitz 		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
71ea3d710fSDaniel Jacobowitz 			break;
72ea3d710fSDaniel Jacobowitz 
7384d77d3fSEric W. Biederman 		copied = ptrace_access_vm(child, (u64)addrOthers, &tmp,
74f307ab6dSLorenzo Stoakes 				sizeof(tmp), FOLL_FORCE);
75ea3d710fSDaniel Jacobowitz 		if (copied != sizeof(tmp))
76ea3d710fSDaniel Jacobowitz 			break;
77ea3d710fSDaniel Jacobowitz 		ret = put_user(tmp, (u32 __user *) (unsigned long) data);
78ea3d710fSDaniel Jacobowitz 		break;
79ea3d710fSDaniel Jacobowitz 	}
80ea3d710fSDaniel Jacobowitz 
811da177e4SLinus Torvalds 	/* Read the word at location addr in the USER area. */
821da177e4SLinus Torvalds 	case PTRACE_PEEKUSR: {
831da177e4SLinus Torvalds 		struct pt_regs *regs;
841da177e4SLinus Torvalds 		unsigned int tmp;
851da177e4SLinus Torvalds 
8640bc9c67SAl Viro 		regs = task_pt_regs(child);
871da177e4SLinus Torvalds 		ret = 0;  /* Default return value. */
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 		switch (addr) {
901da177e4SLinus Torvalds 		case 0 ... 31:
911da177e4SLinus Torvalds 			tmp = regs->regs[addr];
921da177e4SLinus Torvalds 			break;
93*6c79759eSPaul Burton #ifdef CONFIG_MIPS_FP_SUPPORT
94*6c79759eSPaul Burton 		case FPR_BASE ... FPR_BASE + 31: {
95*6c79759eSPaul Burton 			union fpureg *fregs;
96*6c79759eSPaul Burton 
97597ce172SPaul Burton 			if (!tsk_used_math(child)) {
98597ce172SPaul Burton 				/* FP not yet used */
99597ce172SPaul Burton 				tmp = -1;
100597ce172SPaul Burton 				break;
101597ce172SPaul Burton 			}
102597ce172SPaul Burton 			fregs = get_fpu_regs(child);
1039a3a92ccSMaciej W. Rozycki 			if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
1041da177e4SLinus Torvalds 				/*
1051da177e4SLinus Torvalds 				 * The odd registers are actually the high
1061da177e4SLinus Torvalds 				 * order bits of the values stored in the even
107d1157b10SMaciej W. Rozycki 				 * registers.
1081da177e4SLinus Torvalds 				 */
109bbd426f5SPaul Burton 				tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE],
110bbd426f5SPaul Burton 						addr & 1);
111597ce172SPaul Burton 				break;
1121da177e4SLinus Torvalds 			}
113c7e81462SMaciej W. Rozycki 			tmp = get_fpr64(&fregs[addr - FPR_BASE], 0);
1141da177e4SLinus Torvalds 			break;
115*6c79759eSPaul Burton 		}
116*6c79759eSPaul Burton 		case FPC_CSR:
117*6c79759eSPaul Burton 			tmp = child->thread.fpu.fcr31;
118*6c79759eSPaul Burton 			break;
119*6c79759eSPaul Burton 		case FPC_EIR:
120*6c79759eSPaul Burton 			/* implementation / version register */
121*6c79759eSPaul Burton 			tmp = boot_cpu_data.fpu_id;
122*6c79759eSPaul Burton 			break;
123*6c79759eSPaul Burton #endif /* CONFIG_MIPS_FP_SUPPORT */
1241da177e4SLinus Torvalds 		case PC:
1251da177e4SLinus Torvalds 			tmp = regs->cp0_epc;
1261da177e4SLinus Torvalds 			break;
1271da177e4SLinus Torvalds 		case CAUSE:
1281da177e4SLinus Torvalds 			tmp = regs->cp0_cause;
1291da177e4SLinus Torvalds 			break;
1301da177e4SLinus Torvalds 		case BADVADDR:
1311da177e4SLinus Torvalds 			tmp = regs->cp0_badvaddr;
1321da177e4SLinus Torvalds 			break;
1331da177e4SLinus Torvalds 		case MMHI:
1341da177e4SLinus Torvalds 			tmp = regs->hi;
1351da177e4SLinus Torvalds 			break;
1361da177e4SLinus Torvalds 		case MMLO:
1371da177e4SLinus Torvalds 			tmp = regs->lo;
1381da177e4SLinus Torvalds 			break;
1393055acb0SAtsushi Nemoto 		case DSP_BASE ... DSP_BASE + 5: {
1403055acb0SAtsushi Nemoto 			dspreg_t *dregs;
1413055acb0SAtsushi Nemoto 
142e50c0a8fSRalf Baechle 			if (!cpu_has_dsp) {
143e50c0a8fSRalf Baechle 				tmp = 0;
144e50c0a8fSRalf Baechle 				ret = -EIO;
1455d9a76cdSThomas Bogendoerfer 				goto out;
146e50c0a8fSRalf Baechle 			}
1473055acb0SAtsushi Nemoto 			dregs = __get_dsp_regs(child);
148f5958b4cSMaciej W. Rozycki 			tmp = dregs[addr - DSP_BASE];
149e50c0a8fSRalf Baechle 			break;
1503055acb0SAtsushi Nemoto 		}
151e50c0a8fSRalf Baechle 		case DSP_CONTROL:
152e50c0a8fSRalf Baechle 			if (!cpu_has_dsp) {
153e50c0a8fSRalf Baechle 				tmp = 0;
154e50c0a8fSRalf Baechle 				ret = -EIO;
1555d9a76cdSThomas Bogendoerfer 				goto out;
156e50c0a8fSRalf Baechle 			}
157e50c0a8fSRalf Baechle 			tmp = child->thread.dsp.dspcontrol;
158e50c0a8fSRalf Baechle 			break;
1591da177e4SLinus Torvalds 		default:
1601da177e4SLinus Torvalds 			tmp = 0;
1611da177e4SLinus Torvalds 			ret = -EIO;
1625d9a76cdSThomas Bogendoerfer 			goto out;
1631da177e4SLinus Torvalds 		}
1643055acb0SAtsushi Nemoto 		ret = put_user(tmp, (unsigned __user *) (unsigned long) data);
1651da177e4SLinus Torvalds 		break;
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 
168ea3d710fSDaniel Jacobowitz 	/*
169ea3d710fSDaniel Jacobowitz 	 * Write 4 bytes into the other process' storage
170ea3d710fSDaniel Jacobowitz 	 *  data is the 4 bytes that the user wants written
171ea3d710fSDaniel Jacobowitz 	 *  addr is a pointer in the user's storage that contains an
172ea3d710fSDaniel Jacobowitz 	 *	8 byte address in the other process where the 4 bytes
173ea3d710fSDaniel Jacobowitz 	 *	that is to be written
174ea3d710fSDaniel Jacobowitz 	 * (this is run in a 32-bit process looking at a 64-bit process)
175ea3d710fSDaniel Jacobowitz 	 * when I and D space are separate, these will need to be fixed.
176ea3d710fSDaniel Jacobowitz 	 */
177ea3d710fSDaniel Jacobowitz 	case PTRACE_POKETEXT_3264:
178ea3d710fSDaniel Jacobowitz 	case PTRACE_POKEDATA_3264: {
179ea3d710fSDaniel Jacobowitz 		u32 __user * addrOthers;
180ea3d710fSDaniel Jacobowitz 
181ea3d710fSDaniel Jacobowitz 		/* Get the addr in the other process that we want to write into */
182ea3d710fSDaniel Jacobowitz 		ret = -EIO;
183ea3d710fSDaniel Jacobowitz 		if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
184ea3d710fSDaniel Jacobowitz 			break;
185ea3d710fSDaniel Jacobowitz 		ret = 0;
18684d77d3fSEric W. Biederman 		if (ptrace_access_vm(child, (u64)addrOthers, &data,
187f307ab6dSLorenzo Stoakes 					sizeof(data),
188f307ab6dSLorenzo Stoakes 					FOLL_FORCE | FOLL_WRITE) == sizeof(data))
189ea3d710fSDaniel Jacobowitz 			break;
190ea3d710fSDaniel Jacobowitz 		ret = -EIO;
191ea3d710fSDaniel Jacobowitz 		break;
192ea3d710fSDaniel Jacobowitz 	}
193ea3d710fSDaniel Jacobowitz 
1941da177e4SLinus Torvalds 	case PTRACE_POKEUSR: {
1951da177e4SLinus Torvalds 		struct pt_regs *regs;
1961da177e4SLinus Torvalds 		ret = 0;
19740bc9c67SAl Viro 		regs = task_pt_regs(child);
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 		switch (addr) {
2001da177e4SLinus Torvalds 		case 0 ... 31:
2011da177e4SLinus Torvalds 			regs->regs[addr] = data;
202de8cd0dcSJames Hogan 			/* System call number may have been changed */
203de8cd0dcSJames Hogan 			if (addr == 2)
204de8cd0dcSJames Hogan 				mips_syscall_update_nr(child, regs);
205de8cd0dcSJames Hogan 			else if (addr == 4 &&
206de8cd0dcSJames Hogan 				 mips_syscall_is_indirect(child, regs))
207de8cd0dcSJames Hogan 				mips_syscall_update_nr(child, regs);
2081da177e4SLinus Torvalds 			break;
209*6c79759eSPaul Burton #ifdef CONFIG_MIPS_FP_SUPPORT
2101da177e4SLinus Torvalds 		case FPR_BASE ... FPR_BASE + 31: {
211bbd426f5SPaul Burton 			union fpureg *fregs = get_fpu_regs(child);
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 			if (!tsk_used_math(child)) {
2141da177e4SLinus Torvalds 				/* FP not yet used  */
215eae89076SAtsushi Nemoto 				memset(&child->thread.fpu, ~0,
216eae89076SAtsushi Nemoto 				       sizeof(child->thread.fpu));
217eae89076SAtsushi Nemoto 				child->thread.fpu.fcr31 = 0;
2181da177e4SLinus Torvalds 			}
2199a3a92ccSMaciej W. Rozycki 			if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
2201da177e4SLinus Torvalds 				/*
221597ce172SPaul Burton 				 * The odd registers are actually the high
222597ce172SPaul Burton 				 * order bits of the values stored in the even
223d1157b10SMaciej W. Rozycki 				 * registers.
2241da177e4SLinus Torvalds 				 */
225bbd426f5SPaul Burton 				set_fpr32(&fregs[(addr & ~1) - FPR_BASE],
226bbd426f5SPaul Burton 					  addr & 1, data);
2271da177e4SLinus Torvalds 				break;
2281da177e4SLinus Torvalds 			}
229bbd426f5SPaul Burton 			set_fpr64(&fregs[addr - FPR_BASE], 0, data);
230597ce172SPaul Burton 			break;
231597ce172SPaul Burton 		}
232*6c79759eSPaul Burton 		case FPC_CSR:
233*6c79759eSPaul Burton 			child->thread.fpu.fcr31 = data;
234*6c79759eSPaul Burton 			break;
235*6c79759eSPaul Burton #endif /* CONFIG_MIPS_FP_SUPPORT */
2361da177e4SLinus Torvalds 		case PC:
2371da177e4SLinus Torvalds 			regs->cp0_epc = data;
2381da177e4SLinus Torvalds 			break;
2391da177e4SLinus Torvalds 		case MMHI:
2401da177e4SLinus Torvalds 			regs->hi = data;
2411da177e4SLinus Torvalds 			break;
2421da177e4SLinus Torvalds 		case MMLO:
2431da177e4SLinus Torvalds 			regs->lo = data;
2441da177e4SLinus Torvalds 			break;
2453055acb0SAtsushi Nemoto 		case DSP_BASE ... DSP_BASE + 5: {
2463055acb0SAtsushi Nemoto 			dspreg_t *dregs;
2473055acb0SAtsushi Nemoto 
248e50c0a8fSRalf Baechle 			if (!cpu_has_dsp) {
249e50c0a8fSRalf Baechle 				ret = -EIO;
250e50c0a8fSRalf Baechle 				break;
251e50c0a8fSRalf Baechle 			}
252e50c0a8fSRalf Baechle 
2533055acb0SAtsushi Nemoto 			dregs = __get_dsp_regs(child);
254e50c0a8fSRalf Baechle 			dregs[addr - DSP_BASE] = data;
255e50c0a8fSRalf Baechle 			break;
2563055acb0SAtsushi Nemoto 		}
257e50c0a8fSRalf Baechle 		case DSP_CONTROL:
258e50c0a8fSRalf Baechle 			if (!cpu_has_dsp) {
259e50c0a8fSRalf Baechle 				ret = -EIO;
260e50c0a8fSRalf Baechle 				break;
261e50c0a8fSRalf Baechle 			}
262e50c0a8fSRalf Baechle 			child->thread.dsp.dspcontrol = data;
263e50c0a8fSRalf Baechle 			break;
2641da177e4SLinus Torvalds 		default:
2651da177e4SLinus Torvalds 			/* The rest are not allowed. */
2661da177e4SLinus Torvalds 			ret = -EIO;
2671da177e4SLinus Torvalds 			break;
2681da177e4SLinus Torvalds 		}
2691da177e4SLinus Torvalds 		break;
2701da177e4SLinus Torvalds 		}
2711da177e4SLinus Torvalds 
272ea3d710fSDaniel Jacobowitz 	case PTRACE_GETREGS:
273a79ebea6SAlex Smith 		ret = ptrace_getregs(child,
274a79ebea6SAlex Smith 				(struct user_pt_regs __user *) (__u64) data);
275ea3d710fSDaniel Jacobowitz 		break;
276ea3d710fSDaniel Jacobowitz 
277ea3d710fSDaniel Jacobowitz 	case PTRACE_SETREGS:
278a79ebea6SAlex Smith 		ret = ptrace_setregs(child,
279a79ebea6SAlex Smith 				(struct user_pt_regs __user *) (__u64) data);
280ea3d710fSDaniel Jacobowitz 		break;
281ea3d710fSDaniel Jacobowitz 
282*6c79759eSPaul Burton #ifdef CONFIG_MIPS_FP_SUPPORT
283ea3d710fSDaniel Jacobowitz 	case PTRACE_GETFPREGS:
284ea3d710fSDaniel Jacobowitz 		ret = ptrace_getfpregs(child, (__u32 __user *) (__u64) data);
285ea3d710fSDaniel Jacobowitz 		break;
286ea3d710fSDaniel Jacobowitz 
287ea3d710fSDaniel Jacobowitz 	case PTRACE_SETFPREGS:
288ea3d710fSDaniel Jacobowitz 		ret = ptrace_setfpregs(child, (__u32 __user *) (__u64) data);
289ea3d710fSDaniel Jacobowitz 		break;
290*6c79759eSPaul Burton #endif
2913c37026dSRalf Baechle 	case PTRACE_GET_THREAD_AREA:
292dc8f6029SAl Viro 		ret = put_user(task_thread_info(child)->tp_value,
2933c37026dSRalf Baechle 				(unsigned int __user *) (unsigned long) data);
2943c37026dSRalf Baechle 		break;
2953c37026dSRalf Baechle 
296ea3d710fSDaniel Jacobowitz 	case PTRACE_GET_THREAD_AREA_3264:
297dc8f6029SAl Viro 		ret = put_user(task_thread_info(child)->tp_value,
298ea3d710fSDaniel Jacobowitz 				(unsigned long __user *) (unsigned long) data);
299ea3d710fSDaniel Jacobowitz 		break;
300ea3d710fSDaniel Jacobowitz 
3010926bf95SDavid Daney 	case PTRACE_GET_WATCH_REGS:
3020926bf95SDavid Daney 		ret = ptrace_get_watch_regs(child,
3030926bf95SDavid Daney 			(struct pt_watch_regs __user *) (unsigned long) addr);
3040926bf95SDavid Daney 		break;
3050926bf95SDavid Daney 
3060926bf95SDavid Daney 	case PTRACE_SET_WATCH_REGS:
3070926bf95SDavid Daney 		ret = ptrace_set_watch_regs(child,
3080926bf95SDavid Daney 			(struct pt_watch_regs __user *) (unsigned long) addr);
3090926bf95SDavid Daney 		break;
3100926bf95SDavid Daney 
3111da177e4SLinus Torvalds 	default:
312797c3f32SAnirban Sinha 		ret = compat_ptrace_request(child, request, addr, data);
3131da177e4SLinus Torvalds 		break;
3141da177e4SLinus Torvalds 	}
3151da177e4SLinus Torvalds out:
3161da177e4SLinus Torvalds 	return ret;
3171da177e4SLinus Torvalds }
318