aboutsummaryrefslogtreecommitdiffstats
path: root/arch/xtensa/kernel/ptrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/xtensa/kernel/ptrace.c')
-rw-r--r--arch/xtensa/kernel/ptrace.c347
1 files changed, 184 insertions, 163 deletions
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
index 5533c7850d53..f6669d605125 100644
--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -4,7 +4,7 @@
4 * License. See the file "COPYING" in the main directory of this archive 4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details. 5 * for more details.
6 * 6 *
7 * Copyright (C) 2001 - 2005 Tensilica Inc. 7 * Copyright (C) 2001 - 2007 Tensilica Inc.
8 * 8 *
9 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> 9 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
10 * Chris Zankel <chris@zankel.net> 10 * Chris Zankel <chris@zankel.net>
@@ -28,14 +28,10 @@
28#include <asm/uaccess.h> 28#include <asm/uaccess.h>
29#include <asm/ptrace.h> 29#include <asm/ptrace.h>
30#include <asm/elf.h> 30#include <asm/elf.h>
31 31#include <asm/coprocessor.h>
32#define TEST_KERNEL // verify kernel operations FIXME: remove
33
34 32
35/* 33/*
36 * Called by kernel/ptrace.c when detaching.. 34 * Called by kernel/ptrace.c when detaching to disable single stepping.
37 *
38 * Make sure single step bits etc are not set.
39 */ 35 */
40 36
41void ptrace_disable(struct task_struct *child) 37void ptrace_disable(struct task_struct *child)
@@ -43,136 +39,233 @@ void ptrace_disable(struct task_struct *child)
43 /* Nothing to do.. */ 39 /* Nothing to do.. */
44} 40}
45 41
46long arch_ptrace(struct task_struct *child, long request, long addr, long data) 42int ptrace_getregs(struct task_struct *child, void __user *uregs)
47{ 43{
48 int ret = -EPERM; 44 struct pt_regs *regs = task_pt_regs(child);
45 xtensa_gregset_t __user *gregset = uregs;
46 unsigned long wb = regs->windowbase;
47 unsigned long ws = regs->windowstart;
48 unsigned long wm = regs->wmask;
49 int ret = 0;
50 int live, last;
51
52 if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
53 return -EIO;
54
55 /* Norm windowstart to a windowbase of 0. */
56
57 ws = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1);
58
59 ret |= __put_user(regs->pc, &gregset->pc);
60 ret |= __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps);
61 ret |= __put_user(regs->lbeg, &gregset->lbeg);
62 ret |= __put_user(regs->lend, &gregset->lend);
63 ret |= __put_user(regs->lcount, &gregset->lcount);
64 ret |= __put_user(ws, &gregset->windowstart);
65
66 live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
67 last = XCHAL_NUM_AREGS - (wm >> 4) * 4;
68 ret |= __copy_to_user(gregset->a, regs->areg, live * 4);
69 ret |= __copy_to_user(gregset->a + last, regs->areg + last, (wm>>4)*16);
70
71 return ret ? -EFAULT : 0;
72}
49 73
50 switch (request) { 74int ptrace_setregs(struct task_struct *child, void __user *uregs)
51 case PTRACE_PEEKTEXT: /* read word at location addr. */ 75{
52 case PTRACE_PEEKDATA: 76 struct pt_regs *regs = task_pt_regs(child);
53 ret = generic_ptrace_peekdata(child, addr, data); 77 xtensa_gregset_t *gregset = uregs;
54 goto out; 78 const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
79 unsigned long wm = regs->wmask;
80 unsigned long ps;
81 int ret = 0;
82 int live, last;
83
84 if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
85 return -EIO;
86
87 ret |= __get_user(regs->pc, &gregset->pc);
88 ret |= __get_user(ps, &gregset->ps);
89 ret |= __get_user(regs->lbeg, &gregset->lbeg);
90 ret |= __get_user(regs->lend, &gregset->lend);
91 ret |= __get_user(regs->lcount, &gregset->lcount);
92
93 regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
94
95 live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
96 last = XCHAL_NUM_AREGS - (wm >> 4) * 4;
97 ret |= __copy_from_user(regs->areg, gregset->a, live * 4);
98 ret |= __copy_from_user(regs->areg+last, gregset->a+last, (wm>>4)*16);
99
100 return ret ? -EFAULT : 0;
101}
55 102
56 /* Read the word at location addr in the USER area. */
57 103
58 case PTRACE_PEEKUSR: 104int ptrace_getxregs(struct task_struct *child, void __user *uregs)
59 { 105{
60 struct pt_regs *regs; 106 struct pt_regs *regs = task_pt_regs(child);
61 unsigned long tmp; 107 struct thread_info *ti = task_thread_info(child);
108 elf_xtregs_t __user *xtregs = uregs;
109 int ret = 0;
110
111 if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
112 return -EIO;
113
114#if XTENSA_HAVE_COPROCESSORS
115 /* Flush all coprocessor registers to memory. */
116 coprocessor_flush_all(ti);
117 ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
118 sizeof(xtregs_coprocessor_t));
119#endif
120 ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
121 sizeof(xtregs->opt));
122 ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user,
123 sizeof(xtregs->user));
62 124
63 regs = task_pt_regs(child); 125 return ret ? -EFAULT : 0;
64 tmp = 0; /* Default return value. */ 126}
127
128int ptrace_setxregs(struct task_struct *child, void __user *uregs)
129{
130 struct thread_info *ti = task_thread_info(child);
131 struct pt_regs *regs = task_pt_regs(child);
132 elf_xtregs_t *xtregs = uregs;
133 int ret = 0;
134
135#if XTENSA_HAVE_COPROCESSORS
136 /* Flush all coprocessors before we overwrite them. */
137 coprocessor_flush_all(ti);
138 coprocessor_release_all(ti);
139
140 ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
141 sizeof(xtregs_coprocessor_t));
142#endif
143 ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
144 sizeof(xtregs->opt));
145 ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user,
146 sizeof(xtregs->user));
147
148 return ret ? -EFAULT : 0;
149}
150
151int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret)
152{
153 struct pt_regs *regs;
154 unsigned long tmp;
155
156 regs = task_pt_regs(child);
157 tmp = 0; /* Default return value. */
65 158
66 switch(addr) { 159 switch(regno) {
67 160
68 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: 161 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
69 { 162 tmp = regs->areg[regno - REG_AR_BASE];
70 int ar = addr - REG_AR_BASE - regs->windowbase * 4;
71 ar &= (XCHAL_NUM_AREGS - 1);
72 if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
73 tmp = regs->areg[ar];
74 else
75 ret = -EIO;
76 break; 163 break;
77 } 164
78 case REG_A_BASE ... REG_A_BASE + 15: 165 case REG_A_BASE ... REG_A_BASE + 15:
79 tmp = regs->areg[addr - REG_A_BASE]; 166 tmp = regs->areg[regno - REG_A_BASE];
80 break; 167 break;
168
81 case REG_PC: 169 case REG_PC:
82 tmp = regs->pc; 170 tmp = regs->pc;
83 break; 171 break;
172
84 case REG_PS: 173 case REG_PS:
85 /* Note: PS.EXCM is not set while user task is running; 174 /* Note: PS.EXCM is not set while user task is running;
86 * its being set in regs is for exception handling 175 * its being set in regs is for exception handling
87 * convenience. */ 176 * convenience. */
88 tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); 177 tmp = (regs->ps & ~(1 << PS_EXCM_BIT));
89 break; 178 break;
179
90 case REG_WB: 180 case REG_WB:
91 tmp = regs->windowbase; 181 break; /* tmp = 0 */
92 break; 182
93 case REG_WS: 183 case REG_WS:
94 tmp = regs->windowstart; 184 {
185 unsigned long wb = regs->windowbase;
186 unsigned long ws = regs->windowstart;
187 tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1);
95 break; 188 break;
189 }
96 case REG_LBEG: 190 case REG_LBEG:
97 tmp = regs->lbeg; 191 tmp = regs->lbeg;
98 break; 192 break;
193
99 case REG_LEND: 194 case REG_LEND:
100 tmp = regs->lend; 195 tmp = regs->lend;
101 break; 196 break;
197
102 case REG_LCOUNT: 198 case REG_LCOUNT:
103 tmp = regs->lcount; 199 tmp = regs->lcount;
104 break; 200 break;
201
105 case REG_SAR: 202 case REG_SAR:
106 tmp = regs->sar; 203 tmp = regs->sar;
107 break; 204 break;
108 case REG_DEPC: 205
109 tmp = regs->depc;
110 break;
111 case REG_EXCCAUSE:
112 tmp = regs->exccause;
113 break;
114 case REG_EXCVADDR:
115 tmp = regs->excvaddr;
116 break;
117 case SYSCALL_NR: 206 case SYSCALL_NR:
118 tmp = regs->syscall; 207 tmp = regs->syscall;
119 break; 208 break;
120 default:
121 tmp = 0;
122 ret = -EIO;
123 goto out;
124 }
125 ret = put_user(tmp, (unsigned long *) data);
126 goto out;
127 }
128 209
129 case PTRACE_POKETEXT: /* write the word at location addr. */ 210 default:
130 case PTRACE_POKEDATA: 211 return -EIO;
131 ret = generic_ptrace_pokedata(child, addr, data); 212 }
132 goto out; 213 return put_user(tmp, ret);
214}
133 215
134 case PTRACE_POKEUSR: 216int ptrace_pokeusr(struct task_struct *child, long regno, long val)
135 { 217{
136 struct pt_regs *regs; 218 struct pt_regs *regs;
137 regs = task_pt_regs(child); 219 regs = task_pt_regs(child);
138 220
139 switch (addr) { 221 switch (regno) {
140 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: 222 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
141 { 223 regs->areg[regno - REG_AR_BASE] = val;
142 int ar = addr - REG_AR_BASE - regs->windowbase * 4;
143 if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
144 regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data;
145 else
146 ret = -EIO;
147 break; 224 break;
148 } 225
149 case REG_A_BASE ... REG_A_BASE + 15: 226 case REG_A_BASE ... REG_A_BASE + 15:
150 regs->areg[addr - REG_A_BASE] = data; 227 regs->areg[regno - REG_A_BASE] = val;
151 break; 228 break;
229
152 case REG_PC: 230 case REG_PC:
153 regs->pc = data; 231 regs->pc = val;
154 break; 232 break;
233
155 case SYSCALL_NR: 234 case SYSCALL_NR:
156 regs->syscall = data; 235 regs->syscall = val;
157 break;
158#ifdef TEST_KERNEL
159 case REG_WB:
160 regs->windowbase = data;
161 break; 236 break;
162 case REG_WS:
163 regs->windowstart = data;
164 break;
165#endif
166 237
167 default: 238 default:
168 /* The rest are not allowed. */ 239 return -EIO;
169 ret = -EIO; 240 }
170 break; 241 return 0;
171 } 242}
243
244long arch_ptrace(struct task_struct *child, long request, long addr, long data)
245{
246 int ret = -EPERM;
247
248 switch (request) {
249 case PTRACE_PEEKTEXT: /* read word at location addr. */
250 case PTRACE_PEEKDATA:
251 ret = generic_ptrace_peekdata(child, addr, data);
252 break;
253
254 case PTRACE_PEEKUSR: /* read register specified by addr. */
255 ret = ptrace_peekusr(child, addr, (void __user *) data);
256 break;
257
258 case PTRACE_POKETEXT: /* write the word at location addr. */
259 case PTRACE_POKEDATA:
260 ret = generic_ptrace_pokedata(child, addr, data);
261 break;
262
263 case PTRACE_POKEUSR: /* write register specified by addr. */
264 ret = ptrace_pokeusr(child, addr, data);
172 break; 265 break;
173 }
174 266
175 /* continue and stop at next (return from) syscall */ 267 /* continue and stop at next (return from) syscall */
268
176 case PTRACE_SYSCALL: 269 case PTRACE_SYSCALL:
177 case PTRACE_CONT: /* restart after signal. */ 270 case PTRACE_CONT: /* restart after signal. */
178 { 271 {
@@ -217,98 +310,26 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
217 break; 310 break;
218 311
219 case PTRACE_GETREGS: 312 case PTRACE_GETREGS:
220 { 313 ret = ptrace_getregs(child, (void __user *) data);
221 /* 'data' points to user memory in which to write.
222 * Mainly due to the non-live register values, we
223 * reformat the register values into something more
224 * standard. For convenience, we use the handy
225 * elf_gregset_t format. */
226
227 xtensa_gregset_t format;
228 struct pt_regs *regs = task_pt_regs(child);
229
230 do_copy_regs (&format, regs, child);
231
232 /* Now, copy to user space nice and easy... */
233 ret = 0;
234 if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t)))
235 ret = -EFAULT;
236 break; 314 break;
237 }
238 315
239 case PTRACE_SETREGS: 316 case PTRACE_SETREGS:
240 { 317 ret = ptrace_setregs(child, (void __user *) data);
241 /* 'data' points to user memory that contains the new
242 * values in the elf_gregset_t format. */
243
244 xtensa_gregset_t format;
245 struct pt_regs *regs = task_pt_regs(child);
246
247 if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){
248 ret = -EFAULT;
249 break;
250 }
251
252 /* FIXME: Perhaps we want some sanity checks on
253 * these user-space values? See ARM version. Are
254 * debuggers a security concern? */
255
256 do_restore_regs (&format, regs, child);
257
258 ret = 0;
259 break;
260 }
261
262 case PTRACE_GETFPREGS:
263 {
264 /* 'data' points to user memory in which to write.
265 * For convenience, we use the handy
266 * elf_fpregset_t format. */
267
268 elf_fpregset_t fpregs;
269 struct pt_regs *regs = task_pt_regs(child);
270
271 do_save_fpregs (&fpregs, regs, child);
272
273 /* Now, copy to user space nice and easy... */
274 ret = 0;
275 if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t)))
276 ret = -EFAULT;
277
278 break; 318 break;
279 }
280
281 case PTRACE_SETFPREGS:
282 {
283 /* 'data' points to user memory that contains the new
284 * values in the elf_fpregset_t format.
285 */
286 elf_fpregset_t fpregs;
287 struct pt_regs *regs = task_pt_regs(child);
288 319
289 ret = 0; 320 case PTRACE_GETXTREGS:
290 if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) { 321 ret = ptrace_getxregs(child, (void __user *) data);
291 ret = -EFAULT;
292 break;
293 }
294
295 if (do_restore_fpregs (&fpregs, regs, child))
296 ret = -EIO;
297 break; 322 break;
298 }
299 323
300 case PTRACE_GETFPREGSIZE: 324 case PTRACE_SETXTREGS:
301 /* 'data' points to 'unsigned long' set to the size 325 ret = ptrace_setxregs(child, (void __user *) data);
302 * of elf_fpregset_t
303 */
304 ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data);
305 break; 326 break;
306 327
307 default: 328 default:
308 ret = ptrace_request(child, request, addr, data); 329 ret = ptrace_request(child, request, addr, data);
309 goto out; 330 break;
310 } 331 }
311 out: 332
312 return ret; 333 return ret;
313} 334}
314 335