aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-02-07 00:00:44 -0500
committerDavid S. Miller <davem@davemloft.net>2008-02-07 05:58:59 -0500
commit8e3fe806e50d48d875bb56793ca3f984cba6c0db (patch)
tree8eea5838f20d841c43ba40e6ec985edf56a09b62 /arch/sparc/kernel
parentd09c2a23ee4220a6341166a7dab5601258fef91f (diff)
[SPARC32]: Add user regset support.
It is missing lazy FPU handling for the current task, but that can be added later. Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r--arch/sparc/kernel/ptrace.c285
1 files changed, 284 insertions, 1 deletions
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c
index 7452269bba2a..c1e7e6ae7c6f 100644
--- a/arch/sparc/kernel/ptrace.c
+++ b/arch/sparc/kernel/ptrace.c
@@ -1,6 +1,6 @@
1/* ptrace.c: Sparc process tracing support. 1/* ptrace.c: Sparc process tracing support.
2 * 2 *
3 * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) 3 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
4 * 4 *
5 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, 5 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
6 * and David Mosberger. 6 * and David Mosberger.
@@ -19,6 +19,8 @@
19#include <linux/smp_lock.h> 19#include <linux/smp_lock.h>
20#include <linux/security.h> 20#include <linux/security.h>
21#include <linux/signal.h> 21#include <linux/signal.h>
22#include <linux/regset.h>
23#include <linux/elf.h>
22 24
23#include <asm/pgtable.h> 25#include <asm/pgtable.h>
24#include <asm/system.h> 26#include <asm/system.h>
@@ -258,6 +260,287 @@ void ptrace_disable(struct task_struct *child)
258 /* nothing to do */ 260 /* nothing to do */
259} 261}
260 262
263enum sparc_regset {
264 REGSET_GENERAL,
265 REGSET_FP,
266};
267
268static int genregs32_get(struct task_struct *target,
269 const struct user_regset *regset,
270 unsigned int pos, unsigned int count,
271 void *kbuf, void __user *ubuf)
272{
273 const struct pt_regs *regs = target->thread.kregs;
274 unsigned long __user *reg_window;
275 unsigned long *k = kbuf;
276 unsigned long __user *u = ubuf;
277 unsigned long reg;
278
279 if (target == current)
280 flush_user_windows();
281
282 pos /= sizeof(reg);
283 count /= sizeof(reg);
284
285 if (kbuf) {
286 for (; count > 0 && pos < 16; count--)
287 *k++ = regs->u_regs[pos++];
288
289 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
290 for (; count > 0 && pos < 32; count--) {
291 if (get_user(*k++, &reg_window[pos++]))
292 return -EFAULT;
293 }
294 } else {
295 for (; count > 0 && pos < 16; count--) {
296 if (put_user(regs->u_regs[pos++], u++))
297 return -EFAULT;
298 }
299
300 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
301 for (; count > 0 && pos < 32; count--) {
302 if (get_user(reg, &reg_window[pos++]) ||
303 put_user(reg, u++))
304 return -EFAULT;
305 }
306 }
307 while (count > 0) {
308 switch (pos) {
309 case 32: /* PSR */
310 reg = regs->psr;
311 break;
312 case 33: /* PC */
313 reg = regs->pc;
314 break;
315 case 34: /* NPC */
316 reg = regs->npc;
317 break;
318 case 35: /* Y */
319 reg = regs->y;
320 break;
321 case 36: /* WIM */
322 case 37: /* TBR */
323 reg = 0;
324 break;
325 default:
326 goto finish;
327 }
328
329 if (kbuf)
330 *k++ = reg;
331 else if (put_user(reg, u++))
332 return -EFAULT;
333 pos++;
334 count--;
335 }
336finish:
337 pos *= sizeof(reg);
338 count *= sizeof(reg);
339
340 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
341 38 * sizeof(reg), -1);
342}
343
344static int genregs32_set(struct task_struct *target,
345 const struct user_regset *regset,
346 unsigned int pos, unsigned int count,
347 const void *kbuf, const void __user *ubuf)
348{
349 struct pt_regs *regs = target->thread.kregs;
350 unsigned long __user *reg_window;
351 const unsigned long *k = kbuf;
352 const unsigned long __user *u = ubuf;
353 unsigned long reg;
354
355 if (target == current)
356 flush_user_windows();
357
358 pos /= sizeof(reg);
359 count /= sizeof(reg);
360
361 if (kbuf) {
362 for (; count > 0 && pos < 16; count--)
363 regs->u_regs[pos++] = *k++;
364
365 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
366 for (; count > 0 && pos < 32; count--) {
367 if (put_user(*k++, &reg_window[pos++]))
368 return -EFAULT;
369 }
370 } else {
371 for (; count > 0 && pos < 16; count--) {
372 if (get_user(reg, u++))
373 return -EFAULT;
374 regs->u_regs[pos++] = reg;
375 }
376
377 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
378 for (; count > 0 && pos < 32; count--) {
379 if (get_user(reg, u++) ||
380 put_user(reg, &reg_window[pos++]))
381 return -EFAULT;
382 }
383 }
384 while (count > 0) {
385 unsigned long psr;
386
387 if (kbuf)
388 reg = *k++;
389 else if (get_user(reg, u++))
390 return -EFAULT;
391
392 switch (pos) {
393 case 32: /* PSR */
394 psr = regs->psr;
395 psr &= ~PSR_ICC;
396 psr |= (reg & PSR_ICC);
397 regs->psr = psr;
398 break;
399 case 33: /* PC */
400 regs->pc = reg;
401 break;
402 case 34: /* NPC */
403 regs->npc = reg;
404 break;
405 case 35: /* Y */
406 regs->y = reg;
407 break;
408 case 36: /* WIM */
409 case 37: /* TBR */
410 break;
411 default:
412 goto finish;
413 }
414
415 pos++;
416 count--;
417 }
418finish:
419 pos *= sizeof(reg);
420 count *= sizeof(reg);
421
422 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
423 38 * sizeof(reg), -1);
424}
425
426static int fpregs32_get(struct task_struct *target,
427 const struct user_regset *regset,
428 unsigned int pos, unsigned int count,
429 void *kbuf, void __user *ubuf)
430{
431 const unsigned long *fpregs = target->thread.float_regs;
432 int ret = 0;
433
434#if 0
435 if (target == current)
436 save_and_clear_fpu();
437#endif
438
439 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
440 fpregs,
441 0, 32 * sizeof(u32));
442
443 if (!ret)
444 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
445 32 * sizeof(u32),
446 33 * sizeof(u32));
447 if (!ret)
448 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
449 &target->thread.fsr,
450 33 * sizeof(u32),
451 34 * sizeof(u32));
452
453 if (!ret) {
454 unsigned long val;
455
456 val = (1 << 8) | (8 << 16);
457 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
458 &val,
459 34 * sizeof(u32),
460 35 * sizeof(u32));
461 }
462
463 if (!ret)
464 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
465 35 * sizeof(u32), -1);
466
467 return ret;
468}
469
470static int fpregs32_set(struct task_struct *target,
471 const struct user_regset *regset,
472 unsigned int pos, unsigned int count,
473 const void *kbuf, const void __user *ubuf)
474{
475 unsigned long *fpregs = target->thread.float_regs;
476 int ret;
477
478#if 0
479 if (target == current)
480 save_and_clear_fpu();
481#endif
482 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
483 fpregs,
484 0, 32 * sizeof(u32));
485 if (!ret)
486 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
487 32 * sizeof(u32),
488 33 * sizeof(u32));
489 if (!ret && count > 0) {
490 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
491 &target->thread.fsr,
492 33 * sizeof(u32),
493 34 * sizeof(u32));
494 }
495
496 if (!ret)
497 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
498 34 * sizeof(u32), -1);
499 return ret;
500}
501
502static const struct user_regset sparc32_regsets[] = {
503 /* Format is:
504 * G0 --> G7
505 * O0 --> O7
506 * L0 --> L7
507 * I0 --> I7
508 * PSR, PC, nPC, Y, WIM, TBR
509 */
510 [REGSET_GENERAL] = {
511 .core_note_type = NT_PRSTATUS,
512 .n = 38 * sizeof(u32),
513 .size = sizeof(u32), .align = sizeof(u32),
514 .get = genregs32_get, .set = genregs32_set
515 },
516 /* Format is:
517 * F0 --> F31
518 * empty 32-bit word
519 * FSR (32--bit word)
520 * FPU QUEUE COUNT (8-bit char)
521 * FPU QUEUE ENTRYSIZE (8-bit char)
522 * FPU ENABLED (8-bit char)
523 * empty 8-bit char
524 * FPU QUEUE (64 32-bit ints)
525 */
526 [REGSET_FP] = {
527 .core_note_type = NT_PRFPREG,
528 .n = 99 * sizeof(u32),
529 .size = sizeof(u32), .align = sizeof(u32),
530 .get = fpregs32_get, .set = fpregs32_set
531 },
532};
533
534static const struct user_regset_view user_sparc32_view = {
535 .name = "sparc", .e_machine = EM_SPARC,
536 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
537};
538
539const struct user_regset_view *task_user_regset_view(struct task_struct *task)
540{
541 return &user_sparc32_view;
542}
543
261asmlinkage void do_ptrace(struct pt_regs *regs) 544asmlinkage void do_ptrace(struct pt_regs *regs)
262{ 545{
263 unsigned long request = regs->u_regs[UREG_I0]; 546 unsigned long request = regs->u_regs[UREG_I0];