diff options
Diffstat (limited to 'arch/mips/kernel/ptrace.c')
-rw-r--r-- | arch/mips/kernel/ptrace.c | 466 |
1 files changed, 306 insertions, 160 deletions
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index e5ba56c01ee0..ea54575255ea 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -50,25 +50,6 @@ #define CREATE_TRACE_POINTS #include <trace/events/syscalls.h> -static void init_fp_ctx(struct task_struct *target) -{ - /* If FP has been used then the target already has context */ - if (tsk_used_math(target)) - return; - - /* Begin with data registers set to all 1s... */ - memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr)); - - /* FCSR has been preset by `mips_set_personality_nan'. */ - - /* - * Record that the target has "used" math, such that the context - * just initialised, and any modifications made by the caller, - * aren't discarded. - */ - set_stopped_child_used_math(target); -} - /* * Called by kernel/ptrace.c when detaching.. * @@ -81,21 +62,6 @@ void ptrace_disable(struct task_struct *child) } /* - * Poke at FCSR according to its mask. Set the Cause bits even - * if a corresponding Enable bit is set. This will be noticed at - * the time the thread is switched to and SIGFPE thrown accordingly. - */ -static void ptrace_setfcr31(struct task_struct *child, u32 value) -{ - u32 fcr31; - u32 mask; - - fcr31 = child->thread.fpu.fcr31; - mask = boot_cpu_data.fpu_msk31; - child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); -} - -/* * Read a general register set. We always use the 64-bit format, even * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. * Registers are sign extended to fill the available space. @@ -151,55 +117,6 @@ int ptrace_setregs(struct task_struct *child, struct user_pt_regs __user *data) return 0; } -int ptrace_getfpregs(struct task_struct *child, __u32 __user *data) -{ - int i; - - if (!access_ok(VERIFY_WRITE, data, 33 * 8)) - return -EIO; - - if (tsk_used_math(child)) { - union fpureg *fregs = get_fpu_regs(child); - for (i = 0; i < 32; i++) - __put_user(get_fpr64(&fregs[i], 0), - i + (__u64 __user *)data); - } else { - for (i = 0; i < 32; i++) - __put_user((__u64) -1, i + (__u64 __user *) data); - } - - __put_user(child->thread.fpu.fcr31, data + 64); - __put_user(boot_cpu_data.fpu_id, data + 65); - - return 0; -} - -int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) -{ - union fpureg *fregs; - u64 fpr_val; - u32 value; - int i; - - if (!access_ok(VERIFY_READ, data, 33 * 8)) - return -EIO; - - init_fp_ctx(child); - fregs = get_fpu_regs(child); - - for (i = 0; i < 32; i++) { - __get_user(fpr_val, i + (__u64 __user *)data); - set_fpr64(&fregs[i], 0, fpr_val); - } - - __get_user(value, data + 64); - ptrace_setfcr31(child, value); - - /* FIR may not be written. */ - - return 0; -} - int ptrace_get_watch_regs(struct task_struct *child, struct pt_watch_regs __user *addr) { @@ -420,6 +337,73 @@ static int gpr64_set(struct task_struct *target, #endif /* CONFIG_64BIT */ + +#ifdef CONFIG_MIPS_FP_SUPPORT + +/* + * Poke at FCSR according to its mask. Set the Cause bits even + * if a corresponding Enable bit is set. This will be noticed at + * the time the thread is switched to and SIGFPE thrown accordingly. + */ +static void ptrace_setfcr31(struct task_struct *child, u32 value) +{ + u32 fcr31; + u32 mask; + + fcr31 = child->thread.fpu.fcr31; + mask = boot_cpu_data.fpu_msk31; + child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); +} + +int ptrace_getfpregs(struct task_struct *child, __u32 __user *data) +{ + int i; + + if (!access_ok(VERIFY_WRITE, data, 33 * 8)) + return -EIO; + + if (tsk_used_math(child)) { + union fpureg *fregs = get_fpu_regs(child); + for (i = 0; i < 32; i++) + __put_user(get_fpr64(&fregs[i], 0), + i + (__u64 __user *)data); + } else { + for (i = 0; i < 32; i++) + __put_user((__u64) -1, i + (__u64 __user *) data); + } + + __put_user(child->thread.fpu.fcr31, data + 64); + __put_user(boot_cpu_data.fpu_id, data + 65); + + return 0; +} + +int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) +{ + union fpureg *fregs; + u64 fpr_val; + u32 value; + int i; + + if (!access_ok(VERIFY_READ, data, 33 * 8)) + return -EIO; + + init_fp_ctx(child); + fregs = get_fpu_regs(child); + + for (i = 0; i < 32; i++) { + __get_user(fpr_val, i + (__u64 __user *)data); + set_fpr64(&fregs[i], 0, fpr_val); + } + + __get_user(value, data + 64); + ptrace_setfcr31(child, value); + + /* FIR may not be written. */ + + return 0; +} + /* * Copy the floating-point context to the supplied NT_PRFPREG buffer, * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots @@ -590,6 +574,178 @@ static int fpr_set(struct task_struct *target, return err; } +/* Copy the FP mode setting to the supplied NT_MIPS_FP_MODE buffer. */ +static int fp_mode_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int fp_mode; + + fp_mode = mips_get_process_fp_mode(target); + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, + sizeof(fp_mode)); +} + +/* + * Copy the supplied NT_MIPS_FP_MODE buffer to the FP mode setting. + * + * We optimize for the case where `count % sizeof(int) == 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 + * mode writes. + */ +static int fp_mode_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int fp_mode; + int err; + + BUG_ON(count % sizeof(int)); + + if (pos + count > sizeof(fp_mode)) + return -EIO; + + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, + sizeof(fp_mode)); + if (err) + return err; + + if (count > 0) + err = mips_set_process_fp_mode(target, fp_mode); + + return err; +} + +#endif /* CONFIG_MIPS_FP_SUPPORT */ + +#ifdef CONFIG_CPU_HAS_MSA + +struct msa_control_regs { + unsigned int fir; + unsigned int fcsr; + unsigned int msair; + unsigned int msacsr; +}; + +static int copy_pad_fprs(struct task_struct *target, + const struct user_regset *regset, + unsigned int *ppos, unsigned int *pcount, + void **pkbuf, void __user **pubuf, + unsigned int live_sz) +{ + int i, j, start, start_pad, err; + unsigned long long fill = ~0ull; + unsigned int cp_sz, pad_sz; + + cp_sz = min(regset->size, live_sz); + pad_sz = regset->size - cp_sz; + WARN_ON(pad_sz % sizeof(fill)); + + i = start = err = 0; + for (; i < NUM_FPU_REGS; i++, start += regset->size) { + err |= user_regset_copyout(ppos, pcount, pkbuf, pubuf, + &target->thread.fpu.fpr[i], + start, start + cp_sz); + + start_pad = start + cp_sz; + for (j = 0; j < (pad_sz / sizeof(fill)); j++) { + err |= user_regset_copyout(ppos, pcount, pkbuf, pubuf, + &fill, start_pad, + start_pad + sizeof(fill)); + start_pad += sizeof(fill); + } + } + + return err; +} + +static int msa_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned int wr_size = NUM_FPU_REGS * regset->size; + const struct msa_control_regs ctrl_regs = { + .fir = boot_cpu_data.fpu_id, + .fcsr = target->thread.fpu.fcr31, + .msair = boot_cpu_data.msa_id, + .msacsr = target->thread.fpu.msacsr, + }; + int err; + + if (!tsk_used_math(target)) { + /* The task hasn't used FP or MSA, fill with 0xff */ + err = copy_pad_fprs(target, regset, &pos, &count, + &kbuf, &ubuf, 0); + } else if (!test_tsk_thread_flag(target, TIF_MSA_CTX_LIVE)) { + /* Copy scalar FP context, fill the rest with 0xff */ + err = copy_pad_fprs(target, regset, &pos, &count, + &kbuf, &ubuf, 8); + } else if (sizeof(target->thread.fpu.fpr[0]) == regset->size) { + /* Trivially copy the vector registers */ + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fpr, + 0, wr_size); + } else { + /* Copy as much context as possible, fill the rest with 0xff */ + err = copy_pad_fprs(target, regset, &pos, &count, + &kbuf, &ubuf, + sizeof(target->thread.fpu.fpr[0])); + } + + err |= user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &ctrl_regs, wr_size, + wr_size + sizeof(ctrl_regs)); + return err; +} + +static int msa_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + const unsigned int wr_size = NUM_FPU_REGS * regset->size; + struct msa_control_regs ctrl_regs; + unsigned int cp_sz; + int i, err, start; + + init_fp_ctx(target); + + if (sizeof(target->thread.fpu.fpr[0]) == regset->size) { + /* Trivially copy the vector registers */ + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fpr, + 0, wr_size); + } else { + /* Copy as much context as possible */ + cp_sz = min_t(unsigned int, regset->size, + sizeof(target->thread.fpu.fpr[0])); + + i = start = err = 0; + for (; i < NUM_FPU_REGS; i++, start += regset->size) { + err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fpr[i], + start, start + cp_sz); + } + } + + if (!err) + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl_regs, + wr_size, wr_size + sizeof(ctrl_regs)); + if (!err) { + target->thread.fpu.fcr31 = ctrl_regs.fcsr & ~FPU_CSR_ALL_X; + target->thread.fpu.msacsr = ctrl_regs.msacsr & ~MSA_CSR_CAUSEF; + } + + return err; +} + +#endif /* CONFIG_CPU_HAS_MSA */ + #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) /* @@ -759,57 +915,16 @@ static int dsp_active(struct task_struct *target, return cpu_has_dsp ? NUM_DSP_REGS + 1 : -ENODEV; } -/* Copy the FP mode setting to the supplied NT_MIPS_FP_MODE buffer. */ -static int fp_mode_get(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) -{ - int fp_mode; - - fp_mode = mips_get_process_fp_mode(target); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, - sizeof(fp_mode)); -} - -/* - * Copy the supplied NT_MIPS_FP_MODE buffer to the FP mode setting. - * - * We optimize for the case where `count % sizeof(int) == 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 - * mode writes. - */ -static int fp_mode_set(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - const void *kbuf, const void __user *ubuf) -{ - int fp_mode; - int err; - - BUG_ON(count % sizeof(int)); - - if (pos + count > sizeof(fp_mode)) - return -EIO; - - err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, - sizeof(fp_mode)); - if (err) - return err; - - if (count > 0) - err = mips_set_process_fp_mode(target, fp_mode); - - return err; -} - enum mips_regset { REGSET_GPR, - REGSET_FPR, REGSET_DSP, +#ifdef CONFIG_MIPS_FP_SUPPORT + REGSET_FPR, REGSET_FP_MODE, +#endif +#ifdef CONFIG_CPU_HAS_MSA + REGSET_MSA, +#endif }; struct pt_regs_offset { @@ -907,14 +1022,6 @@ static const struct user_regset mips_regsets[] = { .get = gpr32_get, .set = gpr32_set, }, - [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, - .n = ELF_NFPREG, - .size = sizeof(elf_fpreg_t), - .align = sizeof(elf_fpreg_t), - .get = fpr_get, - .set = fpr_set, - }, [REGSET_DSP] = { .core_note_type = NT_MIPS_DSP, .n = NUM_DSP_REGS + 1, @@ -924,6 +1031,15 @@ static const struct user_regset mips_regsets[] = { .set = dsp32_set, .active = dsp_active, }, +#ifdef CONFIG_MIPS_FP_SUPPORT + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, + .n = ELF_NFPREG, + .size = sizeof(elf_fpreg_t), + .align = sizeof(elf_fpreg_t), + .get = fpr_get, + .set = fpr_set, + }, [REGSET_FP_MODE] = { .core_note_type = NT_MIPS_FP_MODE, .n = 1, @@ -932,6 +1048,17 @@ static const struct user_regset mips_regsets[] = { .get = fp_mode_get, .set = fp_mode_set, }, +#endif +#ifdef CONFIG_CPU_HAS_MSA + [REGSET_MSA] = { + .core_note_type = NT_MIPS_MSA, + .n = NUM_FPU_REGS + 1, + .size = 16, + .align = 16, + .get = msa_get, + .set = msa_set, + }, +#endif }; static const struct user_regset_view user_mips_view = { @@ -955,14 +1082,6 @@ static const struct user_regset mips64_regsets[] = { .get = gpr64_get, .set = gpr64_set, }, - [REGSET_FPR] = { - .core_note_type = NT_PRFPREG, - .n = ELF_NFPREG, - .size = sizeof(elf_fpreg_t), - .align = sizeof(elf_fpreg_t), - .get = fpr_get, - .set = fpr_set, - }, [REGSET_DSP] = { .core_note_type = NT_MIPS_DSP, .n = NUM_DSP_REGS + 1, @@ -972,6 +1091,7 @@ static const struct user_regset mips64_regsets[] = { .set = dsp64_set, .active = dsp_active, }, +#ifdef CONFIG_MIPS_FP_SUPPORT [REGSET_FP_MODE] = { .core_note_type = NT_MIPS_FP_MODE, .n = 1, @@ -980,6 +1100,25 @@ static const struct user_regset mips64_regsets[] = { .get = fp_mode_get, .set = fp_mode_set, }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, + .n = ELF_NFPREG, + .size = sizeof(elf_fpreg_t), + .align = sizeof(elf_fpreg_t), + .get = fpr_get, + .set = fpr_set, + }, +#endif +#ifdef CONFIG_CPU_HAS_MSA + [REGSET_MSA] = { + .core_note_type = NT_MIPS_MSA, + .n = NUM_FPU_REGS + 1, + .size = 16, + .align = 16, + .get = msa_get, + .set = msa_set, + }, +#endif }; static const struct user_regset_view user_mips64_view = { @@ -1040,7 +1179,6 @@ long arch_ptrace(struct task_struct *child, long request, /* Read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { struct pt_regs *regs; - union fpureg *fregs; unsigned long tmp = 0; regs = task_pt_regs(child); @@ -1050,7 +1188,10 @@ long arch_ptrace(struct task_struct *child, long request, case 0 ... 31: tmp = regs->regs[addr]; break; - case FPR_BASE ... FPR_BASE + 31: +#ifdef CONFIG_MIPS_FP_SUPPORT + case FPR_BASE ... FPR_BASE + 31: { + union fpureg *fregs; + if (!tsk_used_math(child)) { /* FP not yet used */ tmp = -1; @@ -1072,6 +1213,15 @@ long arch_ptrace(struct task_struct *child, long request, #endif tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; + } + case FPC_CSR: + tmp = child->thread.fpu.fcr31; + break; + case FPC_EIR: + /* implementation / version register */ + tmp = boot_cpu_data.fpu_id; + break; +#endif case PC: tmp = regs->cp0_epc; break; @@ -1092,13 +1242,6 @@ long arch_ptrace(struct task_struct *child, long request, tmp = regs->acx; break; #endif - case FPC_CSR: - tmp = child->thread.fpu.fcr31; - break; - case FPC_EIR: - /* implementation / version register */ - tmp = boot_cpu_data.fpu_id; - break; case DSP_BASE ... DSP_BASE + 5: { dspreg_t *dregs; @@ -1149,6 +1292,7 @@ long arch_ptrace(struct task_struct *child, long request, mips_syscall_is_indirect(child, regs)) mips_syscall_update_nr(child, regs); break; +#ifdef CONFIG_MIPS_FP_SUPPORT case FPR_BASE ... FPR_BASE + 31: { union fpureg *fregs = get_fpu_regs(child); @@ -1168,6 +1312,11 @@ long arch_ptrace(struct task_struct *child, long request, set_fpr64(&fregs[addr - FPR_BASE], 0, data); break; } + case FPC_CSR: + init_fp_ctx(child); + ptrace_setfcr31(child, data); + break; +#endif case PC: regs->cp0_epc = data; break; @@ -1182,10 +1331,6 @@ long arch_ptrace(struct task_struct *child, long request, regs->acx = data; break; #endif - case FPC_CSR: - init_fp_ctx(child); - ptrace_setfcr31(child, data); - break; case DSP_BASE ... DSP_BASE + 5: { dspreg_t *dregs; @@ -1221,6 +1366,7 @@ long arch_ptrace(struct task_struct *child, long request, ret = ptrace_setregs(child, datavp); break; +#ifdef CONFIG_MIPS_FP_SUPPORT case PTRACE_GETFPREGS: ret = ptrace_getfpregs(child, datavp); break; @@ -1228,7 +1374,7 @@ long arch_ptrace(struct task_struct *child, long request, case PTRACE_SETFPREGS: ret = ptrace_setfpregs(child, datavp); break; - +#endif case PTRACE_GET_THREAD_AREA: ret = put_user(task_thread_info(child)->tp_value, datalp); break; |