diff options
author | Maciej W. Rozycki <macro@mips.com> | 2018-04-30 10:56:47 -0400 |
---|---|---|
committer | James Hogan <jhogan@kernel.org> | 2018-05-14 18:54:25 -0400 |
commit | 71e909c0cdad28a1df1fa14442929e68615dee45 (patch) | |
tree | 7b01e10ca233ba665dc4b2c883e661ff68cc4d23 | |
parent | c60128ce97674fd05adb8b5ae79eb6745a03192e (diff) |
MIPS: ptrace: Expose FIR register through FP regset
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>
-rw-r--r-- | arch/mips/kernel/ptrace.c | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 0b23b1ad99e6..aede42990f08 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -463,7 +463,7 @@ static int fpr_get_msa(struct task_struct *target, | |||
463 | /* | 463 | /* |
464 | * Copy the floating-point context to the supplied NT_PRFPREG buffer. | 464 | * Copy the floating-point context to the supplied NT_PRFPREG buffer. |
465 | * Choose the appropriate helper for general registers, and then copy | 465 | * Choose the appropriate helper for general registers, and then copy |
466 | * the FCSR register separately. | 466 | * the FCSR and FIR registers separately. |
467 | */ | 467 | */ |
468 | static int fpr_get(struct task_struct *target, | 468 | static int fpr_get(struct task_struct *target, |
469 | const struct user_regset *regset, | 469 | const struct user_regset *regset, |
@@ -471,6 +471,7 @@ static int fpr_get(struct task_struct *target, | |||
471 | void *kbuf, void __user *ubuf) | 471 | void *kbuf, void __user *ubuf) |
472 | { | 472 | { |
473 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); | 473 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); |
474 | const int fir_pos = fcr31_pos + sizeof(u32); | ||
474 | int err; | 475 | int err; |
475 | 476 | ||
476 | if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) | 477 | if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) |
@@ -483,6 +484,12 @@ static int fpr_get(struct task_struct *target, | |||
483 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 484 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
484 | &target->thread.fpu.fcr31, | 485 | &target->thread.fpu.fcr31, |
485 | fcr31_pos, fcr31_pos + sizeof(u32)); | 486 | fcr31_pos, fcr31_pos + sizeof(u32)); |
487 | if (err) | ||
488 | return err; | ||
489 | |||
490 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
491 | &boot_cpu_data.fpu_id, | ||
492 | fir_pos, fir_pos + sizeof(u32)); | ||
486 | 493 | ||
487 | return err; | 494 | return err; |
488 | } | 495 | } |
@@ -531,7 +538,8 @@ static int fpr_set_msa(struct task_struct *target, | |||
531 | /* | 538 | /* |
532 | * Copy the supplied NT_PRFPREG buffer to the floating-point context. | 539 | * Copy the supplied NT_PRFPREG buffer to the floating-point context. |
533 | * Choose the appropriate helper for general registers, and then copy | 540 | * Choose the appropriate helper for general registers, and then copy |
534 | * the FCSR register separately. | 541 | * the FCSR register separately. Ignore the incoming FIR register |
542 | * contents though, as the register is read-only. | ||
535 | * | 543 | * |
536 | * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', | 544 | * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', |
537 | * which is supposed to have been guaranteed by the kernel before | 545 | * which is supposed to have been guaranteed by the kernel before |
@@ -545,6 +553,7 @@ static int fpr_set(struct task_struct *target, | |||
545 | const void *kbuf, const void __user *ubuf) | 553 | const void *kbuf, const void __user *ubuf) |
546 | { | 554 | { |
547 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); | 555 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); |
556 | const int fir_pos = fcr31_pos + sizeof(u32); | ||
548 | u32 fcr31; | 557 | u32 fcr31; |
549 | int err; | 558 | int err; |
550 | 559 | ||
@@ -572,6 +581,11 @@ static int fpr_set(struct task_struct *target, | |||
572 | ptrace_setfcr31(target, fcr31); | 581 | ptrace_setfcr31(target, fcr31); |
573 | } | 582 | } |
574 | 583 | ||
584 | if (count > 0) | ||
585 | err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
586 | fir_pos, | ||
587 | fir_pos + sizeof(u32)); | ||
588 | |||
575 | return err; | 589 | return err; |
576 | } | 590 | } |
577 | 591 | ||