| From: "Maciej W. Rozycki" <macro@mips.com> |
| Date: Mon, 30 Apr 2018 15:56:47 +0100 |
| Subject: MIPS: ptrace: Expose FIR register through FP regset |
| |
| commit 71e909c0cdad28a1df1fa14442929e68615dee45 upstream. |
| |
| 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 |
| Patchwork: https://patchwork.linux-mips.org/patch/19273/ |
| Signed-off-by: James Hogan <jhogan@kernel.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| arch/mips/kernel/ptrace.c | 18 ++++++++++++++++-- |
| 1 file changed, 16 insertions(+), 2 deletions(-) |
| |
| --- a/arch/mips/kernel/ptrace.c |
| +++ b/arch/mips/kernel/ptrace.c |
| @@ -481,7 +481,7 @@ static int fpr_get_msa(struct task_struc |
| /* |
| * 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, |
| @@ -489,6 +489,7 @@ static int fpr_get(struct task_struct *t |
| 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)) |
| @@ -501,6 +502,12 @@ static int fpr_get(struct task_struct *t |
| 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; |
| } |
| @@ -549,7 +556,8 @@ static int fpr_set_msa(struct task_struc |
| /* |
| * 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 |
| @@ -563,6 +571,7 @@ static int fpr_set(struct task_struct *t |
| 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; |
| |
| @@ -590,6 +599,11 @@ static int fpr_set(struct task_struct *t |
| ptrace_setfcr31(target, fcr31); |
| } |
| |
| + if (count > 0) |
| + err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
| + fir_pos, |
| + fir_pos + sizeof(u32)); |
| + |
| return err; |
| } |
| |