162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/m68k/kernel/ptrace.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1994 by Hamish Macdonald 562306a36Sopenharmony_ci * Taken from linux/kernel/ptrace.c and modified for M680x0. 662306a36Sopenharmony_ci * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 962306a36Sopenharmony_ci * Public License. See the file COPYING in the main directory of 1062306a36Sopenharmony_ci * this archive for more details. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/smp.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/ptrace.h> 2062306a36Sopenharmony_ci#include <linux/user.h> 2162306a36Sopenharmony_ci#include <linux/signal.h> 2262306a36Sopenharmony_ci#include <linux/regset.h> 2362306a36Sopenharmony_ci#include <linux/elf.h> 2462306a36Sopenharmony_ci#include <linux/seccomp.h> 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci#include <asm/page.h> 2762306a36Sopenharmony_ci#include <asm/processor.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * does not yet catch signals sent when the child dies. 3162306a36Sopenharmony_ci * in exit.c or in signal.c. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* determines which bits in the SR the user has access to. */ 3562306a36Sopenharmony_ci/* 1 = access 0 = no access */ 3662306a36Sopenharmony_ci#define SR_MASK 0x001f 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* sets the trace bits. */ 3962306a36Sopenharmony_ci#define TRACE_BITS 0xC000 4062306a36Sopenharmony_ci#define T1_BIT 0x8000 4162306a36Sopenharmony_ci#define T0_BIT 0x4000 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Find the stack offset for a register, relative to thread.esp0. */ 4462306a36Sopenharmony_ci#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) 4562306a36Sopenharmony_ci#define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ 4662306a36Sopenharmony_ci - sizeof(struct switch_stack)) 4762306a36Sopenharmony_ci/* Mapping from PT_xxx to the stack offset at which the register is 4862306a36Sopenharmony_ci saved. Notice that usp has no stack-slot and needs to be treated 4962306a36Sopenharmony_ci specially (see get_reg/put_reg below). */ 5062306a36Sopenharmony_cistatic const int regoff[] = { 5162306a36Sopenharmony_ci [0] = PT_REG(d1), 5262306a36Sopenharmony_ci [1] = PT_REG(d2), 5362306a36Sopenharmony_ci [2] = PT_REG(d3), 5462306a36Sopenharmony_ci [3] = PT_REG(d4), 5562306a36Sopenharmony_ci [4] = PT_REG(d5), 5662306a36Sopenharmony_ci [5] = SW_REG(d6), 5762306a36Sopenharmony_ci [6] = SW_REG(d7), 5862306a36Sopenharmony_ci [7] = PT_REG(a0), 5962306a36Sopenharmony_ci [8] = PT_REG(a1), 6062306a36Sopenharmony_ci [9] = PT_REG(a2), 6162306a36Sopenharmony_ci [10] = SW_REG(a3), 6262306a36Sopenharmony_ci [11] = SW_REG(a4), 6362306a36Sopenharmony_ci [12] = SW_REG(a5), 6462306a36Sopenharmony_ci [13] = SW_REG(a6), 6562306a36Sopenharmony_ci [14] = PT_REG(d0), 6662306a36Sopenharmony_ci [15] = -1, 6762306a36Sopenharmony_ci [16] = PT_REG(orig_d0), 6862306a36Sopenharmony_ci [17] = PT_REG(sr), 6962306a36Sopenharmony_ci [18] = PT_REG(pc), 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * Get contents of register REGNO in task TASK. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic inline long get_reg(struct task_struct *task, int regno) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci unsigned long *addr; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (regno == PT_USP) 8062306a36Sopenharmony_ci addr = &task->thread.usp; 8162306a36Sopenharmony_ci else if (regno < ARRAY_SIZE(regoff)) 8262306a36Sopenharmony_ci addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci /* Need to take stkadj into account. */ 8662306a36Sopenharmony_ci if (regno == PT_SR || regno == PT_PC) { 8762306a36Sopenharmony_ci long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); 8862306a36Sopenharmony_ci addr = (unsigned long *) ((unsigned long)addr + stkadj); 8962306a36Sopenharmony_ci /* The sr is actually a 16 bit register. */ 9062306a36Sopenharmony_ci if (regno == PT_SR) 9162306a36Sopenharmony_ci return *(unsigned short *)addr; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci return *addr; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Write contents of register REGNO in task TASK. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic inline int put_reg(struct task_struct *task, int regno, 10062306a36Sopenharmony_ci unsigned long data) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci unsigned long *addr; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (regno == PT_USP) 10562306a36Sopenharmony_ci addr = &task->thread.usp; 10662306a36Sopenharmony_ci else if (regno < ARRAY_SIZE(regoff)) 10762306a36Sopenharmony_ci addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); 10862306a36Sopenharmony_ci else 10962306a36Sopenharmony_ci return -1; 11062306a36Sopenharmony_ci /* Need to take stkadj into account. */ 11162306a36Sopenharmony_ci if (regno == PT_SR || regno == PT_PC) { 11262306a36Sopenharmony_ci long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); 11362306a36Sopenharmony_ci addr = (unsigned long *) ((unsigned long)addr + stkadj); 11462306a36Sopenharmony_ci /* The sr is actually a 16 bit register. */ 11562306a36Sopenharmony_ci if (regno == PT_SR) { 11662306a36Sopenharmony_ci *(unsigned short *)addr = data; 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci *addr = data; 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Make sure the single step bit is not set. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic inline void singlestep_disable(struct task_struct *child) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; 13062306a36Sopenharmony_ci put_reg(child, PT_SR, tmp); 13162306a36Sopenharmony_ci clear_tsk_thread_flag(child, TIF_DELAYED_TRACE); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Called by kernel/ptrace.c when detaching.. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_civoid ptrace_disable(struct task_struct *child) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci singlestep_disable(child); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid user_enable_single_step(struct task_struct *child) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; 14562306a36Sopenharmony_ci put_reg(child, PT_SR, tmp | T1_BIT); 14662306a36Sopenharmony_ci set_tsk_thread_flag(child, TIF_DELAYED_TRACE); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#ifdef CONFIG_MMU 15062306a36Sopenharmony_civoid user_enable_block_step(struct task_struct *child) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; 15362306a36Sopenharmony_ci put_reg(child, PT_SR, tmp | T0_BIT); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci#endif 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_civoid user_disable_single_step(struct task_struct *child) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci singlestep_disable(child); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cilong arch_ptrace(struct task_struct *child, long request, 16362306a36Sopenharmony_ci unsigned long addr, unsigned long data) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci unsigned long tmp; 16662306a36Sopenharmony_ci int i, ret = 0; 16762306a36Sopenharmony_ci int regno = addr >> 2; /* temporary hack. */ 16862306a36Sopenharmony_ci unsigned long __user *datap = (unsigned long __user *) data; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci switch (request) { 17162306a36Sopenharmony_ci /* read the word at location addr in the USER area. */ 17262306a36Sopenharmony_ci case PTRACE_PEEKUSR: 17362306a36Sopenharmony_ci if (addr & 3) 17462306a36Sopenharmony_ci goto out_eio; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (regno >= 0 && regno < 19) { 17762306a36Sopenharmony_ci tmp = get_reg(child, regno); 17862306a36Sopenharmony_ci } else if (regno >= 21 && regno < 49) { 17962306a36Sopenharmony_ci tmp = child->thread.fp[regno - 21]; 18062306a36Sopenharmony_ci /* Convert internal fpu reg representation 18162306a36Sopenharmony_ci * into long double format 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) 18462306a36Sopenharmony_ci tmp = ((tmp & 0xffff0000) << 15) | 18562306a36Sopenharmony_ci ((tmp & 0x0000ffff) << 16); 18662306a36Sopenharmony_ci#ifndef CONFIG_MMU 18762306a36Sopenharmony_ci } else if (regno == 49) { 18862306a36Sopenharmony_ci tmp = child->mm->start_code; 18962306a36Sopenharmony_ci } else if (regno == 50) { 19062306a36Sopenharmony_ci tmp = child->mm->start_data; 19162306a36Sopenharmony_ci } else if (regno == 51) { 19262306a36Sopenharmony_ci tmp = child->mm->end_code; 19362306a36Sopenharmony_ci#endif 19462306a36Sopenharmony_ci } else 19562306a36Sopenharmony_ci goto out_eio; 19662306a36Sopenharmony_ci ret = put_user(tmp, datap); 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci case PTRACE_POKEUSR: 20062306a36Sopenharmony_ci /* write the word at location addr in the USER area */ 20162306a36Sopenharmony_ci if (addr & 3) 20262306a36Sopenharmony_ci goto out_eio; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (regno == PT_SR) { 20562306a36Sopenharmony_ci data &= SR_MASK; 20662306a36Sopenharmony_ci data |= get_reg(child, PT_SR) & ~SR_MASK; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci if (regno >= 0 && regno < 19) { 20962306a36Sopenharmony_ci if (put_reg(child, regno, data)) 21062306a36Sopenharmony_ci goto out_eio; 21162306a36Sopenharmony_ci } else if (regno >= 21 && regno < 48) { 21262306a36Sopenharmony_ci /* Convert long double format 21362306a36Sopenharmony_ci * into internal fpu reg representation 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) { 21662306a36Sopenharmony_ci data <<= 15; 21762306a36Sopenharmony_ci data = (data & 0xffff0000) | 21862306a36Sopenharmony_ci ((data & 0x0000ffff) >> 1); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci child->thread.fp[regno - 21] = data; 22162306a36Sopenharmony_ci } else 22262306a36Sopenharmony_ci goto out_eio; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci case PTRACE_GETREGS: /* Get all gp regs from the child. */ 22662306a36Sopenharmony_ci for (i = 0; i < 19; i++) { 22762306a36Sopenharmony_ci tmp = get_reg(child, i); 22862306a36Sopenharmony_ci ret = put_user(tmp, datap); 22962306a36Sopenharmony_ci if (ret) 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci datap++; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci case PTRACE_SETREGS: /* Set all gp regs in the child. */ 23662306a36Sopenharmony_ci for (i = 0; i < 19; i++) { 23762306a36Sopenharmony_ci ret = get_user(tmp, datap); 23862306a36Sopenharmony_ci if (ret) 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci if (i == PT_SR) { 24162306a36Sopenharmony_ci tmp &= SR_MASK; 24262306a36Sopenharmony_ci tmp |= get_reg(child, PT_SR) & ~SR_MASK; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci put_reg(child, i, tmp); 24562306a36Sopenharmony_ci datap++; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci case PTRACE_GETFPREGS: /* Get the child FPU state. */ 25062306a36Sopenharmony_ci if (copy_to_user(datap, &child->thread.fp, 25162306a36Sopenharmony_ci sizeof(struct user_m68kfp_struct))) 25262306a36Sopenharmony_ci ret = -EFAULT; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci case PTRACE_SETFPREGS: /* Set the child FPU state. */ 25662306a36Sopenharmony_ci if (copy_from_user(&child->thread.fp, datap, 25762306a36Sopenharmony_ci sizeof(struct user_m68kfp_struct))) 25862306a36Sopenharmony_ci ret = -EFAULT; 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci case PTRACE_GET_THREAD_AREA: 26262306a36Sopenharmony_ci ret = put_user(task_thread_info(child)->tp_value, datap); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci ret = ptrace_request(child, request, addr, data); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ciout_eio: 27262306a36Sopenharmony_ci return -EIO; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciasmlinkage int syscall_trace_enter(void) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci int ret = 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (test_thread_flag(TIF_SYSCALL_TRACE)) 28062306a36Sopenharmony_ci ret = ptrace_report_syscall_entry(task_pt_regs(current)); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (secure_computing() == -1) 28362306a36Sopenharmony_ci return -1; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciasmlinkage void syscall_trace_leave(void) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci if (test_thread_flag(TIF_SYSCALL_TRACE)) 29162306a36Sopenharmony_ci ptrace_report_syscall_exit(task_pt_regs(current), 0); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci#if defined(CONFIG_BINFMT_ELF_FDPIC) && defined(CONFIG_ELF_CORE) 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * Currently the only thing that needs to use regsets for m68k is the 29762306a36Sopenharmony_ci * coredump support of the elf_fdpic loader. Implement the minimum 29862306a36Sopenharmony_ci * definitions required for that. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic int m68k_regset_get(struct task_struct *target, 30162306a36Sopenharmony_ci const struct user_regset *regset, 30262306a36Sopenharmony_ci struct membuf to) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct pt_regs *ptregs = task_pt_regs(target); 30562306a36Sopenharmony_ci u32 uregs[ELF_NGREG]; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ELF_CORE_COPY_REGS(uregs, ptregs); 30862306a36Sopenharmony_ci return membuf_write(&to, uregs, sizeof(uregs)); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cienum m68k_regset { 31262306a36Sopenharmony_ci REGSET_GPR, 31362306a36Sopenharmony_ci#ifdef CONFIG_FPU 31462306a36Sopenharmony_ci REGSET_FPU, 31562306a36Sopenharmony_ci#endif 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic const struct user_regset m68k_user_regsets[] = { 31962306a36Sopenharmony_ci [REGSET_GPR] = { 32062306a36Sopenharmony_ci .core_note_type = NT_PRSTATUS, 32162306a36Sopenharmony_ci .n = ELF_NGREG, 32262306a36Sopenharmony_ci .size = sizeof(u32), 32362306a36Sopenharmony_ci .align = sizeof(u16), 32462306a36Sopenharmony_ci .regset_get = m68k_regset_get, 32562306a36Sopenharmony_ci }, 32662306a36Sopenharmony_ci#ifdef CONFIG_FPU 32762306a36Sopenharmony_ci [REGSET_FPU] = { 32862306a36Sopenharmony_ci .core_note_type = NT_PRFPREG, 32962306a36Sopenharmony_ci .n = sizeof(struct user_m68kfp_struct) / sizeof(u32), 33062306a36Sopenharmony_ci .size = sizeof(u32), 33162306a36Sopenharmony_ci .align = sizeof(u32), 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci#endif /* CONFIG_FPU */ 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic const struct user_regset_view user_m68k_view = { 33762306a36Sopenharmony_ci .name = "m68k", 33862306a36Sopenharmony_ci .e_machine = EM_68K, 33962306a36Sopenharmony_ci .ei_osabi = ELF_OSABI, 34062306a36Sopenharmony_ci .regsets = m68k_user_regsets, 34162306a36Sopenharmony_ci .n = ARRAY_SIZE(m68k_user_regsets) 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciconst struct user_regset_view *task_user_regset_view(struct task_struct *task) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return &user_m68k_view; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci#endif /* CONFIG_BINFMT_ELF_FDPIC && CONFIG_ELF_CORE */ 349