Linux-libre 5.7.3-gnu
[librecmc/linux-libre.git] / arch / unicore32 / kernel / ptrace.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * linux/arch/unicore32/kernel/ptrace.c
4  *
5  * Code specific to PKUnity SoC and UniCore ISA
6  *
7  * Copyright (C) 2001-2010 GUAN Xue-tao
8  *
9  * By Ross Biro 1/23/92
10  */
11 #include <linux/kernel.h>
12 #include <linux/ptrace.h>
13 #include <linux/signal.h>
14 #include <linux/uaccess.h>
15 #include <linux/sched/task_stack.h>
16
17 /*
18  * this routine will get a word off of the processes privileged stack.
19  * the offset is how far from the base addr as stored in the THREAD.
20  * this routine assumes that all the privileged stacks are in our
21  * data space.
22  */
23 static inline long get_user_reg(struct task_struct *task, int offset)
24 {
25         return task_pt_regs(task)->uregs[offset];
26 }
27
28 /*
29  * this routine will put a word on the processes privileged stack.
30  * the offset is how far from the base addr as stored in the THREAD.
31  * this routine assumes that all the privileged stacks are in our
32  * data space.
33  */
34 static inline int
35 put_user_reg(struct task_struct *task, int offset, long data)
36 {
37         struct pt_regs newregs, *regs = task_pt_regs(task);
38         int ret = -EINVAL;
39
40         newregs = *regs;
41         newregs.uregs[offset] = data;
42
43         if (valid_user_regs(&newregs)) {
44                 regs->uregs[offset] = data;
45                 ret = 0;
46         }
47
48         return ret;
49 }
50
51 /*
52  * Called by kernel/ptrace.c when detaching..
53  */
54 void ptrace_disable(struct task_struct *child)
55 {
56 }
57
58 /*
59  * We actually access the pt_regs stored on the kernel stack.
60  */
61 static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
62                             unsigned long __user *ret)
63 {
64         unsigned long tmp;
65
66         tmp = 0;
67         if (off < sizeof(struct pt_regs))
68                 tmp = get_user_reg(tsk, off >> 2);
69
70         return put_user(tmp, ret);
71 }
72
73 /*
74  * We actually access the pt_regs stored on the kernel stack.
75  */
76 static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
77                              unsigned long val)
78 {
79         if (off >= sizeof(struct pt_regs))
80                 return 0;
81
82         return put_user_reg(tsk, off >> 2, val);
83 }
84
85 long arch_ptrace(struct task_struct *child, long request,
86                  unsigned long addr, unsigned long data)
87 {
88         int ret;
89         unsigned long __user *datap = (unsigned long __user *) data;
90
91         switch (request) {
92         case PTRACE_PEEKUSR:
93                 ret = ptrace_read_user(child, addr, datap);
94                 break;
95
96         case PTRACE_POKEUSR:
97                 ret = ptrace_write_user(child, addr, data);
98                 break;
99
100         case PTRACE_GET_THREAD_AREA:
101                 ret = put_user(task_pt_regs(child)->UCreg_16,
102                                datap);
103                 break;
104
105         default:
106                 ret = ptrace_request(child, request, addr, data);
107                 break;
108         }
109
110         return ret;
111 }
112
113 asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
114 {
115         unsigned long ip;
116
117         if (!test_thread_flag(TIF_SYSCALL_TRACE))
118                 return scno;
119         if (!(current->ptrace & PT_PTRACED))
120                 return scno;
121
122         /*
123          * Save IP.  IP is used to denote syscall entry/exit:
124          *  IP = 0 -> entry, = 1 -> exit
125          */
126         ip = regs->UCreg_ip;
127         regs->UCreg_ip = why;
128
129         current_thread_info()->syscall = scno;
130
131         /* the 0x80 provides a way for the tracing parent to distinguish
132            between a syscall stop and SIGTRAP delivery */
133         ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
134                                  ? 0x80 : 0));
135         /*
136          * this isn't the same as continuing with a signal, but it will do
137          * for normal use.  strace only continues with a signal if the
138          * stopping signal is not SIGTRAP.  -brl
139          */
140         if (current->exit_code) {
141                 send_sig(current->exit_code, current, 1);
142                 current->exit_code = 0;
143         }
144         regs->UCreg_ip = ip;
145
146         return current_thread_info()->syscall;
147 }