diff options
author | David S. Miller <davem@davemloft.net> | 2008-02-06 21:00:44 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-02-07 02:58:59 -0800 |
commit | 8e3fe806e50d48d875bb56793ca3f984cba6c0db (patch) | |
tree | 8eea5838f20d841c43ba40e6ec985edf56a09b62 /arch/sparc | |
parent | d09c2a23ee4220a6341166a7dab5601258fef91f (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')
-rw-r--r-- | arch/sparc/kernel/ptrace.c | 285 |
1 files changed, 284 insertions, 1 deletions
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 7452269bba2..c1e7e6ae7c6 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -1,6 +1,6 @@ /* ptrace.c: Sparc process tracing support. * - * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) * * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, * and David Mosberger. @@ -19,6 +19,8 @@ #include <linux/smp_lock.h> #include <linux/security.h> #include <linux/signal.h> +#include <linux/regset.h> +#include <linux/elf.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -258,6 +260,287 @@ void ptrace_disable(struct task_struct *child) /* nothing to do */ } +enum sparc_regset { + REGSET_GENERAL, + REGSET_FP, +}; + +static int genregs32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = target->thread.kregs; + unsigned long __user *reg_window; + unsigned long *k = kbuf; + unsigned long __user *u = ubuf; + unsigned long reg; + + if (target == current) + flush_user_windows(); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) { + for (; count > 0 && pos < 16; count--) + *k++ = regs->u_regs[pos++]; + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(*k++, ®_window[pos++])) + return -EFAULT; + } + } else { + for (; count > 0 && pos < 16; count--) { + if (put_user(regs->u_regs[pos++], u++)) + return -EFAULT; + } + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(reg, ®_window[pos++]) || + put_user(reg, u++)) + return -EFAULT; + } + } + while (count > 0) { + switch (pos) { + case 32: /* PSR */ + reg = regs->psr; + break; + case 33: /* PC */ + reg = regs->pc; + break; + case 34: /* NPC */ + reg = regs->npc; + break; + case 35: /* Y */ + reg = regs->y; + break; + case 36: /* WIM */ + case 37: /* TBR */ + reg = 0; + break; + default: + goto finish; + } + + if (kbuf) + *k++ = reg; + else if (put_user(reg, u++)) + return -EFAULT; + pos++; + count--; + } +finish: + pos *= sizeof(reg); + count *= sizeof(reg); + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 38 * sizeof(reg), -1); +} + +static int genregs32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = target->thread.kregs; + unsigned long __user *reg_window; + const unsigned long *k = kbuf; + const unsigned long __user *u = ubuf; + unsigned long reg; + + if (target == current) + flush_user_windows(); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) { + for (; count > 0 && pos < 16; count--) + regs->u_regs[pos++] = *k++; + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (put_user(*k++, ®_window[pos++])) + return -EFAULT; + } + } else { + for (; count > 0 && pos < 16; count--) { + if (get_user(reg, u++)) + return -EFAULT; + regs->u_regs[pos++] = reg; + } + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(reg, u++) || + put_user(reg, ®_window[pos++])) + return -EFAULT; + } + } + while (count > 0) { + unsigned long psr; + + if (kbuf) + reg = *k++; + else if (get_user(reg, u++)) + return -EFAULT; + + switch (pos) { + case 32: /* PSR */ + psr = regs->psr; + psr &= ~PSR_ICC; + psr |= (reg & PSR_ICC); + regs->psr = psr; + break; + case 33: /* PC */ + regs->pc = reg; + break; + case 34: /* NPC */ + regs->npc = reg; + break; + case 35: /* Y */ + regs->y = reg; + break; + case 36: /* WIM */ + case 37: /* TBR */ + break; + default: + goto finish; + } + + pos++; + count--; + } +finish: + pos *= sizeof(reg); + count *= sizeof(reg); + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 38 * sizeof(reg), -1); +} + +static int fpregs32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = target->thread.float_regs; + int ret = 0; + +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 33 * sizeof(u32), + 34 * sizeof(u32)); + + if (!ret) { + unsigned long val; + + val = (1 << 8) | (8 << 16); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &val, + 34 * sizeof(u32), + 35 * sizeof(u32)); + } + + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 35 * sizeof(u32), -1); + + return ret; +} + +static int fpregs32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = target->thread.float_regs; + int ret; + +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 33 * sizeof(u32), + 34 * sizeof(u32)); + } + + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 34 * sizeof(u32), -1); + return ret; +} + +static const struct user_regset sparc32_regsets[] = { + /* Format is: + * G0 --> G7 + * O0 --> O7 + * L0 --> L7 + * I0 --> I7 + * PSR, PC, nPC, Y, WIM, TBR + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = 38 * sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .get = genregs32_get, .set = genregs32_set + }, + /* Format is: + * F0 --> F31 + * empty 32-bit word + * FSR (32--bit word) + * FPU QUEUE COUNT (8-bit char) + * FPU QUEUE ENTRYSIZE (8-bit char) + * FPU ENABLED (8-bit char) + * empty 8-bit char + * FPU QUEUE (64 32-bit ints) + */ + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, + .n = 99 * sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .get = fpregs32_get, .set = fpregs32_set + }, +}; + +static const struct user_regset_view user_sparc32_view = { + .name = "sparc", .e_machine = EM_SPARC, + .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_sparc32_view; +} + asmlinkage void do_ptrace(struct pt_regs *regs) { unsigned long request = regs->u_regs[UREG_I0]; |