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:
commit
44cae9b209
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue