]>
Commit | Line | Data |
---|---|---|
867e359b CM |
1 | /* |
2 | * Copyright 2010 Tilera Corporation. All Rights Reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation, version 2. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
11 | * NON INFRINGEMENT. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * Copied from i386: Ross Biro 1/23/92 | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/ptrace.h> | |
19 | #include <linux/kprobes.h> | |
20 | #include <linux/compat.h> | |
21 | #include <linux/uaccess.h> | |
7be68284 SM |
22 | #include <linux/regset.h> |
23 | #include <linux/elf.h> | |
ef182724 | 24 | #include <linux/tracehook.h> |
49e4e156 | 25 | #include <linux/context_tracking.h> |
68db0cf1 IM |
26 | #include <linux/sched/task_stack.h> |
27 | ||
0707ad30 | 28 | #include <asm/traps.h> |
7be68284 | 29 | #include <arch/chip.h> |
867e359b | 30 | |
ef567f25 SM |
31 | #define CREATE_TRACE_POINTS |
32 | #include <trace/events/syscalls.h> | |
33 | ||
867e359b CM |
34 | void user_enable_single_step(struct task_struct *child) |
35 | { | |
36 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | |
37 | } | |
38 | ||
39 | void user_disable_single_step(struct task_struct *child) | |
40 | { | |
41 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | |
42 | } | |
43 | ||
867e359b CM |
44 | /* |
45 | * Called by kernel/ptrace.c when detaching.. | |
46 | */ | |
47 | void ptrace_disable(struct task_struct *child) | |
48 | { | |
49 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | |
50 | ||
51 | /* | |
52 | * These two are currently unused, but will be set by arch_ptrace() | |
53 | * and used in the syscall assembly when we do support them. | |
54 | */ | |
55 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | |
56 | } | |
57 | ||
cb67e161 CM |
58 | /* |
59 | * Get registers from task and ready the result for userspace. | |
60 | * Note that we localize the API issues to getregs() and putregs() at | |
61 | * some cost in performance, e.g. we need a full pt_regs copy for | |
62 | * PEEKUSR, and two copies for POKEUSR. But in general we expect | |
63 | * GETREGS/PUTREGS to be the API of choice anyway. | |
64 | */ | |
65 | static char *getregs(struct task_struct *child, struct pt_regs *uregs) | |
66 | { | |
67 | *uregs = *task_pt_regs(child); | |
68 | ||
69 | /* Set up flags ABI bits. */ | |
70 | uregs->flags = 0; | |
71 | #ifdef CONFIG_COMPAT | |
72 | if (task_thread_info(child)->status & TS_COMPAT) | |
73 | uregs->flags |= PT_FLAGS_COMPAT; | |
74 | #endif | |
75 | ||
76 | return (char *)uregs; | |
77 | } | |
78 | ||
79 | /* Put registers back to task. */ | |
80 | static void putregs(struct task_struct *child, struct pt_regs *uregs) | |
81 | { | |
82 | struct pt_regs *regs = task_pt_regs(child); | |
83 | ||
84 | /* Don't allow overwriting the kernel-internal flags word. */ | |
85 | uregs->flags = regs->flags; | |
86 | ||
87 | /* Only allow setting the ICS bit in the ex1 word. */ | |
88 | uregs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(uregs->ex1)); | |
89 | ||
90 | *regs = *uregs; | |
91 | } | |
92 | ||
7be68284 SM |
93 | enum tile_regset { |
94 | REGSET_GPR, | |
95 | }; | |
96 | ||
97 | static int tile_gpr_get(struct task_struct *target, | |
98 | const struct user_regset *regset, | |
99 | unsigned int pos, unsigned int count, | |
100 | void *kbuf, void __user *ubuf) | |
101 | { | |
102 | struct pt_regs regs; | |
103 | ||
104 | getregs(target, ®s); | |
105 | ||
106 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s, 0, | |
107 | sizeof(regs)); | |
108 | } | |
109 | ||
110 | static int tile_gpr_set(struct task_struct *target, | |
111 | const struct user_regset *regset, | |
112 | unsigned int pos, unsigned int count, | |
113 | const void *kbuf, const void __user *ubuf) | |
114 | { | |
115 | int ret; | |
fd7c9914 | 116 | struct pt_regs regs = *task_pt_regs(target); |
7be68284 SM |
117 | |
118 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, 0, | |
119 | sizeof(regs)); | |
120 | if (ret) | |
121 | return ret; | |
122 | ||
123 | putregs(target, ®s); | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static const struct user_regset tile_user_regset[] = { | |
129 | [REGSET_GPR] = { | |
130 | .core_note_type = NT_PRSTATUS, | |
131 | .n = ELF_NGREG, | |
132 | .size = sizeof(elf_greg_t), | |
133 | .align = sizeof(elf_greg_t), | |
134 | .get = tile_gpr_get, | |
135 | .set = tile_gpr_set, | |
136 | }, | |
137 | }; | |
138 | ||
139 | static const struct user_regset_view tile_user_regset_view = { | |
140 | .name = CHIP_ARCH_NAME, | |
141 | .e_machine = ELF_ARCH, | |
142 | .ei_osabi = ELF_OSABI, | |
143 | .regsets = tile_user_regset, | |
144 | .n = ARRAY_SIZE(tile_user_regset), | |
145 | }; | |
146 | ||
147 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
148 | { | |
149 | return &tile_user_regset_view; | |
150 | } | |
151 | ||
9b05a69e NK |
152 | long arch_ptrace(struct task_struct *child, long request, |
153 | unsigned long addr, unsigned long data) | |
867e359b | 154 | { |
ce7f2a39 | 155 | unsigned long __user *datap = (long __user __force *)data; |
867e359b | 156 | unsigned long tmp; |
867e359b | 157 | long ret = -EIO; |
ce7f2a39 | 158 | char *childreg; |
1deb9c5d | 159 | struct pt_regs copyregs; |
867e359b CM |
160 | |
161 | switch (request) { | |
162 | ||
163 | case PTRACE_PEEKUSR: /* Read register from pt_regs. */ | |
8c0acac3 | 164 | if (addr >= PTREGS_SIZE) |
867e359b | 165 | break; |
cb67e161 | 166 | childreg = getregs(child, ©regs) + addr; |
ce7f2a39 CM |
167 | #ifdef CONFIG_COMPAT |
168 | if (is_compat_task()) { | |
169 | if (addr & (sizeof(compat_long_t)-1)) | |
170 | break; | |
171 | ret = put_user(*(compat_long_t *)childreg, | |
172 | (compat_long_t __user *)datap); | |
173 | } else | |
174 | #endif | |
175 | { | |
176 | if (addr & (sizeof(long)-1)) | |
177 | break; | |
178 | ret = put_user(*(long *)childreg, datap); | |
179 | } | |
867e359b CM |
180 | break; |
181 | ||
182 | case PTRACE_POKEUSR: /* Write register in pt_regs. */ | |
8c0acac3 | 183 | if (addr >= PTREGS_SIZE) |
867e359b | 184 | break; |
cb67e161 | 185 | childreg = getregs(child, ©regs) + addr; |
ce7f2a39 CM |
186 | #ifdef CONFIG_COMPAT |
187 | if (is_compat_task()) { | |
188 | if (addr & (sizeof(compat_long_t)-1)) | |
189 | break; | |
190 | *(compat_long_t *)childreg = data; | |
191 | } else | |
192 | #endif | |
193 | { | |
194 | if (addr & (sizeof(long)-1)) | |
195 | break; | |
196 | *(long *)childreg = data; | |
197 | } | |
cb67e161 | 198 | putregs(child, ©regs); |
bcd97c3f | 199 | ret = 0; |
867e359b CM |
200 | break; |
201 | ||
202 | case PTRACE_GETREGS: /* Get all registers from the child. */ | |
9af62547 SM |
203 | ret = copy_regset_to_user(child, &tile_user_regset_view, |
204 | REGSET_GPR, 0, | |
205 | sizeof(struct pt_regs), datap); | |
867e359b CM |
206 | break; |
207 | ||
208 | case PTRACE_SETREGS: /* Set all registers in the child. */ | |
9af62547 SM |
209 | ret = copy_regset_from_user(child, &tile_user_regset_view, |
210 | REGSET_GPR, 0, | |
211 | sizeof(struct pt_regs), datap); | |
867e359b CM |
212 | break; |
213 | ||
214 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | |
215 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ | |
216 | break; | |
217 | ||
218 | case PTRACE_SETOPTIONS: | |
219 | /* Support TILE-specific ptrace options. */ | |
395e095e | 220 | BUILD_BUG_ON(PTRACE_O_MASK_TILE & PTRACE_O_MASK); |
867e359b CM |
221 | tmp = data & PTRACE_O_MASK_TILE; |
222 | data &= ~PTRACE_O_MASK_TILE; | |
223 | ret = ptrace_request(child, request, addr, data); | |
395e095e CM |
224 | if (ret == 0) { |
225 | unsigned int flags = child->ptrace; | |
226 | flags &= ~(PTRACE_O_MASK_TILE << PT_OPT_FLAG_SHIFT); | |
227 | flags |= (tmp << PT_OPT_FLAG_SHIFT); | |
228 | child->ptrace = flags; | |
229 | } | |
867e359b CM |
230 | break; |
231 | ||
232 | default: | |
233 | #ifdef CONFIG_COMPAT | |
234 | if (task_thread_info(current)->status & TS_COMPAT) { | |
235 | ret = compat_ptrace_request(child, request, | |
236 | addr, data); | |
237 | break; | |
238 | } | |
239 | #endif | |
240 | ret = ptrace_request(child, request, addr, data); | |
241 | break; | |
242 | } | |
243 | ||
244 | return ret; | |
245 | } | |
246 | ||
247 | #ifdef CONFIG_COMPAT | |
248 | /* Not used; we handle compat issues in arch_ptrace() directly. */ | |
249 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |
250 | compat_ulong_t addr, compat_ulong_t data) | |
251 | { | |
252 | BUG(); | |
253 | } | |
254 | #endif | |
255 | ||
ef182724 | 256 | int do_syscall_trace_enter(struct pt_regs *regs) |
867e359b | 257 | { |
49e4e156 CM |
258 | u32 work = ACCESS_ONCE(current_thread_info()->flags); |
259 | ||
635efc70 KC |
260 | if ((work & _TIF_SYSCALL_TRACE) && |
261 | tracehook_report_syscall_entry(regs)) { | |
262 | regs->regs[TREG_SYSCALL_NR] = -1; | |
a0ddef81 | 263 | return -1; |
ef182724 | 264 | } |
867e359b | 265 | |
635efc70 KC |
266 | if (secure_computing(NULL) == -1) |
267 | return -1; | |
268 | ||
49e4e156 | 269 | if (work & _TIF_SYSCALL_TRACEPOINT) |
ef567f25 SM |
270 | trace_sys_enter(regs, regs->regs[TREG_SYSCALL_NR]); |
271 | ||
ef182724 SM |
272 | return regs->regs[TREG_SYSCALL_NR]; |
273 | } | |
867e359b | 274 | |
ef182724 SM |
275 | void do_syscall_trace_exit(struct pt_regs *regs) |
276 | { | |
9b5bbf72 CM |
277 | long errno; |
278 | ||
279 | /* | |
280 | * The standard tile calling convention returns the value (or negative | |
281 | * errno) in r0, and zero (or positive errno) in r1. | |
282 | * It saves a couple of cycles on the hot path to do this work in | |
283 | * registers only as we return, rather than updating the in-memory | |
284 | * struct ptregs. | |
285 | */ | |
286 | errno = (long) regs->regs[0]; | |
287 | if (errno < 0 && errno > -4096) | |
288 | regs->regs[1] = -errno; | |
289 | else | |
290 | regs->regs[1] = 0; | |
291 | ||
ef567f25 SM |
292 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
293 | tracehook_report_syscall_exit(regs, 0); | |
294 | ||
295 | if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) | |
9fc1894c | 296 | trace_sys_exit(regs, regs->regs[0]); |
867e359b CM |
297 | } |
298 | ||
2f9ac29e | 299 | void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs) |
867e359b CM |
300 | { |
301 | struct siginfo info; | |
302 | ||
303 | memset(&info, 0, sizeof(info)); | |
304 | info.si_signo = SIGTRAP; | |
305 | info.si_code = TRAP_BRKPT; | |
306 | info.si_addr = (void __user *) regs->pc; | |
307 | ||
308 | /* Send us the fakey SIGTRAP */ | |
309 | force_sig_info(SIGTRAP, &info, tsk); | |
310 | } | |
311 | ||
312 | /* Handle synthetic interrupt delivered only by the simulator. */ | |
313 | void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num) | |
314 | { | |
2f9ac29e | 315 | send_sigtrap(current, regs); |
867e359b | 316 | } |