diff options
Diffstat (limited to 'arch/xtensa/kernel/ptrace.c')
-rw-r--r-- | arch/xtensa/kernel/ptrace.c | 351 |
1 files changed, 188 insertions, 163 deletions
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 5533c7850d53..9486882ef0af 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 | ||
41 | void ptrace_disable(struct task_struct *child) | 37 | void ptrace_disable(struct task_struct *child) |
@@ -43,136 +39,237 @@ void ptrace_disable(struct task_struct *child) | |||
43 | /* Nothing to do.. */ | 39 | /* Nothing to do.. */ |
44 | } | 40 | } |
45 | 41 | ||
46 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 42 | int 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 wm = regs->wmask; | ||
47 | unsigned long wb = regs->windowbase; | ||
48 | int live, i; | ||
49 | |||
50 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | ||
51 | return -EIO; | ||
52 | |||
53 | __put_user(regs->pc, &gregset->pc); | ||
54 | __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps); | ||
55 | __put_user(regs->lbeg, &gregset->lbeg); | ||
56 | __put_user(regs->lend, &gregset->lend); | ||
57 | __put_user(regs->lcount, &gregset->lcount); | ||
58 | __put_user(regs->windowstart, &gregset->windowstart); | ||
59 | __put_user(regs->windowbase, &gregset->windowbase); | ||
60 | |||
61 | live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16; | ||
62 | |||
63 | for (i = 0; i < live; i++) | ||
64 | __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS)); | ||
65 | for (i = XCHAL_NUM_AREGS - (wm >> 4) * 4; i < XCHAL_NUM_AREGS; i++) | ||
66 | __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS)); | ||
67 | |||
68 | return 0; | ||
69 | } | ||
49 | 70 | ||
50 | switch (request) { | 71 | int ptrace_setregs(struct task_struct *child, void __user *uregs) |
51 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 72 | { |
52 | case PTRACE_PEEKDATA: | 73 | struct pt_regs *regs = task_pt_regs(child); |
53 | ret = generic_ptrace_peekdata(child, addr, data); | 74 | xtensa_gregset_t *gregset = uregs; |
54 | goto out; | 75 | const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK; |
76 | unsigned long ps; | ||
77 | unsigned long wb; | ||
55 | 78 | ||
56 | /* Read the word at location addr in the USER area. */ | 79 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) |
80 | return -EIO; | ||
57 | 81 | ||
58 | case PTRACE_PEEKUSR: | 82 | __get_user(regs->pc, &gregset->pc); |
59 | { | 83 | __get_user(ps, &gregset->ps); |
60 | struct pt_regs *regs; | 84 | __get_user(regs->lbeg, &gregset->lbeg); |
61 | unsigned long tmp; | 85 | __get_user(regs->lend, &gregset->lend); |
86 | __get_user(regs->lcount, &gregset->lcount); | ||
87 | __get_user(regs->windowstart, &gregset->windowstart); | ||
88 | __get_user(wb, &gregset->windowbase); | ||
89 | |||
90 | regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT); | ||
91 | |||
92 | if (wb >= XCHAL_NUM_AREGS / 4) | ||
93 | return -EFAULT; | ||
94 | |||
95 | regs->windowbase = wb; | ||
96 | |||
97 | if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4, | ||
98 | gregset->a, wb * 16)) | ||
99 | return -EFAULT; | ||
100 | |||
101 | if (__copy_from_user(regs->areg, gregset->a + wb*4, (WSBITS-wb) * 16)) | ||
102 | return -EFAULT; | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | |||
108 | int ptrace_getxregs(struct task_struct *child, void __user *uregs) | ||
109 | { | ||
110 | struct pt_regs *regs = task_pt_regs(child); | ||
111 | struct thread_info *ti = task_thread_info(child); | ||
112 | elf_xtregs_t __user *xtregs = uregs; | ||
113 | int ret = 0; | ||
114 | |||
115 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t))) | ||
116 | return -EIO; | ||
117 | |||
118 | #if XTENSA_HAVE_COPROCESSORS | ||
119 | /* Flush all coprocessor registers to memory. */ | ||
120 | coprocessor_flush_all(ti); | ||
121 | ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp, | ||
122 | sizeof(xtregs_coprocessor_t)); | ||
123 | #endif | ||
124 | ret |= __copy_to_user(&xtregs->opt, ®s->xtregs_opt, | ||
125 | sizeof(xtregs->opt)); | ||
126 | ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user, | ||
127 | sizeof(xtregs->user)); | ||
128 | |||
129 | return ret ? -EFAULT : 0; | ||
130 | } | ||
131 | |||
132 | int ptrace_setxregs(struct task_struct *child, void __user *uregs) | ||
133 | { | ||
134 | struct thread_info *ti = task_thread_info(child); | ||
135 | struct pt_regs *regs = task_pt_regs(child); | ||
136 | elf_xtregs_t *xtregs = uregs; | ||
137 | int ret = 0; | ||
138 | |||
139 | #if XTENSA_HAVE_COPROCESSORS | ||
140 | /* Flush all coprocessors before we overwrite them. */ | ||
141 | coprocessor_flush_all(ti); | ||
142 | coprocessor_release_all(ti); | ||
143 | |||
144 | ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0, | ||
145 | sizeof(xtregs_coprocessor_t)); | ||
146 | #endif | ||
147 | ret |= __copy_from_user(®s->xtregs_opt, &xtregs->opt, | ||
148 | sizeof(xtregs->opt)); | ||
149 | ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user, | ||
150 | sizeof(xtregs->user)); | ||
151 | |||
152 | return ret ? -EFAULT : 0; | ||
153 | } | ||
154 | |||
155 | int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret) | ||
156 | { | ||
157 | struct pt_regs *regs; | ||
158 | unsigned long tmp; | ||
62 | 159 | ||
63 | regs = task_pt_regs(child); | 160 | regs = task_pt_regs(child); |
64 | tmp = 0; /* Default return value. */ | 161 | tmp = 0; /* Default return value. */ |
65 | 162 | ||
66 | switch(addr) { | 163 | switch(regno) { |
67 | 164 | ||
68 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: | 165 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
69 | { | 166 | 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; | 167 | break; |
77 | } | 168 | |
78 | case REG_A_BASE ... REG_A_BASE + 15: | 169 | case REG_A_BASE ... REG_A_BASE + 15: |
79 | tmp = regs->areg[addr - REG_A_BASE]; | 170 | tmp = regs->areg[regno - REG_A_BASE]; |
80 | break; | 171 | break; |
172 | |||
81 | case REG_PC: | 173 | case REG_PC: |
82 | tmp = regs->pc; | 174 | tmp = regs->pc; |
83 | break; | 175 | break; |
176 | |||
84 | case REG_PS: | 177 | case REG_PS: |
85 | /* Note: PS.EXCM is not set while user task is running; | 178 | /* Note: PS.EXCM is not set while user task is running; |
86 | * its being set in regs is for exception handling | 179 | * its being set in regs is for exception handling |
87 | * convenience. */ | 180 | * convenience. */ |
88 | tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); | 181 | tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); |
89 | break; | 182 | break; |
183 | |||
90 | case REG_WB: | 184 | case REG_WB: |
91 | tmp = regs->windowbase; | 185 | break; /* tmp = 0 */ |
92 | break; | 186 | |
93 | case REG_WS: | 187 | case REG_WS: |
94 | tmp = regs->windowstart; | 188 | { |
189 | unsigned long wb = regs->windowbase; | ||
190 | unsigned long ws = regs->windowstart; | ||
191 | tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1); | ||
95 | break; | 192 | break; |
193 | } | ||
96 | case REG_LBEG: | 194 | case REG_LBEG: |
97 | tmp = regs->lbeg; | 195 | tmp = regs->lbeg; |
98 | break; | 196 | break; |
197 | |||
99 | case REG_LEND: | 198 | case REG_LEND: |
100 | tmp = regs->lend; | 199 | tmp = regs->lend; |
101 | break; | 200 | break; |
201 | |||
102 | case REG_LCOUNT: | 202 | case REG_LCOUNT: |
103 | tmp = regs->lcount; | 203 | tmp = regs->lcount; |
104 | break; | 204 | break; |
205 | |||
105 | case REG_SAR: | 206 | case REG_SAR: |
106 | tmp = regs->sar; | 207 | tmp = regs->sar; |
107 | break; | 208 | break; |
108 | case REG_DEPC: | 209 | |
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: | 210 | case SYSCALL_NR: |
118 | tmp = regs->syscall; | 211 | tmp = regs->syscall; |
119 | break; | 212 | 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 | 213 | ||
129 | case PTRACE_POKETEXT: /* write the word at location addr. */ | 214 | default: |
130 | case PTRACE_POKEDATA: | 215 | return -EIO; |
131 | ret = generic_ptrace_pokedata(child, addr, data); | 216 | } |
132 | goto out; | 217 | return put_user(tmp, ret); |
218 | } | ||
133 | 219 | ||
134 | case PTRACE_POKEUSR: | 220 | int ptrace_pokeusr(struct task_struct *child, long regno, long val) |
135 | { | 221 | { |
136 | struct pt_regs *regs; | 222 | struct pt_regs *regs; |
137 | regs = task_pt_regs(child); | 223 | regs = task_pt_regs(child); |
138 | 224 | ||
139 | switch (addr) { | 225 | switch (regno) { |
140 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: | 226 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
141 | { | 227 | 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; | 228 | break; |
148 | } | 229 | |
149 | case REG_A_BASE ... REG_A_BASE + 15: | 230 | case REG_A_BASE ... REG_A_BASE + 15: |
150 | regs->areg[addr - REG_A_BASE] = data; | 231 | regs->areg[regno - REG_A_BASE] = val; |
151 | break; | 232 | break; |
233 | |||
152 | case REG_PC: | 234 | case REG_PC: |
153 | regs->pc = data; | 235 | regs->pc = val; |
154 | break; | 236 | break; |
237 | |||
155 | case SYSCALL_NR: | 238 | case SYSCALL_NR: |
156 | regs->syscall = data; | 239 | regs->syscall = val; |
157 | break; | ||
158 | #ifdef TEST_KERNEL | ||
159 | case REG_WB: | ||
160 | regs->windowbase = data; | ||
161 | break; | ||
162 | case REG_WS: | ||
163 | regs->windowstart = data; | ||
164 | break; | 240 | break; |
165 | #endif | ||
166 | 241 | ||
167 | default: | 242 | default: |
168 | /* The rest are not allowed. */ | 243 | return -EIO; |
169 | ret = -EIO; | 244 | } |
170 | break; | 245 | return 0; |
171 | } | 246 | } |
247 | |||
248 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | ||
249 | { | ||
250 | int ret = -EPERM; | ||
251 | |||
252 | switch (request) { | ||
253 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
254 | case PTRACE_PEEKDATA: | ||
255 | ret = generic_ptrace_peekdata(child, addr, data); | ||
256 | break; | ||
257 | |||
258 | case PTRACE_PEEKUSR: /* read register specified by addr. */ | ||
259 | ret = ptrace_peekusr(child, addr, (void __user *) data); | ||
260 | break; | ||
261 | |||
262 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
263 | case PTRACE_POKEDATA: | ||
264 | ret = generic_ptrace_pokedata(child, addr, data); | ||
265 | break; | ||
266 | |||
267 | case PTRACE_POKEUSR: /* write register specified by addr. */ | ||
268 | ret = ptrace_pokeusr(child, addr, data); | ||
172 | break; | 269 | break; |
173 | } | ||
174 | 270 | ||
175 | /* continue and stop at next (return from) syscall */ | 271 | /* continue and stop at next (return from) syscall */ |
272 | |||
176 | case PTRACE_SYSCALL: | 273 | case PTRACE_SYSCALL: |
177 | case PTRACE_CONT: /* restart after signal. */ | 274 | case PTRACE_CONT: /* restart after signal. */ |
178 | { | 275 | { |
@@ -217,98 +314,26 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
217 | break; | 314 | break; |
218 | 315 | ||
219 | case PTRACE_GETREGS: | 316 | case PTRACE_GETREGS: |
220 | { | 317 | 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; | 318 | break; |
237 | } | ||
238 | 319 | ||
239 | case PTRACE_SETREGS: | 320 | case PTRACE_SETREGS: |
240 | { | 321 | 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; | 322 | 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 | 323 | ||
289 | ret = 0; | 324 | case PTRACE_GETXTREGS: |
290 | if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) { | 325 | 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; | 326 | break; |
298 | } | ||
299 | 327 | ||
300 | case PTRACE_GETFPREGSIZE: | 328 | case PTRACE_SETXTREGS: |
301 | /* 'data' points to 'unsigned long' set to the size | 329 | 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; | 330 | break; |
306 | 331 | ||
307 | default: | 332 | default: |
308 | ret = ptrace_request(child, request, addr, data); | 333 | ret = ptrace_request(child, request, addr, data); |
309 | goto out; | 334 | break; |
310 | } | 335 | } |
311 | out: | 336 | |
312 | return ret; | 337 | return ret; |
313 | } | 338 | } |
314 | 339 | ||