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