Copy FCSR in the FP regset to match the original pre-regset core dumper. The code paths for where sizeof(union fpureg) == sizeof(elf_fpreg_t) already do so, but they actually copy 4 bytes more than they should do as FCSR is only 32 bits. The not equal code paths do not copy it at all. Therefore change the copy to be done explicitly (with the correct size) for both paths. Additionally, clear the cause bits from FCSR when setting the FP regset to avoid the possibility of causing an FP exception (and an oops) in the kernel. Signed-off-by: Alex Smith <alex@xxxxxxxxxxxxxxxx> Cc: Paul Burton <paul.burton@xxxxxxxxxx> Cc: <stable@xxxxxxxxxxxxxxx> # v3.13+ --- Changes in v2: - Zero fill the last 4 bytes in the FP regset. --- arch/mips/kernel/ptrace.c | 73 +++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 8bd13ed..e082079 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -409,23 +409,35 @@ static int fpr_get(struct task_struct *target, int err; u64 fpr_val; - /* XXX fcr31 */ - - if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); - - for (i = 0; i < NUM_FPU_REGS; i++) { - fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); + if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) { err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fpr_val, i * sizeof(elf_fpreg_t), - (i + 1) * sizeof(elf_fpreg_t)); + &target->thread.fpu.fpr, + 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); if (err) return err; + } else { + for (i = 0; i < NUM_FPU_REGS; i++) { + fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fpr_val, + i * sizeof(elf_fpreg_t), + (i + 1) * sizeof(elf_fpreg_t)); + if (err) + return err; + } } - return 0; + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fcr31, + NUM_FPU_REGS * sizeof(elf_fpreg_t), + (NUM_FPU_REGS * sizeof(elf_fpreg_t)) + sizeof(u32)); + if (err) + return err; + + /* Zero fill the remaining 4 bytes. */ + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + (NUM_FPU_REGS * sizeof(elf_fpreg_t)) + sizeof(u32), + sizeof(elf_fpregset_t)); } static int fpr_set(struct task_struct *target, @@ -436,24 +448,37 @@ static int fpr_set(struct task_struct *target, unsigned i; int err; u64 fpr_val; + u32 fcr31; - /* XXX fcr31 */ - - if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) - return user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); - - for (i = 0; i < NUM_FPU_REGS; i++) { + if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) { err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &fpr_val, i * sizeof(elf_fpreg_t), - (i + 1) * sizeof(elf_fpreg_t)); + &target->thread.fpu.fpr, + 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); if (err) return err; - set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); + } else { + for (i = 0; i < NUM_FPU_REGS; i++) { + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fpr_val, + i * sizeof(elf_fpreg_t), + (i + 1) * sizeof(elf_fpreg_t)); + if (err) + return err; + set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); + } } - return 0; + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fcr31, + NUM_FPU_REGS * sizeof(elf_fpreg_t), + (NUM_FPU_REGS * sizeof(elf_fpreg_t)) + sizeof(u32)); + if (err) + return err; + + target->thread.fpu.fcr31 = fcr31 & ~FPU_CSR_ALL_X; + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + (NUM_FPU_REGS * sizeof(elf_fpreg_t)) + sizeof(u32), + sizeof(elf_fpregset_t)); } enum mips_regset { -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html