MIPS: ptrace: Expose FIR register through FP regset
authorMaciej W. Rozycki <macro@mips.com>
Mon, 30 Apr 2018 14:56:47 +0000 (15:56 +0100)
committerJames Hogan <jhogan@kernel.org>
Mon, 14 May 2018 22:54:25 +0000 (23:54 +0100)
Correct commit 7aeb753b5353 ("MIPS: Implement task_user_regset_view.")
and expose the FIR register using the unused 4 bytes at the end of the
NT_PRFPREG regset.  Without that register included clients cannot use
the PTRACE_GETREGSET request to retrieve the complete FPU register set
and have to resort to one of the older interfaces, either PTRACE_PEEKUSR
or PTRACE_GETFPREGS, to retrieve the missing piece of data.  Also the
register is irreversibly missing from core dumps.

This register is architecturally hardwired and read-only so the write
path does not matter.  Ignore data supplied on writes then.

Fixes: 7aeb753b5353 ("MIPS: Implement task_user_regset_view.")
Signed-off-by: James Hogan <jhogan@kernel.org>
Signed-off-by: Maciej W. Rozycki <macro@mips.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: <stable@vger.kernel.org> # 3.13+
Patchwork: https://patchwork.linux-mips.org/patch/19273/
Signed-off-by: James Hogan <jhogan@kernel.org>
arch/mips/kernel/ptrace.c

index 0b23b1ad99e65f1e21d1810340f9dd306483b8d3..aede42990f086b903dfb72c508bff1db89c5ccfe 100644 (file)
@@ -463,7 +463,7 @@ static int fpr_get_msa(struct task_struct *target,
 /*
  * Copy the floating-point context to the supplied NT_PRFPREG buffer.
  * Choose the appropriate helper for general registers, and then copy
- * the FCSR register separately.
+ * the FCSR and FIR registers separately.
  */
 static int fpr_get(struct task_struct *target,
                   const struct user_regset *regset,
@@ -471,6 +471,7 @@ static int fpr_get(struct task_struct *target,
                   void *kbuf, void __user *ubuf)
 {
        const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+       const int fir_pos = fcr31_pos + sizeof(u32);
        int err;
 
        if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
@@ -483,6 +484,12 @@ static int fpr_get(struct task_struct *target,
        err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
                                  &target->thread.fpu.fcr31,
                                  fcr31_pos, fcr31_pos + sizeof(u32));
+       if (err)
+               return err;
+
+       err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                 &boot_cpu_data.fpu_id,
+                                 fir_pos, fir_pos + sizeof(u32));
 
        return err;
 }
@@ -531,7 +538,8 @@ static int fpr_set_msa(struct task_struct *target,
 /*
  * Copy the supplied NT_PRFPREG buffer to the floating-point context.
  * Choose the appropriate helper for general registers, and then copy
- * the FCSR register separately.
+ * the FCSR register separately.  Ignore the incoming FIR register
+ * contents though, as the register is read-only.
  *
  * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0',
  * which is supposed to have been guaranteed by the kernel before
@@ -545,6 +553,7 @@ static int fpr_set(struct task_struct *target,
                   const void *kbuf, const void __user *ubuf)
 {
        const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+       const int fir_pos = fcr31_pos + sizeof(u32);
        u32 fcr31;
        int err;
 
@@ -572,6 +581,11 @@ static int fpr_set(struct task_struct *target,
                ptrace_setfcr31(target, fcr31);
        }
 
+       if (count > 0)
+               err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+                                               fir_pos,
+                                               fir_pos + sizeof(u32));
+
        return err;
 }