]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c08098f2 VG |
2 | /* |
3 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | |
c08098f2 VG |
4 | */ |
5 | ||
6 | #include <linux/ptrace.h> | |
547f1125 | 7 | #include <linux/tracehook.h> |
68db0cf1 | 8 | #include <linux/sched/task_stack.h> |
547f1125 VG |
9 | #include <linux/regset.h> |
10 | #include <linux/unistd.h> | |
11 | #include <linux/elf.h> | |
12 | ||
13 | static struct callee_regs *task_callee_regs(struct task_struct *tsk) | |
14 | { | |
15 | struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg; | |
16 | return tmp; | |
17 | } | |
18 | ||
19 | static int genregs_get(struct task_struct *target, | |
20 | const struct user_regset *regset, | |
21 | unsigned int pos, unsigned int count, | |
22 | void *kbuf, void __user *ubuf) | |
23 | { | |
24 | const struct pt_regs *ptregs = task_pt_regs(target); | |
25 | const struct callee_regs *cregs = task_callee_regs(target); | |
26 | int ret = 0; | |
27 | unsigned int stop_pc_val; | |
28 | ||
29 | #define REG_O_CHUNK(START, END, PTR) \ | |
30 | if (!ret) \ | |
31 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ | |
32 | offsetof(struct user_regs_struct, START), \ | |
33 | offsetof(struct user_regs_struct, END)); | |
34 | ||
35 | #define REG_O_ONE(LOC, PTR) \ | |
36 | if (!ret) \ | |
37 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ | |
38 | offsetof(struct user_regs_struct, LOC), \ | |
39 | offsetof(struct user_regs_struct, LOC) + 4); | |
40 | ||
2fa91904 VG |
41 | #define REG_O_ZERO(LOC) \ |
42 | if (!ret) \ | |
43 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \ | |
44 | offsetof(struct user_regs_struct, LOC), \ | |
45 | offsetof(struct user_regs_struct, LOC) + 4); | |
46 | ||
47 | REG_O_ZERO(pad); | |
6ffb9c8c VG |
48 | REG_O_ONE(scratch.bta, &ptregs->bta); |
49 | REG_O_ONE(scratch.lp_start, &ptregs->lp_start); | |
50 | REG_O_ONE(scratch.lp_end, &ptregs->lp_end); | |
51 | REG_O_ONE(scratch.lp_count, &ptregs->lp_count); | |
52 | REG_O_ONE(scratch.status32, &ptregs->status32); | |
53 | REG_O_ONE(scratch.ret, &ptregs->ret); | |
54 | REG_O_ONE(scratch.blink, &ptregs->blink); | |
55 | REG_O_ONE(scratch.fp, &ptregs->fp); | |
56 | REG_O_ONE(scratch.gp, &ptregs->r26); | |
57 | REG_O_ONE(scratch.r12, &ptregs->r12); | |
58 | REG_O_ONE(scratch.r11, &ptregs->r11); | |
59 | REG_O_ONE(scratch.r10, &ptregs->r10); | |
60 | REG_O_ONE(scratch.r9, &ptregs->r9); | |
61 | REG_O_ONE(scratch.r8, &ptregs->r8); | |
62 | REG_O_ONE(scratch.r7, &ptregs->r7); | |
63 | REG_O_ONE(scratch.r6, &ptregs->r6); | |
64 | REG_O_ONE(scratch.r5, &ptregs->r5); | |
65 | REG_O_ONE(scratch.r4, &ptregs->r4); | |
66 | REG_O_ONE(scratch.r3, &ptregs->r3); | |
67 | REG_O_ONE(scratch.r2, &ptregs->r2); | |
68 | REG_O_ONE(scratch.r1, &ptregs->r1); | |
69 | REG_O_ONE(scratch.r0, &ptregs->r0); | |
70 | REG_O_ONE(scratch.sp, &ptregs->sp); | |
71 | ||
16f9afe6 | 72 | REG_O_ZERO(pad2); |
6ffb9c8c VG |
73 | |
74 | REG_O_ONE(callee.r25, &cregs->r25); | |
75 | REG_O_ONE(callee.r24, &cregs->r24); | |
76 | REG_O_ONE(callee.r23, &cregs->r23); | |
77 | REG_O_ONE(callee.r22, &cregs->r22); | |
78 | REG_O_ONE(callee.r21, &cregs->r21); | |
79 | REG_O_ONE(callee.r20, &cregs->r20); | |
80 | REG_O_ONE(callee.r19, &cregs->r19); | |
81 | REG_O_ONE(callee.r18, &cregs->r18); | |
82 | REG_O_ONE(callee.r17, &cregs->r17); | |
83 | REG_O_ONE(callee.r16, &cregs->r16); | |
84 | REG_O_ONE(callee.r15, &cregs->r15); | |
85 | REG_O_ONE(callee.r14, &cregs->r14); | |
86 | REG_O_ONE(callee.r13, &cregs->r13); | |
87 | ||
88 | REG_O_ONE(efa, &target->thread.fault_address); | |
547f1125 VG |
89 | |
90 | if (!ret) { | |
91 | if (in_brkpt_trap(ptregs)) { | |
92 | stop_pc_val = target->thread.fault_address; | |
93 | pr_debug("\t\tstop_pc (brk-pt)\n"); | |
94 | } else { | |
95 | stop_pc_val = ptregs->ret; | |
96 | pr_debug("\t\tstop_pc (others)\n"); | |
97 | } | |
98 | ||
99 | REG_O_ONE(stop_pc, &stop_pc_val); | |
100 | } | |
101 | ||
102 | return ret; | |
103 | } | |
104 | ||
105 | static int genregs_set(struct task_struct *target, | |
106 | const struct user_regset *regset, | |
107 | unsigned int pos, unsigned int count, | |
108 | const void *kbuf, const void __user *ubuf) | |
109 | { | |
110 | const struct pt_regs *ptregs = task_pt_regs(target); | |
111 | const struct callee_regs *cregs = task_callee_regs(target); | |
112 | int ret = 0; | |
113 | ||
114 | #define REG_IN_CHUNK(FIRST, NEXT, PTR) \ | |
115 | if (!ret) \ | |
116 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ | |
117 | (void *)(PTR), \ | |
118 | offsetof(struct user_regs_struct, FIRST), \ | |
119 | offsetof(struct user_regs_struct, NEXT)); | |
120 | ||
121 | #define REG_IN_ONE(LOC, PTR) \ | |
122 | if (!ret) \ | |
123 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ | |
124 | (void *)(PTR), \ | |
125 | offsetof(struct user_regs_struct, LOC), \ | |
126 | offsetof(struct user_regs_struct, LOC) + 4); | |
127 | ||
128 | #define REG_IGNORE_ONE(LOC) \ | |
129 | if (!ret) \ | |
130 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ | |
131 | offsetof(struct user_regs_struct, LOC), \ | |
132 | offsetof(struct user_regs_struct, LOC) + 4); | |
133 | ||
2fa91904 | 134 | REG_IGNORE_ONE(pad); |
6ffb9c8c VG |
135 | |
136 | REG_IN_ONE(scratch.bta, &ptregs->bta); | |
137 | REG_IN_ONE(scratch.lp_start, &ptregs->lp_start); | |
138 | REG_IN_ONE(scratch.lp_end, &ptregs->lp_end); | |
139 | REG_IN_ONE(scratch.lp_count, &ptregs->lp_count); | |
140 | ||
141 | REG_IGNORE_ONE(scratch.status32); | |
142 | ||
143 | REG_IN_ONE(scratch.ret, &ptregs->ret); | |
144 | REG_IN_ONE(scratch.blink, &ptregs->blink); | |
145 | REG_IN_ONE(scratch.fp, &ptregs->fp); | |
146 | REG_IN_ONE(scratch.gp, &ptregs->r26); | |
147 | REG_IN_ONE(scratch.r12, &ptregs->r12); | |
148 | REG_IN_ONE(scratch.r11, &ptregs->r11); | |
149 | REG_IN_ONE(scratch.r10, &ptregs->r10); | |
150 | REG_IN_ONE(scratch.r9, &ptregs->r9); | |
151 | REG_IN_ONE(scratch.r8, &ptregs->r8); | |
152 | REG_IN_ONE(scratch.r7, &ptregs->r7); | |
153 | REG_IN_ONE(scratch.r6, &ptregs->r6); | |
154 | REG_IN_ONE(scratch.r5, &ptregs->r5); | |
155 | REG_IN_ONE(scratch.r4, &ptregs->r4); | |
156 | REG_IN_ONE(scratch.r3, &ptregs->r3); | |
157 | REG_IN_ONE(scratch.r2, &ptregs->r2); | |
158 | REG_IN_ONE(scratch.r1, &ptregs->r1); | |
159 | REG_IN_ONE(scratch.r0, &ptregs->r0); | |
160 | REG_IN_ONE(scratch.sp, &ptregs->sp); | |
161 | ||
16f9afe6 | 162 | REG_IGNORE_ONE(pad2); |
6ffb9c8c VG |
163 | |
164 | REG_IN_ONE(callee.r25, &cregs->r25); | |
165 | REG_IN_ONE(callee.r24, &cregs->r24); | |
166 | REG_IN_ONE(callee.r23, &cregs->r23); | |
167 | REG_IN_ONE(callee.r22, &cregs->r22); | |
168 | REG_IN_ONE(callee.r21, &cregs->r21); | |
169 | REG_IN_ONE(callee.r20, &cregs->r20); | |
170 | REG_IN_ONE(callee.r19, &cregs->r19); | |
171 | REG_IN_ONE(callee.r18, &cregs->r18); | |
172 | REG_IN_ONE(callee.r17, &cregs->r17); | |
173 | REG_IN_ONE(callee.r16, &cregs->r16); | |
174 | REG_IN_ONE(callee.r15, &cregs->r15); | |
175 | REG_IN_ONE(callee.r14, &cregs->r14); | |
176 | REG_IN_ONE(callee.r13, &cregs->r13); | |
177 | ||
547f1125 | 178 | REG_IGNORE_ONE(efa); /* efa update invalid */ |
6ffb9c8c | 179 | REG_IGNORE_ONE(stop_pc); /* PC updated via @ret */ |
547f1125 VG |
180 | |
181 | return ret; | |
182 | } | |
183 | ||
65c02a55 VG |
184 | #ifdef CONFIG_ISA_ARCV2 |
185 | static int arcv2regs_get(struct task_struct *target, | |
186 | const struct user_regset *regset, | |
187 | unsigned int pos, unsigned int count, | |
188 | void *kbuf, void __user *ubuf) | |
189 | { | |
190 | const struct pt_regs *regs = task_pt_regs(target); | |
191 | int ret, copy_sz; | |
192 | ||
193 | if (IS_ENABLED(CONFIG_ARC_HAS_ACCL_REGS)) | |
194 | copy_sz = sizeof(struct user_regs_arcv2); | |
195 | else | |
196 | copy_sz = 4; /* r30 only */ | |
197 | ||
198 | /* | |
199 | * itemized copy not needed like above as layout of regs (r30,r58,r59) | |
200 | * is exactly same in kernel (pt_regs) and userspace (user_regs_arcv2) | |
201 | */ | |
202 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s->r30, | |
203 | 0, copy_sz); | |
204 | ||
205 | return ret; | |
206 | } | |
207 | ||
208 | static int arcv2regs_set(struct task_struct *target, | |
209 | const struct user_regset *regset, | |
210 | unsigned int pos, unsigned int count, | |
211 | const void *kbuf, const void __user *ubuf) | |
212 | { | |
213 | const struct pt_regs *regs = task_pt_regs(target); | |
214 | int ret, copy_sz; | |
215 | ||
216 | if (IS_ENABLED(CONFIG_ARC_HAS_ACCL_REGS)) | |
217 | copy_sz = sizeof(struct user_regs_arcv2); | |
218 | else | |
219 | copy_sz = 4; /* r30 only */ | |
220 | ||
221 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, (void *)®s->r30, | |
222 | 0, copy_sz); | |
223 | ||
224 | return ret; | |
225 | } | |
226 | ||
227 | #endif | |
228 | ||
547f1125 | 229 | enum arc_getset { |
65c02a55 VG |
230 | REGSET_CMN, |
231 | REGSET_ARCV2, | |
547f1125 VG |
232 | }; |
233 | ||
234 | static const struct user_regset arc_regsets[] = { | |
65c02a55 | 235 | [REGSET_CMN] = { |
547f1125 VG |
236 | .core_note_type = NT_PRSTATUS, |
237 | .n = ELF_NGREG, | |
238 | .size = sizeof(unsigned long), | |
239 | .align = sizeof(unsigned long), | |
240 | .get = genregs_get, | |
241 | .set = genregs_set, | |
65c02a55 VG |
242 | }, |
243 | #ifdef CONFIG_ISA_ARCV2 | |
244 | [REGSET_ARCV2] = { | |
245 | .core_note_type = NT_ARC_V2, | |
246 | .n = ELF_ARCV2REG, | |
247 | .size = sizeof(unsigned long), | |
248 | .align = sizeof(unsigned long), | |
249 | .get = arcv2regs_get, | |
250 | .set = arcv2regs_set, | |
251 | }, | |
252 | #endif | |
547f1125 VG |
253 | }; |
254 | ||
255 | static const struct user_regset_view user_arc_view = { | |
9eca345c | 256 | .name = "arc", |
1f6ccfff | 257 | .e_machine = EM_ARC_INUSE, |
547f1125 VG |
258 | .regsets = arc_regsets, |
259 | .n = ARRAY_SIZE(arc_regsets) | |
260 | }; | |
261 | ||
262 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
263 | { | |
264 | return &user_arc_view; | |
265 | } | |
c08098f2 VG |
266 | |
267 | void ptrace_disable(struct task_struct *child) | |
268 | { | |
269 | } | |
270 | ||
271 | long arch_ptrace(struct task_struct *child, long request, | |
272 | unsigned long addr, unsigned long data) | |
273 | { | |
274 | int ret = -EIO; | |
547f1125 VG |
275 | |
276 | pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data); | |
277 | ||
278 | switch (request) { | |
a4b6cb73 AK |
279 | case PTRACE_GET_THREAD_AREA: |
280 | ret = put_user(task_thread_info(child)->thr_ptr, | |
281 | (unsigned long __user *)data); | |
282 | break; | |
547f1125 VG |
283 | default: |
284 | ret = ptrace_request(child, request, addr, data); | |
285 | break; | |
286 | } | |
287 | ||
c08098f2 VG |
288 | return ret; |
289 | } | |
290 | ||
547f1125 VG |
291 | asmlinkage int syscall_trace_entry(struct pt_regs *regs) |
292 | { | |
293 | if (tracehook_report_syscall_entry(regs)) | |
294 | return ULONG_MAX; | |
c08098f2 | 295 | |
547f1125 VG |
296 | return regs->r8; |
297 | } | |
298 | ||
299 | asmlinkage void syscall_trace_exit(struct pt_regs *regs) | |
c08098f2 | 300 | { |
547f1125 | 301 | tracehook_report_syscall_exit(regs, 0); |
c08098f2 | 302 | } |