Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus

Pull MIPS fixes from Ralf Baechle:
 "Another round of MIPS fixes for 4.15.

   - Maciej Rozycki found another series of FP issues which requires a
     seven part series to restructure and fix.

   - James fixes a warning about .set mt which gas doesn't like when
     building for R1 processors"

* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus:
  MIPS: Validate PR_SET_FP_MODE prctl(2) requests against the ABI of the task
  MIPS: Disallow outsized PTRACE_SETREGSET NT_PRFPREG regset accesses
  MIPS: Also verify sizeof `elf_fpreg_t' with PTRACE_SETREGSET
  MIPS: Fix an FCSR access API regression with NT_PRFPREG and MSA
  MIPS: Consistently handle buffer counter with PTRACE_SETREGSET
  MIPS: Guard against any partial write attempt with PTRACE_SETREGSET
  MIPS: Factor out NT_PRFPREG regset access helpers
  MIPS: CPS: Fix r1 .set mt assembler warning
This commit is contained in:
Linus Torvalds 2018-01-09 15:43:13 -08:00
commit 44cae9b209
3 changed files with 139 additions and 28 deletions

View File

@ -235,6 +235,7 @@ LEAF(mips_cps_core_init)
has_mt t0, 3f has_mt t0, 3f
.set push .set push
.set MIPS_ISA_LEVEL_RAW
.set mt .set mt
/* Only allow 1 TC per VPE to execute... */ /* Only allow 1 TC per VPE to execute... */
@ -388,6 +389,7 @@ LEAF(mips_cps_boot_vpes)
#elif defined(CONFIG_MIPS_MT) #elif defined(CONFIG_MIPS_MT)
.set push .set push
.set MIPS_ISA_LEVEL_RAW
.set mt .set mt
/* If the core doesn't support MT then return */ /* If the core doesn't support MT then return */

View File

@ -705,6 +705,18 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
struct task_struct *t; struct task_struct *t;
int max_users; int max_users;
/* If nothing to change, return right away, successfully. */
if (value == mips_get_process_fp_mode(task))
return 0;
/* Only accept a mode change if 64-bit FP enabled for o32. */
if (!IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT))
return -EOPNOTSUPP;
/* And only for o32 tasks. */
if (IS_ENABLED(CONFIG_64BIT) && !test_thread_flag(TIF_32BIT_REGS))
return -EOPNOTSUPP;
/* Check the value is valid */ /* Check the value is valid */
if (value & ~known_bits) if (value & ~known_bits)
return -EOPNOTSUPP; return -EOPNOTSUPP;

View File

@ -419,25 +419,38 @@ static int gpr64_set(struct task_struct *target,
#endif /* CONFIG_64BIT */ #endif /* CONFIG_64BIT */
static int fpr_get(struct task_struct *target, /*
const struct user_regset *regset, * Copy the floating-point context to the supplied NT_PRFPREG buffer,
unsigned int pos, unsigned int count, * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots
void *kbuf, void __user *ubuf) * correspond 1:1 to buffer slots. Only general registers are copied.
*/
static int fpr_get_fpa(struct task_struct *target,
unsigned int *pos, unsigned int *count,
void **kbuf, void __user **ubuf)
{ {
unsigned i; return user_regset_copyout(pos, count, kbuf, ubuf,
int err; &target->thread.fpu,
0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
}
/*
* Copy the floating-point context to the supplied NT_PRFPREG buffer,
* CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's
* general register slots are copied to buffer slots. Only general
* registers are copied.
*/
static int fpr_get_msa(struct task_struct *target,
unsigned int *pos, unsigned int *count,
void **kbuf, void __user **ubuf)
{
unsigned int i;
u64 fpr_val; u64 fpr_val;
int err;
/* XXX fcr31 */ BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
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++) { for (i = 0; i < NUM_FPU_REGS; i++) {
fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0);
err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, err = user_regset_copyout(pos, count, kbuf, ubuf,
&fpr_val, i * sizeof(elf_fpreg_t), &fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t)); (i + 1) * sizeof(elf_fpreg_t));
if (err) if (err)
@ -447,27 +460,64 @@ static int fpr_get(struct task_struct *target,
return 0; return 0;
} }
static int fpr_set(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.
*/
static int fpr_get(struct task_struct *target,
const struct user_regset *regset, const struct user_regset *regset,
unsigned int pos, unsigned int count, unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf) void *kbuf, void __user *ubuf)
{ {
unsigned i; const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
int err; int err;
if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf);
else
err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf);
if (err)
return err;
err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fpu.fcr31,
fcr31_pos, fcr31_pos + sizeof(u32));
return err;
}
/*
* Copy the supplied NT_PRFPREG buffer to the floating-point context,
* !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP
* context's general register slots. Only general registers are copied.
*/
static int fpr_set_fpa(struct task_struct *target,
unsigned int *pos, unsigned int *count,
const void **kbuf, const void __user **ubuf)
{
return user_regset_copyin(pos, count, kbuf, ubuf,
&target->thread.fpu,
0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
}
/*
* Copy the supplied NT_PRFPREG buffer to the floating-point context,
* CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64
* bits only of FP context's general register slots. Only general
* registers are copied.
*/
static int fpr_set_msa(struct task_struct *target,
unsigned int *pos, unsigned int *count,
const void **kbuf, const void __user **ubuf)
{
unsigned int i;
u64 fpr_val; u64 fpr_val;
int err;
/* XXX fcr31 */
init_fp_ctx(target);
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));
BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) { for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) {
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, err = user_regset_copyin(pos, count, kbuf, ubuf,
&fpr_val, i * sizeof(elf_fpreg_t), &fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t)); (i + 1) * sizeof(elf_fpreg_t));
if (err) if (err)
@ -478,6 +528,53 @@ static int fpr_set(struct task_struct *target,
return 0; return 0;
} }
/*
* 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.
*
* We optimize for the case where `count % sizeof(elf_fpreg_t) == 0',
* which is supposed to have been guaranteed by the kernel before
* calling us, e.g. in `ptrace_regset'. We enforce that requirement,
* so that we can safely avoid preinitializing temporaries for
* partial register writes.
*/
static int fpr_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
u32 fcr31;
int err;
BUG_ON(count % sizeof(elf_fpreg_t));
if (pos + count > sizeof(elf_fpregset_t))
return -EIO;
init_fp_ctx(target);
if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf);
else
err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf);
if (err)
return err;
if (count > 0) {
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&fcr31,
fcr31_pos, fcr31_pos + sizeof(u32));
if (err)
return err;
ptrace_setfcr31(target, fcr31);
}
return err;
}
enum mips_regset { enum mips_regset {
REGSET_GPR, REGSET_GPR,
REGSET_FPR, REGSET_FPR,