xref: /linux/arch/microblaze/kernel/ptrace.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12b438454SMichal Simek /*
22b438454SMichal Simek  * `ptrace' system call
32b438454SMichal Simek  *
42b438454SMichal Simek  * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
52b438454SMichal Simek  * Copyright (C) 2007-2009 PetaLogix
62b438454SMichal Simek  * Copyright (C) 2004-2007 John Williams <john.williams@petalogix.com>
72b438454SMichal Simek  *
82b438454SMichal Simek  * derived from arch/v850/kernel/ptrace.c
92b438454SMichal Simek  *
102b438454SMichal Simek  * Copyright (C) 2002,03 NEC Electronics Corporation
112b438454SMichal Simek  * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
122b438454SMichal Simek  *
132b438454SMichal Simek  * Derived from arch/mips/kernel/ptrace.c:
142b438454SMichal Simek  *
152b438454SMichal Simek  * Copyright (C) 1992 Ross Biro
162b438454SMichal Simek  * Copyright (C) Linus Torvalds
172b438454SMichal Simek  * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
182b438454SMichal Simek  * Copyright (C) 1996 David S. Miller
192b438454SMichal Simek  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
202b438454SMichal Simek  * Copyright (C) 1999 MIPS Technologies, Inc.
212b438454SMichal Simek  *
222b438454SMichal Simek  * This file is subject to the terms and conditions of the GNU General
232b438454SMichal Simek  * Public License. See the file COPYING in the main directory of this
242b438454SMichal Simek  * archive for more details.
252b438454SMichal Simek  */
262b438454SMichal Simek 
272b438454SMichal Simek #include <linux/kernel.h>
282b438454SMichal Simek #include <linux/mm.h>
292b438454SMichal Simek #include <linux/sched.h>
3068db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
312b438454SMichal Simek #include <linux/ptrace.h>
322b438454SMichal Simek #include <linux/signal.h>
3323575483SMichal Simek #include <linux/elf.h>
3423575483SMichal Simek #include <linux/audit.h>
3523575483SMichal Simek #include <linux/seccomp.h>
362b438454SMichal Simek 
372b438454SMichal Simek #include <linux/errno.h>
382b438454SMichal Simek #include <asm/processor.h>
392b438454SMichal Simek #include <linux/uaccess.h>
402b438454SMichal Simek #include <asm/asm-offsets.h>
416847ba91SMichal Simek #include <asm/cacheflush.h>
42c1df53b3SMichal Simek #include <asm/syscall.h>
436bd55f0bSMichal Simek #include <linux/io.h>
442b438454SMichal Simek 
452b438454SMichal Simek /* Returns the address where the register at REG_OFFS in P is stashed away. */
reg_save_addr(unsigned reg_offs,struct task_struct * t)462b438454SMichal Simek static microblaze_reg_t *reg_save_addr(unsigned reg_offs,
472b438454SMichal Simek 					struct task_struct *t)
482b438454SMichal Simek {
492b438454SMichal Simek 	struct pt_regs *regs;
502b438454SMichal Simek 
512b438454SMichal Simek 	/*
522b438454SMichal Simek 	 * Three basic cases:
532b438454SMichal Simek 	 *
542b438454SMichal Simek 	 * (1)	A register normally saved before calling the scheduler, is
552b438454SMichal Simek 	 *	available in the kernel entry pt_regs structure at the top
562b438454SMichal Simek 	 *	of the kernel stack. The kernel trap/irq exit path takes
572b438454SMichal Simek 	 *	care to save/restore almost all registers for ptrace'd
582b438454SMichal Simek 	 *	processes.
592b438454SMichal Simek 	 *
602b438454SMichal Simek 	 * (2)	A call-clobbered register, where the process P entered the
612b438454SMichal Simek 	 *	kernel via [syscall] trap, is not stored anywhere; that's
622b438454SMichal Simek 	 *	OK, because such registers are not expected to be preserved
632b438454SMichal Simek 	 *	when the trap returns anyway (so we don't actually bother to
642b438454SMichal Simek 	 *	test for this case).
652b438454SMichal Simek 	 *
662b438454SMichal Simek 	 * (3)	A few registers not used at all by the kernel, and so
672b438454SMichal Simek 	 *	normally never saved except by context-switches, are in the
682b438454SMichal Simek 	 *	context switch state.
692b438454SMichal Simek 	 */
702b438454SMichal Simek 
712b438454SMichal Simek 	/* Register saved during kernel entry (or not available). */
722b438454SMichal Simek 	regs = task_pt_regs(t);
732b438454SMichal Simek 
742b438454SMichal Simek 	return (microblaze_reg_t *)((char *)regs + reg_offs);
752b438454SMichal Simek }
762b438454SMichal Simek 
arch_ptrace(struct task_struct * child,long request,unsigned long addr,unsigned long data)779b05a69eSNamhyung Kim long arch_ptrace(struct task_struct *child, long request,
789b05a69eSNamhyung Kim 		 unsigned long addr, unsigned long data)
792b438454SMichal Simek {
802b438454SMichal Simek 	int rval;
812b438454SMichal Simek 	unsigned long val = 0;
822b438454SMichal Simek 
832b438454SMichal Simek 	switch (request) {
842b438454SMichal Simek 	/* Read/write the word at location ADDR in the registers. */
852b438454SMichal Simek 	case PTRACE_PEEKUSR:
862b438454SMichal Simek 	case PTRACE_POKEUSR:
872b438454SMichal Simek 		pr_debug("PEEKUSR/POKEUSR : 0x%08lx\n", addr);
882b438454SMichal Simek 		rval = 0;
892b438454SMichal Simek 		if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) {
902b438454SMichal Simek 			/*
912b438454SMichal Simek 			 * Special requests that don't actually correspond
922b438454SMichal Simek 			 * to offsets in struct pt_regs.
932b438454SMichal Simek 			 */
942b438454SMichal Simek 			if (addr == PT_TEXT_ADDR) {
952b438454SMichal Simek 				val = child->mm->start_code;
962b438454SMichal Simek 			} else if (addr == PT_DATA_ADDR) {
972b438454SMichal Simek 				val = child->mm->start_data;
982b438454SMichal Simek 			} else if (addr == PT_TEXT_LEN) {
992b438454SMichal Simek 				val = child->mm->end_code
1002b438454SMichal Simek 					- child->mm->start_code;
1012b438454SMichal Simek 			} else {
1022b438454SMichal Simek 				rval = -EIO;
1032b438454SMichal Simek 			}
104cfd866f6SNamhyung Kim 		} else if (addr < PT_SIZE && (addr & 0x3) == 0) {
1052b438454SMichal Simek 			microblaze_reg_t *reg_addr = reg_save_addr(addr, child);
1062b438454SMichal Simek 			if (request == PTRACE_PEEKUSR)
1072b438454SMichal Simek 				val = *reg_addr;
1086847ba91SMichal Simek 			else {
1096847ba91SMichal Simek #if 1
1102b438454SMichal Simek 				*reg_addr = data;
1116847ba91SMichal Simek #else
1126847ba91SMichal Simek 				/* MS potential problem on WB system
1136847ba91SMichal Simek 				 * Be aware that reg_addr is virtual address
1146847ba91SMichal Simek 				 * virt_to_phys conversion is necessary.
1156847ba91SMichal Simek 				 * This could be sensible solution.
1166847ba91SMichal Simek 				 */
1176847ba91SMichal Simek 				u32 paddr = virt_to_phys((u32)reg_addr);
1186847ba91SMichal Simek 				invalidate_icache_range(paddr, paddr + 4);
1196847ba91SMichal Simek 				*reg_addr = data;
1206847ba91SMichal Simek 				flush_dcache_range(paddr, paddr + 4);
1216847ba91SMichal Simek #endif
1226847ba91SMichal Simek 			}
1232b438454SMichal Simek 		} else
1242b438454SMichal Simek 			rval = -EIO;
1252b438454SMichal Simek 
1262b438454SMichal Simek 		if (rval == 0 && request == PTRACE_PEEKUSR)
127c1df53b3SMichal Simek 			rval = put_user(val, (unsigned long __user *)data);
1282b438454SMichal Simek 		break;
1292b438454SMichal Simek 	default:
130b3c1e01aSChristoph Hellwig 		rval = ptrace_request(child, request, addr, data);
1312b438454SMichal Simek 	}
1322b438454SMichal Simek 	return rval;
1332b438454SMichal Simek }
1342b438454SMichal Simek 
do_syscall_trace_enter(struct pt_regs * regs)1358543e6c9SMichal Simek asmlinkage unsigned long do_syscall_trace_enter(struct pt_regs *regs)
13623575483SMichal Simek {
1378543e6c9SMichal Simek 	unsigned long ret = 0;
13823575483SMichal Simek 
139e4da89d0SWill Drewry 	secure_computing_strict(regs->r12);
14023575483SMichal Simek 
14123575483SMichal Simek 	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
142*153474baSEric W. Biederman 	    ptrace_report_syscall_entry(regs))
14323575483SMichal Simek 		/*
14423575483SMichal Simek 		 * Tracing decided this syscall should not happen.
14523575483SMichal Simek 		 * We'll return a bogus call number to get an ENOSYS
14623575483SMichal Simek 		 * error, but leave the original number in regs->regs[0].
14723575483SMichal Simek 		 */
14823575483SMichal Simek 		ret = -1L;
14923575483SMichal Simek 
15091397401SEric Paris 	audit_syscall_entry(regs->r12, regs->r5, regs->r6, regs->r7, regs->r8);
15123575483SMichal Simek 
15223575483SMichal Simek 	return ret ?: regs->r12;
15323575483SMichal Simek }
15423575483SMichal Simek 
do_syscall_trace_leave(struct pt_regs * regs)15523575483SMichal Simek asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
15623575483SMichal Simek {
15723575483SMichal Simek 	int step;
15823575483SMichal Simek 
159d7e7528bSEric Paris 	audit_syscall_exit(regs);
16023575483SMichal Simek 
16123575483SMichal Simek 	step = test_thread_flag(TIF_SINGLESTEP);
16223575483SMichal Simek 	if (step || test_thread_flag(TIF_SYSCALL_TRACE))
163*153474baSEric W. Biederman 		ptrace_report_syscall_exit(regs, step);
16423575483SMichal Simek }
16523575483SMichal Simek 
ptrace_disable(struct task_struct * child)1662b438454SMichal Simek void ptrace_disable(struct task_struct *child)
1672b438454SMichal Simek {
1682b438454SMichal Simek 	/* nothing to do */
1692b438454SMichal Simek }
170