xref: /openbmc/linux/arch/m68k/kernel/ptrace.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
143dec91fSGreg Ungerer /*
243dec91fSGreg Ungerer  *  linux/arch/m68k/kernel/ptrace.c
343dec91fSGreg Ungerer  *
443dec91fSGreg Ungerer  *  Copyright (C) 1994 by Hamish Macdonald
543dec91fSGreg Ungerer  *  Taken from linux/kernel/ptrace.c and modified for M680x0.
643dec91fSGreg Ungerer  *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
743dec91fSGreg Ungerer  *
843dec91fSGreg Ungerer  * This file is subject to the terms and conditions of the GNU General
943dec91fSGreg Ungerer  * Public License.  See the file COPYING in the main directory of
1043dec91fSGreg Ungerer  * this archive for more details.
1143dec91fSGreg Ungerer  */
1243dec91fSGreg Ungerer 
1343dec91fSGreg Ungerer #include <linux/kernel.h>
1443dec91fSGreg Ungerer #include <linux/sched.h>
1568db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
1643dec91fSGreg Ungerer #include <linux/mm.h>
1743dec91fSGreg Ungerer #include <linux/smp.h>
1843dec91fSGreg Ungerer #include <linux/errno.h>
1943dec91fSGreg Ungerer #include <linux/ptrace.h>
2043dec91fSGreg Ungerer #include <linux/user.h>
2143dec91fSGreg Ungerer #include <linux/signal.h>
22bd53e442SGreg Ungerer #include <linux/regset.h>
23bd53e442SGreg Ungerer #include <linux/elf.h>
24*6baaade1SMichael Schmitz #include <linux/seccomp.h>
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2643dec91fSGreg Ungerer #include <asm/page.h>
2743dec91fSGreg Ungerer #include <asm/processor.h>
2843dec91fSGreg Ungerer 
2943dec91fSGreg Ungerer /*
3043dec91fSGreg Ungerer  * does not yet catch signals sent when the child dies.
3143dec91fSGreg Ungerer  * in exit.c or in signal.c.
3243dec91fSGreg Ungerer  */
3343dec91fSGreg Ungerer 
3443dec91fSGreg Ungerer /* determines which bits in the SR the user has access to. */
3543dec91fSGreg Ungerer /* 1 = access 0 = no access */
3643dec91fSGreg Ungerer #define SR_MASK 0x001f
3743dec91fSGreg Ungerer 
3843dec91fSGreg Ungerer /* sets the trace bits. */
3943dec91fSGreg Ungerer #define TRACE_BITS 0xC000
4043dec91fSGreg Ungerer #define T1_BIT 0x8000
4143dec91fSGreg Ungerer #define T0_BIT 0x4000
4243dec91fSGreg Ungerer 
4343dec91fSGreg Ungerer /* Find the stack offset for a register, relative to thread.esp0. */
4443dec91fSGreg Ungerer #define PT_REG(reg)	((long)&((struct pt_regs *)0)->reg)
4543dec91fSGreg Ungerer #define SW_REG(reg)	((long)&((struct switch_stack *)0)->reg \
4643dec91fSGreg Ungerer 			 - sizeof(struct switch_stack))
4743dec91fSGreg Ungerer /* Mapping from PT_xxx to the stack offset at which the register is
4843dec91fSGreg Ungerer    saved.  Notice that usp has no stack-slot and needs to be treated
4943dec91fSGreg Ungerer    specially (see get_reg/put_reg below). */
5043dec91fSGreg Ungerer static const int regoff[] = {
5143dec91fSGreg Ungerer 	[0]	= PT_REG(d1),
5243dec91fSGreg Ungerer 	[1]	= PT_REG(d2),
5343dec91fSGreg Ungerer 	[2]	= PT_REG(d3),
5443dec91fSGreg Ungerer 	[3]	= PT_REG(d4),
5543dec91fSGreg Ungerer 	[4]	= PT_REG(d5),
5643dec91fSGreg Ungerer 	[5]	= SW_REG(d6),
5743dec91fSGreg Ungerer 	[6]	= SW_REG(d7),
5843dec91fSGreg Ungerer 	[7]	= PT_REG(a0),
5943dec91fSGreg Ungerer 	[8]	= PT_REG(a1),
6043dec91fSGreg Ungerer 	[9]	= PT_REG(a2),
6143dec91fSGreg Ungerer 	[10]	= SW_REG(a3),
6243dec91fSGreg Ungerer 	[11]	= SW_REG(a4),
6343dec91fSGreg Ungerer 	[12]	= SW_REG(a5),
6443dec91fSGreg Ungerer 	[13]	= SW_REG(a6),
6543dec91fSGreg Ungerer 	[14]	= PT_REG(d0),
6643dec91fSGreg Ungerer 	[15]	= -1,
6743dec91fSGreg Ungerer 	[16]	= PT_REG(orig_d0),
6843dec91fSGreg Ungerer 	[17]	= PT_REG(sr),
6943dec91fSGreg Ungerer 	[18]	= PT_REG(pc),
7043dec91fSGreg Ungerer };
7143dec91fSGreg Ungerer 
7243dec91fSGreg Ungerer /*
7343dec91fSGreg Ungerer  * Get contents of register REGNO in task TASK.
7443dec91fSGreg Ungerer  */
get_reg(struct task_struct * task,int regno)7543dec91fSGreg Ungerer static inline long get_reg(struct task_struct *task, int regno)
7643dec91fSGreg Ungerer {
7743dec91fSGreg Ungerer 	unsigned long *addr;
7843dec91fSGreg Ungerer 
7943dec91fSGreg Ungerer 	if (regno == PT_USP)
8043dec91fSGreg Ungerer 		addr = &task->thread.usp;
8143dec91fSGreg Ungerer 	else if (regno < ARRAY_SIZE(regoff))
8243dec91fSGreg Ungerer 		addr = (unsigned long *)(task->thread.esp0 + regoff[regno]);
8343dec91fSGreg Ungerer 	else
8443dec91fSGreg Ungerer 		return 0;
8543dec91fSGreg Ungerer 	/* Need to take stkadj into account. */
8643dec91fSGreg Ungerer 	if (regno == PT_SR || regno == PT_PC) {
8743dec91fSGreg Ungerer 		long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj));
8843dec91fSGreg Ungerer 		addr = (unsigned long *) ((unsigned long)addr + stkadj);
8943dec91fSGreg Ungerer 		/* The sr is actually a 16 bit register.  */
9043dec91fSGreg Ungerer 		if (regno == PT_SR)
9143dec91fSGreg Ungerer 			return *(unsigned short *)addr;
9243dec91fSGreg Ungerer 	}
9343dec91fSGreg Ungerer 	return *addr;
9443dec91fSGreg Ungerer }
9543dec91fSGreg Ungerer 
9643dec91fSGreg Ungerer /*
9743dec91fSGreg Ungerer  * Write contents of register REGNO in task TASK.
9843dec91fSGreg Ungerer  */
put_reg(struct task_struct * task,int regno,unsigned long data)9943dec91fSGreg Ungerer static inline int put_reg(struct task_struct *task, int regno,
10043dec91fSGreg Ungerer 			  unsigned long data)
10143dec91fSGreg Ungerer {
10243dec91fSGreg Ungerer 	unsigned long *addr;
10343dec91fSGreg Ungerer 
10443dec91fSGreg Ungerer 	if (regno == PT_USP)
10543dec91fSGreg Ungerer 		addr = &task->thread.usp;
10643dec91fSGreg Ungerer 	else if (regno < ARRAY_SIZE(regoff))
10743dec91fSGreg Ungerer 		addr = (unsigned long *)(task->thread.esp0 + regoff[regno]);
10843dec91fSGreg Ungerer 	else
10943dec91fSGreg Ungerer 		return -1;
11043dec91fSGreg Ungerer 	/* Need to take stkadj into account. */
11143dec91fSGreg Ungerer 	if (regno == PT_SR || regno == PT_PC) {
11243dec91fSGreg Ungerer 		long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj));
11343dec91fSGreg Ungerer 		addr = (unsigned long *) ((unsigned long)addr + stkadj);
11443dec91fSGreg Ungerer 		/* The sr is actually a 16 bit register.  */
11543dec91fSGreg Ungerer 		if (regno == PT_SR) {
11643dec91fSGreg Ungerer 			*(unsigned short *)addr = data;
11743dec91fSGreg Ungerer 			return 0;
11843dec91fSGreg Ungerer 		}
11943dec91fSGreg Ungerer 	}
12043dec91fSGreg Ungerer 	*addr = data;
12143dec91fSGreg Ungerer 	return 0;
12243dec91fSGreg Ungerer }
12343dec91fSGreg Ungerer 
12443dec91fSGreg Ungerer /*
12543dec91fSGreg Ungerer  * Make sure the single step bit is not set.
12643dec91fSGreg Ungerer  */
singlestep_disable(struct task_struct * child)12743dec91fSGreg Ungerer static inline void singlestep_disable(struct task_struct *child)
12843dec91fSGreg Ungerer {
12943dec91fSGreg Ungerer 	unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS;
13043dec91fSGreg Ungerer 	put_reg(child, PT_SR, tmp);
13143dec91fSGreg Ungerer 	clear_tsk_thread_flag(child, TIF_DELAYED_TRACE);
13243dec91fSGreg Ungerer }
13343dec91fSGreg Ungerer 
13443dec91fSGreg Ungerer /*
13543dec91fSGreg Ungerer  * Called by kernel/ptrace.c when detaching..
13643dec91fSGreg Ungerer  */
ptrace_disable(struct task_struct * child)13743dec91fSGreg Ungerer void ptrace_disable(struct task_struct *child)
13843dec91fSGreg Ungerer {
13943dec91fSGreg Ungerer 	singlestep_disable(child);
14043dec91fSGreg Ungerer }
14143dec91fSGreg Ungerer 
user_enable_single_step(struct task_struct * child)14243dec91fSGreg Ungerer void user_enable_single_step(struct task_struct *child)
14343dec91fSGreg Ungerer {
14443dec91fSGreg Ungerer 	unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS;
14543dec91fSGreg Ungerer 	put_reg(child, PT_SR, tmp | T1_BIT);
14643dec91fSGreg Ungerer 	set_tsk_thread_flag(child, TIF_DELAYED_TRACE);
14743dec91fSGreg Ungerer }
14843dec91fSGreg Ungerer 
14966d857b0SGreg Ungerer #ifdef CONFIG_MMU
user_enable_block_step(struct task_struct * child)15043dec91fSGreg Ungerer void user_enable_block_step(struct task_struct *child)
15143dec91fSGreg Ungerer {
15243dec91fSGreg Ungerer 	unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS;
15343dec91fSGreg Ungerer 	put_reg(child, PT_SR, tmp | T0_BIT);
15443dec91fSGreg Ungerer }
15566d857b0SGreg Ungerer #endif
15643dec91fSGreg Ungerer 
user_disable_single_step(struct task_struct * child)15743dec91fSGreg Ungerer void user_disable_single_step(struct task_struct *child)
15843dec91fSGreg Ungerer {
15943dec91fSGreg Ungerer 	singlestep_disable(child);
16043dec91fSGreg Ungerer }
16143dec91fSGreg Ungerer 
arch_ptrace(struct task_struct * child,long request,unsigned long addr,unsigned long data)16243dec91fSGreg Ungerer long arch_ptrace(struct task_struct *child, long request,
16343dec91fSGreg Ungerer 		 unsigned long addr, unsigned long data)
16443dec91fSGreg Ungerer {
16543dec91fSGreg Ungerer 	unsigned long tmp;
16643dec91fSGreg Ungerer 	int i, ret = 0;
16743dec91fSGreg Ungerer 	int regno = addr >> 2; /* temporary hack. */
16843dec91fSGreg Ungerer 	unsigned long __user *datap = (unsigned long __user *) data;
16943dec91fSGreg Ungerer 
17043dec91fSGreg Ungerer 	switch (request) {
17143dec91fSGreg Ungerer 	/* read the word at location addr in the USER area. */
17243dec91fSGreg Ungerer 	case PTRACE_PEEKUSR:
17343dec91fSGreg Ungerer 		if (addr & 3)
17443dec91fSGreg Ungerer 			goto out_eio;
17543dec91fSGreg Ungerer 
17643dec91fSGreg Ungerer 		if (regno >= 0 && regno < 19) {
17743dec91fSGreg Ungerer 			tmp = get_reg(child, regno);
17843dec91fSGreg Ungerer 		} else if (regno >= 21 && regno < 49) {
17943dec91fSGreg Ungerer 			tmp = child->thread.fp[regno - 21];
18043dec91fSGreg Ungerer 			/* Convert internal fpu reg representation
18143dec91fSGreg Ungerer 			 * into long double format
18243dec91fSGreg Ungerer 			 */
18343dec91fSGreg Ungerer 			if (FPU_IS_EMU && (regno < 45) && !(regno % 3))
18443dec91fSGreg Ungerer 				tmp = ((tmp & 0xffff0000) << 15) |
18543dec91fSGreg Ungerer 				      ((tmp & 0x0000ffff) << 16);
18643dec91fSGreg Ungerer #ifndef CONFIG_MMU
18743dec91fSGreg Ungerer 		} else if (regno == 49) {
18843dec91fSGreg Ungerer 			tmp = child->mm->start_code;
18943dec91fSGreg Ungerer 		} else if (regno == 50) {
19043dec91fSGreg Ungerer 			tmp = child->mm->start_data;
19143dec91fSGreg Ungerer 		} else if (regno == 51) {
19243dec91fSGreg Ungerer 			tmp = child->mm->end_code;
19343dec91fSGreg Ungerer #endif
19443dec91fSGreg Ungerer 		} else
19543dec91fSGreg Ungerer 			goto out_eio;
19643dec91fSGreg Ungerer 		ret = put_user(tmp, datap);
19743dec91fSGreg Ungerer 		break;
19843dec91fSGreg Ungerer 
19943dec91fSGreg Ungerer 	case PTRACE_POKEUSR:
20043dec91fSGreg Ungerer 	/* write the word at location addr in the USER area */
20143dec91fSGreg Ungerer 		if (addr & 3)
20243dec91fSGreg Ungerer 			goto out_eio;
20343dec91fSGreg Ungerer 
20443dec91fSGreg Ungerer 		if (regno == PT_SR) {
20543dec91fSGreg Ungerer 			data &= SR_MASK;
20643dec91fSGreg Ungerer 			data |= get_reg(child, PT_SR) & ~SR_MASK;
20743dec91fSGreg Ungerer 		}
20843dec91fSGreg Ungerer 		if (regno >= 0 && regno < 19) {
20943dec91fSGreg Ungerer 			if (put_reg(child, regno, data))
21043dec91fSGreg Ungerer 				goto out_eio;
21143dec91fSGreg Ungerer 		} else if (regno >= 21 && regno < 48) {
21243dec91fSGreg Ungerer 			/* Convert long double format
21343dec91fSGreg Ungerer 			 * into internal fpu reg representation
21443dec91fSGreg Ungerer 			 */
21543dec91fSGreg Ungerer 			if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) {
21643dec91fSGreg Ungerer 				data <<= 15;
21743dec91fSGreg Ungerer 				data = (data & 0xffff0000) |
21843dec91fSGreg Ungerer 				       ((data & 0x0000ffff) >> 1);
21943dec91fSGreg Ungerer 			}
22043dec91fSGreg Ungerer 			child->thread.fp[regno - 21] = data;
22143dec91fSGreg Ungerer 		} else
22243dec91fSGreg Ungerer 			goto out_eio;
22343dec91fSGreg Ungerer 		break;
22443dec91fSGreg Ungerer 
22543dec91fSGreg Ungerer 	case PTRACE_GETREGS:	/* Get all gp regs from the child. */
22643dec91fSGreg Ungerer 		for (i = 0; i < 19; i++) {
22743dec91fSGreg Ungerer 			tmp = get_reg(child, i);
22843dec91fSGreg Ungerer 			ret = put_user(tmp, datap);
22943dec91fSGreg Ungerer 			if (ret)
23043dec91fSGreg Ungerer 				break;
23143dec91fSGreg Ungerer 			datap++;
23243dec91fSGreg Ungerer 		}
23343dec91fSGreg Ungerer 		break;
23443dec91fSGreg Ungerer 
23543dec91fSGreg Ungerer 	case PTRACE_SETREGS:	/* Set all gp regs in the child. */
23643dec91fSGreg Ungerer 		for (i = 0; i < 19; i++) {
23743dec91fSGreg Ungerer 			ret = get_user(tmp, datap);
23843dec91fSGreg Ungerer 			if (ret)
23943dec91fSGreg Ungerer 				break;
24043dec91fSGreg Ungerer 			if (i == PT_SR) {
24143dec91fSGreg Ungerer 				tmp &= SR_MASK;
24243dec91fSGreg Ungerer 				tmp |= get_reg(child, PT_SR) & ~SR_MASK;
24343dec91fSGreg Ungerer 			}
24443dec91fSGreg Ungerer 			put_reg(child, i, tmp);
24543dec91fSGreg Ungerer 			datap++;
24643dec91fSGreg Ungerer 		}
24743dec91fSGreg Ungerer 		break;
24843dec91fSGreg Ungerer 
24943dec91fSGreg Ungerer 	case PTRACE_GETFPREGS:	/* Get the child FPU state. */
25043dec91fSGreg Ungerer 		if (copy_to_user(datap, &child->thread.fp,
25143dec91fSGreg Ungerer 				 sizeof(struct user_m68kfp_struct)))
25243dec91fSGreg Ungerer 			ret = -EFAULT;
25343dec91fSGreg Ungerer 		break;
25443dec91fSGreg Ungerer 
25543dec91fSGreg Ungerer 	case PTRACE_SETFPREGS:	/* Set the child FPU state. */
25643dec91fSGreg Ungerer 		if (copy_from_user(&child->thread.fp, datap,
25743dec91fSGreg Ungerer 				   sizeof(struct user_m68kfp_struct)))
25843dec91fSGreg Ungerer 			ret = -EFAULT;
25943dec91fSGreg Ungerer 		break;
26043dec91fSGreg Ungerer 
26143dec91fSGreg Ungerer 	case PTRACE_GET_THREAD_AREA:
26243dec91fSGreg Ungerer 		ret = put_user(task_thread_info(child)->tp_value, datap);
26343dec91fSGreg Ungerer 		break;
26443dec91fSGreg Ungerer 
26543dec91fSGreg Ungerer 	default:
26643dec91fSGreg Ungerer 		ret = ptrace_request(child, request, addr, data);
26743dec91fSGreg Ungerer 		break;
26843dec91fSGreg Ungerer 	}
26943dec91fSGreg Ungerer 
27043dec91fSGreg Ungerer 	return ret;
27143dec91fSGreg Ungerer out_eio:
27243dec91fSGreg Ungerer 	return -EIO;
27343dec91fSGreg Ungerer }
27443dec91fSGreg Ungerer 
syscall_trace_enter(void)27543dec91fSGreg Ungerer asmlinkage int syscall_trace_enter(void)
27643dec91fSGreg Ungerer {
27743dec91fSGreg Ungerer 	int ret = 0;
27843dec91fSGreg Ungerer 
27943dec91fSGreg Ungerer 	if (test_thread_flag(TIF_SYSCALL_TRACE))
280153474baSEric W. Biederman 		ret = ptrace_report_syscall_entry(task_pt_regs(current));
281*6baaade1SMichael Schmitz 
282*6baaade1SMichael Schmitz 	if (secure_computing() == -1)
283*6baaade1SMichael Schmitz 		return -1;
284*6baaade1SMichael Schmitz 
28543dec91fSGreg Ungerer 	return ret;
28643dec91fSGreg Ungerer }
28743dec91fSGreg Ungerer 
syscall_trace_leave(void)28843dec91fSGreg Ungerer asmlinkage void syscall_trace_leave(void)
28943dec91fSGreg Ungerer {
29043dec91fSGreg Ungerer 	if (test_thread_flag(TIF_SYSCALL_TRACE))
291153474baSEric W. Biederman 		ptrace_report_syscall_exit(task_pt_regs(current), 0);
29243dec91fSGreg Ungerer }
293bd53e442SGreg Ungerer 
294bd53e442SGreg Ungerer #if defined(CONFIG_BINFMT_ELF_FDPIC) && defined(CONFIG_ELF_CORE)
295bd53e442SGreg Ungerer /*
296bd53e442SGreg Ungerer  * Currently the only thing that needs to use regsets for m68k is the
297bd53e442SGreg Ungerer  * coredump support of the elf_fdpic loader. Implement the minimum
298bd53e442SGreg Ungerer  * definitions required for that.
299bd53e442SGreg Ungerer  */
m68k_regset_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)300bd53e442SGreg Ungerer static int m68k_regset_get(struct task_struct *target,
301bd53e442SGreg Ungerer 			   const struct user_regset *regset,
302bd53e442SGreg Ungerer 			   struct membuf to)
303bd53e442SGreg Ungerer {
304bd53e442SGreg Ungerer 	struct pt_regs *ptregs = task_pt_regs(target);
305bd53e442SGreg Ungerer 	u32 uregs[ELF_NGREG];
306bd53e442SGreg Ungerer 
307bd53e442SGreg Ungerer 	ELF_CORE_COPY_REGS(uregs, ptregs);
308bd53e442SGreg Ungerer 	return membuf_write(&to, uregs, sizeof(uregs));
309bd53e442SGreg Ungerer }
310bd53e442SGreg Ungerer 
311bd53e442SGreg Ungerer enum m68k_regset {
312bd53e442SGreg Ungerer 	REGSET_GPR,
313bd53e442SGreg Ungerer #ifdef CONFIG_FPU
314bd53e442SGreg Ungerer 	REGSET_FPU,
315bd53e442SGreg Ungerer #endif
316bd53e442SGreg Ungerer };
317bd53e442SGreg Ungerer 
318bd53e442SGreg Ungerer static const struct user_regset m68k_user_regsets[] = {
319bd53e442SGreg Ungerer 	[REGSET_GPR] = {
320bd53e442SGreg Ungerer 		.core_note_type = NT_PRSTATUS,
321bd53e442SGreg Ungerer 		.n = ELF_NGREG,
322bd53e442SGreg Ungerer 		.size = sizeof(u32),
323bd53e442SGreg Ungerer 		.align = sizeof(u16),
324bd53e442SGreg Ungerer 		.regset_get = m68k_regset_get,
325bd53e442SGreg Ungerer 	},
326bd53e442SGreg Ungerer #ifdef CONFIG_FPU
327bd53e442SGreg Ungerer 	[REGSET_FPU] = {
328bd53e442SGreg Ungerer 		.core_note_type = NT_PRFPREG,
329bd53e442SGreg Ungerer 		.n = sizeof(struct user_m68kfp_struct) / sizeof(u32),
330bd53e442SGreg Ungerer 		.size = sizeof(u32),
331bd53e442SGreg Ungerer 		.align = sizeof(u32),
332bd53e442SGreg Ungerer 	}
333bd53e442SGreg Ungerer #endif /* CONFIG_FPU */
334bd53e442SGreg Ungerer };
335bd53e442SGreg Ungerer 
336bd53e442SGreg Ungerer static const struct user_regset_view user_m68k_view = {
337bd53e442SGreg Ungerer 	.name = "m68k",
338bd53e442SGreg Ungerer 	.e_machine = EM_68K,
339bd53e442SGreg Ungerer 	.ei_osabi = ELF_OSABI,
340bd53e442SGreg Ungerer 	.regsets = m68k_user_regsets,
341bd53e442SGreg Ungerer 	.n = ARRAY_SIZE(m68k_user_regsets)
342bd53e442SGreg Ungerer };
343bd53e442SGreg Ungerer 
task_user_regset_view(struct task_struct * task)344bd53e442SGreg Ungerer const struct user_regset_view *task_user_regset_view(struct task_struct *task)
345bd53e442SGreg Ungerer {
346bd53e442SGreg Ungerer 	return &user_m68k_view;
347bd53e442SGreg Ungerer }
348bd53e442SGreg Ungerer #endif /* CONFIG_BINFMT_ELF_FDPIC && CONFIG_ELF_CORE */
349