text: // SPDX-License-Identifier: GPL-2.0-only
  /*
   * linux/arch/unicore32/kernel/ptrace.c
   *
   * Code specific to PKUnity SoC and UniCore ISA
   *
   * Copyright (C) 2001-2010 GUAN Xue-tao
   *
   * By Ross Biro 1/23/92
   */
  #include <linux/kernel.h>
  #include <linux/ptrace.h>
  #include <linux/signal.h>
  #include <linux/uaccess.h>
  #include <linux/sched/task_stack.h>
  
  /*
   * this routine will get a word off of the processes privileged stack.
   * the offset is how far from the base addr as stored in the THREAD.
   * this routine assumes that all the privileged stacks are in our
   * data space.
   */
  static inline long get_user_reg(struct task_struct *task, int offset)
  {
  	return task_pt_regs(task)->uregs[offset];
  }
  
  /*
   * this routine will put a word on the processes privileged stack.
   * the offset is how far from the base addr as stored in the THREAD.
   * this routine assumes that all the privileged stacks are in our
   * data space.
   */
  static inline int
  put_user_reg(struct task_struct *task, int offset, long data)
  {
  	struct pt_regs newregs, *regs = task_pt_regs(task);
  	int ret = -EINVAL;
  
  	newregs = *regs;
  	newregs.uregs[offset] = data;
  
  	if (valid_user_regs(&newregs)) {
  		regs->uregs[offset] = data;
  		ret = 0;
  	}
  
  	return ret;
  }
  
  /*
   * Called by kernel/ptrace.c when detaching..
   */
  void ptrace_disable(struct task_struct *child)
  {
  }
  
  /*
   * We actually access the pt_regs stored on the kernel stack.
   */
  static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
  			    unsigned long __user *ret)
  {
  	unsigned long tmp;
  
  	tmp = 0;
  	if (off < sizeof(struct pt_regs))
  		tmp = get_user_reg(tsk, off >> 2);
  
  	return put_user(tmp, ret);
  }
  
  /*
   * We actually access the pt_regs stored on the kernel stack.
   */
  static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
  			     unsigned long val)
  {
  	if (off >= sizeof(struct pt_regs))
  		return 0;
  
  	return put_user_reg(tsk, off >> 2, val);
  }
  
  long arch_ptrace(struct task_struct *child, long request,
  		 unsigned long addr, unsigned long data)
  {
  	int ret;
  	unsigned long __user *datap = (unsigned long __user *) data;
  
  	switch (request) {
  	case PTRACE_PEEKUSR:
  		ret = ptrace_read_user(child, addr, datap);
  		break;
  
  	case PTRACE_POKEUSR:
  		ret = ptrace_write_user(child, addr, data);
  		break;
  
  	case PTRACE_GET_THREAD_AREA:
  		ret = put_user(task_pt_regs(child)->UCreg_16,
  			       datap);
  		break;
  
  	default:
  		ret = ptrace_request(child, request, addr, data);
  		break;
  	}
  
  	return ret;
  }
  
  asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
  {
  	unsigned long ip;
  
  	if (!test_thread_flag(TIF_SYSCALL_TRACE))
  		return scno;
  	if (!(current->ptrace & PT_PTRACED))
  		return scno;
  
  	/*
  	 * Save IP.  IP is used to denote syscall entry/exit:
  	 *  IP = 0 -> entry, = 1 -> exit
  	 */
  	ip = regs->UCreg_ip;
  	regs->UCreg_ip = why;
  
  	current_thread_info()->syscall = scno;
  
  	/* the 0x80 provides a way for the tracing parent to distinguish
  	   between a syscall stop and SIGTRAP delivery */
  	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
  				 ? 0x80 : 0));
  	/*
  	 * this isn't the same as continuing with a signal, but it will do
  	 * for normal use.  strace only continues with a signal if the
  	 * stopping signal is not SIGTRAP.  -brl
  	 */
  	if (current->exit_code) {
  		send_sig(current->exit_code, current, 1);
  		current->exit_code = 0;
  	}
  	regs->UCreg_ip = ip;
  
  	return current_thread_info()->syscall;
  }
  