// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. #include <linux/ptrace.h> #include <linux/uaccess.h> #include <abi/reg_ops.h> #define MTCR_MASK 0xFC00FFE0 #define MFCR_MASK 0xFC00FFE0 #define MTCR_DIST 0xC0006420 #define MFCR_DIST 0xC0006020 /* * fpu_libc_helper() is to help libc to excute: * - mfcr %a, cr<1, 2> * - mfcr %a, cr<2, 2> * - mtcr %a, cr<1, 2> * - mtcr %a, cr<2, 2> */ int fpu_libc_helper(struct pt_regs *regs) { int fault; unsigned long instrptr, regx = 0; unsigned long index = 0, tmp = 0; unsigned long tinstr = 0; u16 instr_hi, instr_low; instrptr = instruction_pointer(regs); if (instrptr & 1) return 0; fault = __get_user(instr_low, (u16 *)instrptr); if (fault) return 0; fault = __get_user(instr_hi, (u16 *)(instrptr + 2)); if (fault) return 0; tinstr = instr_hi | ((unsigned long)instr_low << 16); if (((tinstr >> 21) & 0x1F) != 2) return 0; if ((tinstr & MTCR_MASK) == MTCR_DIST) { index = (tinstr >> 16) & 0x1F; if (index > 13) return 0; tmp = tinstr & 0x1F; if (tmp > 2) return 0; regx = *(®s->a0 + index); if (tmp == 1) mtcr("cr<1, 2>", regx); else if (tmp == 2) mtcr("cr<2, 2>", regx); else return 0; regs->pc += 4; return 1; } if ((tinstr & MFCR_MASK) == MFCR_DIST) { index = tinstr & 0x1F; if (index > 13) return 0; tmp = ((tinstr >> 16) & 0x1F); if (tmp > 2) return 0; if (tmp == 1) regx = mfcr("cr<1, 2>"); else if (tmp == 2) regx = mfcr("cr<2, 2>"); else return 0; *(®s->a0 + index) = regx; regs->pc += 4; return 1; } return 0; } void fpu_fpe(struct pt_regs *regs) { int sig, code; unsigned int fesr; fesr = mfcr("cr<2, 2>"); sig = SIGFPE; code = FPE_FLTUNK; if (fesr & FPE_ILLE) { sig = SIGILL; code = ILL_ILLOPC; } else if (fesr & FPE_IDC) { sig = SIGILL; code = ILL_ILLOPN; } else if (fesr & FPE_FEC) { sig = SIGFPE; if (fesr & FPE_IOC) code = FPE_FLTINV; else if (fesr & FPE_DZC) code = FPE_FLTDIV; else if (fesr & FPE_UFC) code = FPE_FLTUND; else if (fesr & FPE_OFC) code = FPE_FLTOVF; else if (fesr & FPE_IXC) code = FPE_FLTRES; } force_sig_fault(sig, code, (void __user *)regs->pc); } #define FMFVR_FPU_REGS(vrx, vry) \ "fmfvrl %0, "#vrx"\n" \ "fmfvrh %1, "#vrx"\n" \ "fmfvrl %2, "#vry"\n" \ "fmfvrh %3, "#vry"\n" #define FMTVR_FPU_REGS(vrx, vry) \ "fmtvrl "#vrx", %0\n" \ "fmtvrh "#vrx", %1\n" \ "fmtvrl "#vry", %2\n" \ "fmtvrh "#vry", %3\n" #define STW_FPU_REGS(a, b, c, d) \ "stw %0, (%4, "#a")\n" \ "stw %1, (%4, "#b")\n" \ "stw %2, (%4, "#c")\n" \ "stw %3, (%4, "#d")\n" #define LDW_FPU_REGS(a, b, c, d) \ "ldw %0, (%4, "#a")\n" \ "ldw %1, (%4, "#b")\n" \ "ldw %2, (%4, "#c")\n" \ "ldw %3, (%4, "#d")\n" void save_to_user_fp(struct user_fp *user_fp) { unsigned long flg; unsigned long tmp1, tmp2; unsigned long *fpregs; local_irq_save(flg); tmp1 = mfcr("cr<1, 2>"); tmp2 = mfcr("cr<2, 2>"); user_fp->fcr = tmp1; user_fp->fesr = tmp2; fpregs = &user_fp->vr[0]; #ifdef CONFIG_CPU_HAS_FPUV2 #ifdef CONFIG_CPU_HAS_VDSP asm volatile( "vstmu.32 vr0-vr3, (%0)\n" "vstmu.32 vr4-vr7, (%0)\n" "vstmu.32 vr8-vr11, (%0)\n" "vstmu.32 vr12-vr15, (%0)\n" "fstmu.64 vr16-vr31, (%0)\n" : "+a"(fpregs) ::"memory"); #else asm volatile( "fstmu.64 vr0-vr31, (%0)\n" : "+a"(fpregs) ::"memory"); #endif #else { unsigned long tmp3, tmp4; asm volatile( FMFVR_FPU_REGS(vr0, vr1) STW_FPU_REGS(0, 4, 16, 20) FMFVR_FPU_REGS(vr2, vr3) STW_FPU_REGS(32, 36, 48, 52) FMFVR_FPU_REGS(vr4, vr5) STW_FPU_REGS(64, 68, 80, 84) FMFVR_FPU_REGS(vr6, vr7) STW_FPU_REGS(96, 100, 112, 116) "addi %4, 128\n" FMFVR_FPU_REGS(vr8, vr9) STW_FPU_REGS(0, 4, 16, 20) FMFVR_FPU_REGS(vr10, vr11) STW_FPU_REGS(32, 36, 48, 52) FMFVR_FPU_REGS(vr12, vr13) STW_FPU_REGS(64, 68, 80, 84) FMFVR_FPU_REGS(vr14, vr15) STW_FPU_REGS(96, 100, 112, 116) : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), "=a"(tmp4), "+a"(fpregs) ::"memory"); } #endif local_irq_restore(flg); } void restore_from_user_fp(struct user_fp *user_fp) { unsigned long flg; unsigned long tmp1, tmp2; unsigned long *fpregs; local_irq_save(flg); tmp1 = user_fp->fcr; tmp2 = user_fp->fesr; mtcr("cr<1, 2>", tmp1); mtcr("cr<2, 2>", tmp2); fpregs = &user_fp->vr[0]; #ifdef CONFIG_CPU_HAS_FPUV2 #ifdef CONFIG_CPU_HAS_VDSP asm volatile( "vldmu.32 vr0-vr3, (%0)\n" "vldmu.32 vr4-vr7, (%0)\n" "vldmu.32 vr8-vr11, (%0)\n" "vldmu.32 vr12-vr15, (%0)\n" "fldmu.64 vr16-vr31, (%0)\n" : "+a"(fpregs) ::"memory"); #else asm volatile( "fldmu.64 vr0-vr31, (%0)\n" : "+a"(fpregs) ::"memory"); #endif #else { unsigned long tmp3, tmp4; asm volatile( LDW_FPU_REGS(0, 4, 16, 20) FMTVR_FPU_REGS(vr0, vr1) LDW_FPU_REGS(32, 36, 48, 52) FMTVR_FPU_REGS(vr2, vr3) LDW_FPU_REGS(64, 68, 80, 84) FMTVR_FPU_REGS(vr4, vr5) LDW_FPU_REGS(96, 100, 112, 116) FMTVR_FPU_REGS(vr6, vr7) "addi %4, 128\n" LDW_FPU_REGS(0, 4, 16, 20) FMTVR_FPU_REGS(vr8, vr9) LDW_FPU_REGS(32, 36, 48, 52) FMTVR_FPU_REGS(vr10, vr11) LDW_FPU_REGS(64, 68, 80, 84) FMTVR_FPU_REGS(vr12, vr13) LDW_FPU_REGS(96, 100, 112, 116) FMTVR_FPU_REGS(vr14, vr15) : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), "=a"(tmp4), "+a"(fpregs) ::"memory"); } #endif local_irq_restore(flg); }