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