18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 58c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 68c2ecf20Sopenharmony_ci#include <abi/reg_ops.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define MTCR_MASK 0xFC00FFE0 98c2ecf20Sopenharmony_ci#define MFCR_MASK 0xFC00FFE0 108c2ecf20Sopenharmony_ci#define MTCR_DIST 0xC0006420 118c2ecf20Sopenharmony_ci#define MFCR_DIST 0xC0006020 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * fpu_libc_helper() is to help libc to excute: 158c2ecf20Sopenharmony_ci * - mfcr %a, cr<1, 2> 168c2ecf20Sopenharmony_ci * - mfcr %a, cr<2, 2> 178c2ecf20Sopenharmony_ci * - mtcr %a, cr<1, 2> 188c2ecf20Sopenharmony_ci * - mtcr %a, cr<2, 2> 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ciint fpu_libc_helper(struct pt_regs *regs) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci int fault; 238c2ecf20Sopenharmony_ci unsigned long instrptr, regx = 0; 248c2ecf20Sopenharmony_ci unsigned long index = 0, tmp = 0; 258c2ecf20Sopenharmony_ci unsigned long tinstr = 0; 268c2ecf20Sopenharmony_ci u16 instr_hi, instr_low; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci instrptr = instruction_pointer(regs); 298c2ecf20Sopenharmony_ci if (instrptr & 1) 308c2ecf20Sopenharmony_ci return 0; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci fault = __get_user(instr_low, (u16 *)instrptr); 338c2ecf20Sopenharmony_ci if (fault) 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci fault = __get_user(instr_hi, (u16 *)(instrptr + 2)); 378c2ecf20Sopenharmony_ci if (fault) 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci tinstr = instr_hi | ((unsigned long)instr_low << 16); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (((tinstr >> 21) & 0x1F) != 2) 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if ((tinstr & MTCR_MASK) == MTCR_DIST) { 468c2ecf20Sopenharmony_ci index = (tinstr >> 16) & 0x1F; 478c2ecf20Sopenharmony_ci if (index > 13) 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci tmp = tinstr & 0x1F; 518c2ecf20Sopenharmony_ci if (tmp > 2) 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci regx = *(®s->a0 + index); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (tmp == 1) 578c2ecf20Sopenharmony_ci mtcr("cr<1, 2>", regx); 588c2ecf20Sopenharmony_ci else if (tmp == 2) 598c2ecf20Sopenharmony_ci mtcr("cr<2, 2>", regx); 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci regs->pc += 4; 648c2ecf20Sopenharmony_ci return 1; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if ((tinstr & MFCR_MASK) == MFCR_DIST) { 688c2ecf20Sopenharmony_ci index = tinstr & 0x1F; 698c2ecf20Sopenharmony_ci if (index > 13) 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci tmp = ((tinstr >> 16) & 0x1F); 738c2ecf20Sopenharmony_ci if (tmp > 2) 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (tmp == 1) 778c2ecf20Sopenharmony_ci regx = mfcr("cr<1, 2>"); 788c2ecf20Sopenharmony_ci else if (tmp == 2) 798c2ecf20Sopenharmony_ci regx = mfcr("cr<2, 2>"); 808c2ecf20Sopenharmony_ci else 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci *(®s->a0 + index) = regx; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci regs->pc += 4; 868c2ecf20Sopenharmony_ci return 1; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_civoid fpu_fpe(struct pt_regs *regs) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int sig, code; 958c2ecf20Sopenharmony_ci unsigned int fesr; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci fesr = mfcr("cr<2, 2>"); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci sig = SIGFPE; 1008c2ecf20Sopenharmony_ci code = FPE_FLTUNK; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (fesr & FPE_ILLE) { 1038c2ecf20Sopenharmony_ci sig = SIGILL; 1048c2ecf20Sopenharmony_ci code = ILL_ILLOPC; 1058c2ecf20Sopenharmony_ci } else if (fesr & FPE_IDC) { 1068c2ecf20Sopenharmony_ci sig = SIGILL; 1078c2ecf20Sopenharmony_ci code = ILL_ILLOPN; 1088c2ecf20Sopenharmony_ci } else if (fesr & FPE_FEC) { 1098c2ecf20Sopenharmony_ci sig = SIGFPE; 1108c2ecf20Sopenharmony_ci if (fesr & FPE_IOC) 1118c2ecf20Sopenharmony_ci code = FPE_FLTINV; 1128c2ecf20Sopenharmony_ci else if (fesr & FPE_DZC) 1138c2ecf20Sopenharmony_ci code = FPE_FLTDIV; 1148c2ecf20Sopenharmony_ci else if (fesr & FPE_UFC) 1158c2ecf20Sopenharmony_ci code = FPE_FLTUND; 1168c2ecf20Sopenharmony_ci else if (fesr & FPE_OFC) 1178c2ecf20Sopenharmony_ci code = FPE_FLTOVF; 1188c2ecf20Sopenharmony_ci else if (fesr & FPE_IXC) 1198c2ecf20Sopenharmony_ci code = FPE_FLTRES; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci force_sig_fault(sig, code, (void __user *)regs->pc); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define FMFVR_FPU_REGS(vrx, vry) \ 1268c2ecf20Sopenharmony_ci "fmfvrl %0, "#vrx"\n" \ 1278c2ecf20Sopenharmony_ci "fmfvrh %1, "#vrx"\n" \ 1288c2ecf20Sopenharmony_ci "fmfvrl %2, "#vry"\n" \ 1298c2ecf20Sopenharmony_ci "fmfvrh %3, "#vry"\n" 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define FMTVR_FPU_REGS(vrx, vry) \ 1328c2ecf20Sopenharmony_ci "fmtvrl "#vrx", %0\n" \ 1338c2ecf20Sopenharmony_ci "fmtvrh "#vrx", %1\n" \ 1348c2ecf20Sopenharmony_ci "fmtvrl "#vry", %2\n" \ 1358c2ecf20Sopenharmony_ci "fmtvrh "#vry", %3\n" 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define STW_FPU_REGS(a, b, c, d) \ 1388c2ecf20Sopenharmony_ci "stw %0, (%4, "#a")\n" \ 1398c2ecf20Sopenharmony_ci "stw %1, (%4, "#b")\n" \ 1408c2ecf20Sopenharmony_ci "stw %2, (%4, "#c")\n" \ 1418c2ecf20Sopenharmony_ci "stw %3, (%4, "#d")\n" 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define LDW_FPU_REGS(a, b, c, d) \ 1448c2ecf20Sopenharmony_ci "ldw %0, (%4, "#a")\n" \ 1458c2ecf20Sopenharmony_ci "ldw %1, (%4, "#b")\n" \ 1468c2ecf20Sopenharmony_ci "ldw %2, (%4, "#c")\n" \ 1478c2ecf20Sopenharmony_ci "ldw %3, (%4, "#d")\n" 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_civoid save_to_user_fp(struct user_fp *user_fp) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci unsigned long flg; 1528c2ecf20Sopenharmony_ci unsigned long tmp1, tmp2; 1538c2ecf20Sopenharmony_ci unsigned long *fpregs; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci local_irq_save(flg); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci tmp1 = mfcr("cr<1, 2>"); 1588c2ecf20Sopenharmony_ci tmp2 = mfcr("cr<2, 2>"); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci user_fp->fcr = tmp1; 1618c2ecf20Sopenharmony_ci user_fp->fesr = tmp2; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci fpregs = &user_fp->vr[0]; 1648c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_FPUV2 1658c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_VDSP 1668c2ecf20Sopenharmony_ci asm volatile( 1678c2ecf20Sopenharmony_ci "vstmu.32 vr0-vr3, (%0)\n" 1688c2ecf20Sopenharmony_ci "vstmu.32 vr4-vr7, (%0)\n" 1698c2ecf20Sopenharmony_ci "vstmu.32 vr8-vr11, (%0)\n" 1708c2ecf20Sopenharmony_ci "vstmu.32 vr12-vr15, (%0)\n" 1718c2ecf20Sopenharmony_ci "fstmu.64 vr16-vr31, (%0)\n" 1728c2ecf20Sopenharmony_ci : "+a"(fpregs) 1738c2ecf20Sopenharmony_ci ::"memory"); 1748c2ecf20Sopenharmony_ci#else 1758c2ecf20Sopenharmony_ci asm volatile( 1768c2ecf20Sopenharmony_ci "fstmu.64 vr0-vr31, (%0)\n" 1778c2ecf20Sopenharmony_ci : "+a"(fpregs) 1788c2ecf20Sopenharmony_ci ::"memory"); 1798c2ecf20Sopenharmony_ci#endif 1808c2ecf20Sopenharmony_ci#else 1818c2ecf20Sopenharmony_ci { 1828c2ecf20Sopenharmony_ci unsigned long tmp3, tmp4; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci asm volatile( 1858c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr0, vr1) 1868c2ecf20Sopenharmony_ci STW_FPU_REGS(0, 4, 16, 20) 1878c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr2, vr3) 1888c2ecf20Sopenharmony_ci STW_FPU_REGS(32, 36, 48, 52) 1898c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr4, vr5) 1908c2ecf20Sopenharmony_ci STW_FPU_REGS(64, 68, 80, 84) 1918c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr6, vr7) 1928c2ecf20Sopenharmony_ci STW_FPU_REGS(96, 100, 112, 116) 1938c2ecf20Sopenharmony_ci "addi %4, 128\n" 1948c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr8, vr9) 1958c2ecf20Sopenharmony_ci STW_FPU_REGS(0, 4, 16, 20) 1968c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr10, vr11) 1978c2ecf20Sopenharmony_ci STW_FPU_REGS(32, 36, 48, 52) 1988c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr12, vr13) 1998c2ecf20Sopenharmony_ci STW_FPU_REGS(64, 68, 80, 84) 2008c2ecf20Sopenharmony_ci FMFVR_FPU_REGS(vr14, vr15) 2018c2ecf20Sopenharmony_ci STW_FPU_REGS(96, 100, 112, 116) 2028c2ecf20Sopenharmony_ci : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), 2038c2ecf20Sopenharmony_ci "=a"(tmp4), "+a"(fpregs) 2048c2ecf20Sopenharmony_ci ::"memory"); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci#endif 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci local_irq_restore(flg); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_civoid restore_from_user_fp(struct user_fp *user_fp) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci unsigned long flg; 2148c2ecf20Sopenharmony_ci unsigned long tmp1, tmp2; 2158c2ecf20Sopenharmony_ci unsigned long *fpregs; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci local_irq_save(flg); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci tmp1 = user_fp->fcr; 2208c2ecf20Sopenharmony_ci tmp2 = user_fp->fesr; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mtcr("cr<1, 2>", tmp1); 2238c2ecf20Sopenharmony_ci mtcr("cr<2, 2>", tmp2); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci fpregs = &user_fp->vr[0]; 2268c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_FPUV2 2278c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_VDSP 2288c2ecf20Sopenharmony_ci asm volatile( 2298c2ecf20Sopenharmony_ci "vldmu.32 vr0-vr3, (%0)\n" 2308c2ecf20Sopenharmony_ci "vldmu.32 vr4-vr7, (%0)\n" 2318c2ecf20Sopenharmony_ci "vldmu.32 vr8-vr11, (%0)\n" 2328c2ecf20Sopenharmony_ci "vldmu.32 vr12-vr15, (%0)\n" 2338c2ecf20Sopenharmony_ci "fldmu.64 vr16-vr31, (%0)\n" 2348c2ecf20Sopenharmony_ci : "+a"(fpregs) 2358c2ecf20Sopenharmony_ci ::"memory"); 2368c2ecf20Sopenharmony_ci#else 2378c2ecf20Sopenharmony_ci asm volatile( 2388c2ecf20Sopenharmony_ci "fldmu.64 vr0-vr31, (%0)\n" 2398c2ecf20Sopenharmony_ci : "+a"(fpregs) 2408c2ecf20Sopenharmony_ci ::"memory"); 2418c2ecf20Sopenharmony_ci#endif 2428c2ecf20Sopenharmony_ci#else 2438c2ecf20Sopenharmony_ci { 2448c2ecf20Sopenharmony_ci unsigned long tmp3, tmp4; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci asm volatile( 2478c2ecf20Sopenharmony_ci LDW_FPU_REGS(0, 4, 16, 20) 2488c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr0, vr1) 2498c2ecf20Sopenharmony_ci LDW_FPU_REGS(32, 36, 48, 52) 2508c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr2, vr3) 2518c2ecf20Sopenharmony_ci LDW_FPU_REGS(64, 68, 80, 84) 2528c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr4, vr5) 2538c2ecf20Sopenharmony_ci LDW_FPU_REGS(96, 100, 112, 116) 2548c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr6, vr7) 2558c2ecf20Sopenharmony_ci "addi %4, 128\n" 2568c2ecf20Sopenharmony_ci LDW_FPU_REGS(0, 4, 16, 20) 2578c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr8, vr9) 2588c2ecf20Sopenharmony_ci LDW_FPU_REGS(32, 36, 48, 52) 2598c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr10, vr11) 2608c2ecf20Sopenharmony_ci LDW_FPU_REGS(64, 68, 80, 84) 2618c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr12, vr13) 2628c2ecf20Sopenharmony_ci LDW_FPU_REGS(96, 100, 112, 116) 2638c2ecf20Sopenharmony_ci FMTVR_FPU_REGS(vr14, vr15) 2648c2ecf20Sopenharmony_ci : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), 2658c2ecf20Sopenharmony_ci "=a"(tmp4), "+a"(fpregs) 2668c2ecf20Sopenharmony_ci ::"memory"); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci#endif 2698c2ecf20Sopenharmony_ci local_irq_restore(flg); 2708c2ecf20Sopenharmony_ci} 271