1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 3 4 #include <linux/ptrace.h> 5 #include <linux/uaccess.h> 6 #include <abi/reg_ops.h> 7 8 #define MTCR_MASK 0xFC00FFE0 9 #define MFCR_MASK 0xFC00FFE0 10 #define MTCR_DIST 0xC0006420 11 #define MFCR_DIST 0xC0006020 12 13 void __init init_fpu(void) 14 { 15 mtcr("cr<1, 2>", 0); 16 } 17 18 /* 19 * fpu_libc_helper() is to help libc to excute: 20 * - mfcr %a, cr<1, 2> 21 * - mfcr %a, cr<2, 2> 22 * - mtcr %a, cr<1, 2> 23 * - mtcr %a, cr<2, 2> 24 */ 25 int fpu_libc_helper(struct pt_regs *regs) 26 { 27 int fault; 28 unsigned long instrptr, regx = 0; 29 unsigned long index = 0, tmp = 0; 30 unsigned long tinstr = 0; 31 u16 instr_hi, instr_low; 32 33 instrptr = instruction_pointer(regs); 34 if (instrptr & 1) 35 return 0; 36 37 fault = __get_user(instr_low, (u16 *)instrptr); 38 if (fault) 39 return 0; 40 41 fault = __get_user(instr_hi, (u16 *)(instrptr + 2)); 42 if (fault) 43 return 0; 44 45 tinstr = instr_hi | ((unsigned long)instr_low << 16); 46 47 if (((tinstr >> 21) & 0x1F) != 2) 48 return 0; 49 50 if ((tinstr & MTCR_MASK) == MTCR_DIST) { 51 index = (tinstr >> 16) & 0x1F; 52 if (index > 13) 53 return 0; 54 55 tmp = tinstr & 0x1F; 56 if (tmp > 2) 57 return 0; 58 59 regx = *(®s->a0 + index); 60 61 if (tmp == 1) 62 mtcr("cr<1, 2>", regx); 63 else if (tmp == 2) 64 mtcr("cr<2, 2>", regx); 65 else 66 return 0; 67 68 regs->pc += 4; 69 return 1; 70 } 71 72 if ((tinstr & MFCR_MASK) == MFCR_DIST) { 73 index = tinstr & 0x1F; 74 if (index > 13) 75 return 0; 76 77 tmp = ((tinstr >> 16) & 0x1F); 78 if (tmp > 2) 79 return 0; 80 81 if (tmp == 1) 82 regx = mfcr("cr<1, 2>"); 83 else if (tmp == 2) 84 regx = mfcr("cr<2, 2>"); 85 else 86 return 0; 87 88 *(®s->a0 + index) = regx; 89 90 regs->pc += 4; 91 return 1; 92 } 93 94 return 0; 95 } 96 97 void fpu_fpe(struct pt_regs *regs) 98 { 99 int sig, code; 100 unsigned int fesr; 101 102 fesr = mfcr("cr<2, 2>"); 103 104 sig = SIGFPE; 105 code = FPE_FLTUNK; 106 107 if (fesr & FPE_ILLE) { 108 sig = SIGILL; 109 code = ILL_ILLOPC; 110 } else if (fesr & FPE_IDC) { 111 sig = SIGILL; 112 code = ILL_ILLOPN; 113 } else if (fesr & FPE_FEC) { 114 sig = SIGFPE; 115 if (fesr & FPE_IOC) 116 code = FPE_FLTINV; 117 else if (fesr & FPE_DZC) 118 code = FPE_FLTDIV; 119 else if (fesr & FPE_UFC) 120 code = FPE_FLTUND; 121 else if (fesr & FPE_OFC) 122 code = FPE_FLTOVF; 123 else if (fesr & FPE_IXC) 124 code = FPE_FLTRES; 125 } 126 127 force_sig_fault(sig, code, (void __user *)regs->pc, current); 128 } 129 130 #define FMFVR_FPU_REGS(vrx, vry) \ 131 "fmfvrl %0, "#vrx"\n" \ 132 "fmfvrh %1, "#vrx"\n" \ 133 "fmfvrl %2, "#vry"\n" \ 134 "fmfvrh %3, "#vry"\n" 135 136 #define FMTVR_FPU_REGS(vrx, vry) \ 137 "fmtvrl "#vrx", %0\n" \ 138 "fmtvrh "#vrx", %1\n" \ 139 "fmtvrl "#vry", %2\n" \ 140 "fmtvrh "#vry", %3\n" 141 142 #define STW_FPU_REGS(a, b, c, d) \ 143 "stw %0, (%4, "#a")\n" \ 144 "stw %1, (%4, "#b")\n" \ 145 "stw %2, (%4, "#c")\n" \ 146 "stw %3, (%4, "#d")\n" 147 148 #define LDW_FPU_REGS(a, b, c, d) \ 149 "ldw %0, (%4, "#a")\n" \ 150 "ldw %1, (%4, "#b")\n" \ 151 "ldw %2, (%4, "#c")\n" \ 152 "ldw %3, (%4, "#d")\n" 153 154 void save_to_user_fp(struct user_fp *user_fp) 155 { 156 unsigned long flg; 157 unsigned long tmp1, tmp2; 158 unsigned long *fpregs; 159 160 local_irq_save(flg); 161 162 tmp1 = mfcr("cr<1, 2>"); 163 tmp2 = mfcr("cr<2, 2>"); 164 165 user_fp->fcr = tmp1; 166 user_fp->fesr = tmp2; 167 168 fpregs = &user_fp->vr[0]; 169 #ifdef CONFIG_CPU_HAS_FPUV2 170 #ifdef CONFIG_CPU_HAS_VDSP 171 asm volatile( 172 "vstmu.32 vr0-vr3, (%0)\n" 173 "vstmu.32 vr4-vr7, (%0)\n" 174 "vstmu.32 vr8-vr11, (%0)\n" 175 "vstmu.32 vr12-vr15, (%0)\n" 176 "fstmu.64 vr16-vr31, (%0)\n" 177 : "+a"(fpregs) 178 ::"memory"); 179 #else 180 asm volatile( 181 "fstmu.64 vr0-vr31, (%0)\n" 182 : "+a"(fpregs) 183 ::"memory"); 184 #endif 185 #else 186 { 187 unsigned long tmp3, tmp4; 188 189 asm volatile( 190 FMFVR_FPU_REGS(vr0, vr1) 191 STW_FPU_REGS(0, 4, 16, 20) 192 FMFVR_FPU_REGS(vr2, vr3) 193 STW_FPU_REGS(32, 36, 48, 52) 194 FMFVR_FPU_REGS(vr4, vr5) 195 STW_FPU_REGS(64, 68, 80, 84) 196 FMFVR_FPU_REGS(vr6, vr7) 197 STW_FPU_REGS(96, 100, 112, 116) 198 "addi %4, 128\n" 199 FMFVR_FPU_REGS(vr8, vr9) 200 STW_FPU_REGS(0, 4, 16, 20) 201 FMFVR_FPU_REGS(vr10, vr11) 202 STW_FPU_REGS(32, 36, 48, 52) 203 FMFVR_FPU_REGS(vr12, vr13) 204 STW_FPU_REGS(64, 68, 80, 84) 205 FMFVR_FPU_REGS(vr14, vr15) 206 STW_FPU_REGS(96, 100, 112, 116) 207 : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), 208 "=a"(tmp4), "+a"(fpregs) 209 ::"memory"); 210 } 211 #endif 212 213 local_irq_restore(flg); 214 } 215 216 void restore_from_user_fp(struct user_fp *user_fp) 217 { 218 unsigned long flg; 219 unsigned long tmp1, tmp2; 220 unsigned long *fpregs; 221 222 local_irq_save(flg); 223 224 tmp1 = user_fp->fcr; 225 tmp2 = user_fp->fesr; 226 227 mtcr("cr<1, 2>", tmp1); 228 mtcr("cr<2, 2>", tmp2); 229 230 fpregs = &user_fp->vr[0]; 231 #ifdef CONFIG_CPU_HAS_FPUV2 232 #ifdef CONFIG_CPU_HAS_VDSP 233 asm volatile( 234 "vldmu.32 vr0-vr3, (%0)\n" 235 "vldmu.32 vr4-vr7, (%0)\n" 236 "vldmu.32 vr8-vr11, (%0)\n" 237 "vldmu.32 vr12-vr15, (%0)\n" 238 "fldmu.64 vr16-vr31, (%0)\n" 239 : "+a"(fpregs) 240 ::"memory"); 241 #else 242 asm volatile( 243 "fldmu.64 vr0-vr31, (%0)\n" 244 : "+a"(fpregs) 245 ::"memory"); 246 #endif 247 #else 248 { 249 unsigned long tmp3, tmp4; 250 251 asm volatile( 252 LDW_FPU_REGS(0, 4, 16, 20) 253 FMTVR_FPU_REGS(vr0, vr1) 254 LDW_FPU_REGS(32, 36, 48, 52) 255 FMTVR_FPU_REGS(vr2, vr3) 256 LDW_FPU_REGS(64, 68, 80, 84) 257 FMTVR_FPU_REGS(vr4, vr5) 258 LDW_FPU_REGS(96, 100, 112, 116) 259 FMTVR_FPU_REGS(vr6, vr7) 260 "addi %4, 128\n" 261 LDW_FPU_REGS(0, 4, 16, 20) 262 FMTVR_FPU_REGS(vr8, vr9) 263 LDW_FPU_REGS(32, 36, 48, 52) 264 FMTVR_FPU_REGS(vr10, vr11) 265 LDW_FPU_REGS(64, 68, 80, 84) 266 FMTVR_FPU_REGS(vr12, vr13) 267 LDW_FPU_REGS(96, 100, 112, 116) 268 FMTVR_FPU_REGS(vr14, vr15) 269 : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), 270 "=a"(tmp4), "+a"(fpregs) 271 ::"memory"); 272 } 273 #endif 274 local_irq_restore(flg); 275 } 276