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