1// SPDX-License-Identifier: GPL-2.0 2// Copyright (C) 2005-2018 Andes Technology Corporation 3 4#include <asm/bitfield.h> 5#include <asm/uaccess.h> 6#include <asm/sfp-machine.h> 7#include <asm/fpuemu.h> 8#include <asm/nds32_fpu_inst.h> 9 10#define DPFROMREG(dp, x) (dp = (void *)((unsigned long *)fpu_reg + 2*x)) 11#ifdef __NDS32_EL__ 12#define SPFROMREG(sp, x)\ 13 ((sp) = (void *)((unsigned long *)fpu_reg + (x^1))) 14#else 15#define SPFROMREG(sp, x) ((sp) = (void *)((unsigned long *)fpu_reg + x)) 16#endif 17 18#define DEF3OP(name, p, f1, f2) \ 19void fpemu_##name##p(void *ft, void *fa, void *fb) \ 20{ \ 21 f1(fa, fa, fb); \ 22 f2(ft, ft, fa); \ 23} 24 25#define DEF3OPNEG(name, p, f1, f2, f3) \ 26void fpemu_##name##p(void *ft, void *fa, void *fb) \ 27{ \ 28 f1(fa, fa, fb); \ 29 f2(ft, ft, fa); \ 30 f3(ft, ft); \ 31} 32DEF3OP(fmadd, s, fmuls, fadds); 33DEF3OP(fmsub, s, fmuls, fsubs); 34DEF3OP(fmadd, d, fmuld, faddd); 35DEF3OP(fmsub, d, fmuld, fsubd); 36DEF3OPNEG(fnmadd, s, fmuls, fadds, fnegs); 37DEF3OPNEG(fnmsub, s, fmuls, fsubs, fnegs); 38DEF3OPNEG(fnmadd, d, fmuld, faddd, fnegd); 39DEF3OPNEG(fnmsub, d, fmuld, fsubd, fnegd); 40 41static const unsigned char cmptab[8] = { 42 SF_CEQ, 43 SF_CEQ, 44 SF_CLT, 45 SF_CLT, 46 SF_CLT | SF_CEQ, 47 SF_CLT | SF_CEQ, 48 SF_CUN, 49 SF_CUN 50}; 51 52enum ARGTYPE { 53 S1S = 1, 54 S2S, 55 S1D, 56 CS, 57 D1D, 58 D2D, 59 D1S, 60 CD 61}; 62union func_t { 63 void (*t)(void *ft, void *fa, void *fb); 64 void (*b)(void *ft, void *fa); 65}; 66/* 67 * Emulate a single FPU arithmetic instruction. 68 */ 69static int fpu_emu(struct fpu_struct *fpu_reg, unsigned long insn) 70{ 71 int rfmt; /* resulting format */ 72 union func_t func; 73 int ftype = 0; 74 75 switch (rfmt = NDS32Insn_OPCODE_COP0(insn)) { 76 case fs1_op:{ 77 switch (NDS32Insn_OPCODE_BIT69(insn)) { 78 case fadds_op: 79 func.t = fadds; 80 ftype = S2S; 81 break; 82 case fsubs_op: 83 func.t = fsubs; 84 ftype = S2S; 85 break; 86 case fmadds_op: 87 func.t = fpemu_fmadds; 88 ftype = S2S; 89 break; 90 case fmsubs_op: 91 func.t = fpemu_fmsubs; 92 ftype = S2S; 93 break; 94 case fnmadds_op: 95 func.t = fpemu_fnmadds; 96 ftype = S2S; 97 break; 98 case fnmsubs_op: 99 func.t = fpemu_fnmsubs; 100 ftype = S2S; 101 break; 102 case fmuls_op: 103 func.t = fmuls; 104 ftype = S2S; 105 break; 106 case fdivs_op: 107 func.t = fdivs; 108 ftype = S2S; 109 break; 110 case fs1_f2op_op: 111 switch (NDS32Insn_OPCODE_BIT1014(insn)) { 112 case fs2d_op: 113 func.b = fs2d; 114 ftype = S1D; 115 break; 116 case fs2si_op: 117 func.b = fs2si; 118 ftype = S1S; 119 break; 120 case fs2si_z_op: 121 func.b = fs2si_z; 122 ftype = S1S; 123 break; 124 case fs2ui_op: 125 func.b = fs2ui; 126 ftype = S1S; 127 break; 128 case fs2ui_z_op: 129 func.b = fs2ui_z; 130 ftype = S1S; 131 break; 132 case fsi2s_op: 133 func.b = fsi2s; 134 ftype = S1S; 135 break; 136 case fui2s_op: 137 func.b = fui2s; 138 ftype = S1S; 139 break; 140 case fsqrts_op: 141 func.b = fsqrts; 142 ftype = S1S; 143 break; 144 default: 145 return SIGILL; 146 } 147 break; 148 default: 149 return SIGILL; 150 } 151 break; 152 } 153 case fs2_op: 154 switch (NDS32Insn_OPCODE_BIT69(insn)) { 155 case fcmpeqs_op: 156 case fcmpeqs_e_op: 157 case fcmplts_op: 158 case fcmplts_e_op: 159 case fcmples_op: 160 case fcmples_e_op: 161 case fcmpuns_op: 162 case fcmpuns_e_op: 163 ftype = CS; 164 break; 165 default: 166 return SIGILL; 167 } 168 break; 169 case fd1_op:{ 170 switch (NDS32Insn_OPCODE_BIT69(insn)) { 171 case faddd_op: 172 func.t = faddd; 173 ftype = D2D; 174 break; 175 case fsubd_op: 176 func.t = fsubd; 177 ftype = D2D; 178 break; 179 case fmaddd_op: 180 func.t = fpemu_fmaddd; 181 ftype = D2D; 182 break; 183 case fmsubd_op: 184 func.t = fpemu_fmsubd; 185 ftype = D2D; 186 break; 187 case fnmaddd_op: 188 func.t = fpemu_fnmaddd; 189 ftype = D2D; 190 break; 191 case fnmsubd_op: 192 func.t = fpemu_fnmsubd; 193 ftype = D2D; 194 break; 195 case fmuld_op: 196 func.t = fmuld; 197 ftype = D2D; 198 break; 199 case fdivd_op: 200 func.t = fdivd; 201 ftype = D2D; 202 break; 203 case fd1_f2op_op: 204 switch (NDS32Insn_OPCODE_BIT1014(insn)) { 205 case fd2s_op: 206 func.b = fd2s; 207 ftype = D1S; 208 break; 209 case fd2si_op: 210 func.b = fd2si; 211 ftype = D1S; 212 break; 213 case fd2si_z_op: 214 func.b = fd2si_z; 215 ftype = D1S; 216 break; 217 case fd2ui_op: 218 func.b = fd2ui; 219 ftype = D1S; 220 break; 221 case fd2ui_z_op: 222 func.b = fd2ui_z; 223 ftype = D1S; 224 break; 225 case fsi2d_op: 226 func.b = fsi2d; 227 ftype = D1S; 228 break; 229 case fui2d_op: 230 func.b = fui2d; 231 ftype = D1S; 232 break; 233 case fsqrtd_op: 234 func.b = fsqrtd; 235 ftype = D1D; 236 break; 237 default: 238 return SIGILL; 239 } 240 break; 241 default: 242 return SIGILL; 243 244 } 245 break; 246 } 247 248 case fd2_op: 249 switch (NDS32Insn_OPCODE_BIT69(insn)) { 250 case fcmpeqd_op: 251 case fcmpeqd_e_op: 252 case fcmpltd_op: 253 case fcmpltd_e_op: 254 case fcmpled_op: 255 case fcmpled_e_op: 256 case fcmpund_op: 257 case fcmpund_e_op: 258 ftype = CD; 259 break; 260 default: 261 return SIGILL; 262 } 263 break; 264 265 default: 266 return SIGILL; 267 } 268 269 switch (ftype) { 270 case S1S:{ 271 void *ft, *fa; 272 273 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 274 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 275 func.b(ft, fa); 276 break; 277 } 278 case S2S:{ 279 void *ft, *fa, *fb; 280 281 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 282 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 283 SPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn)); 284 func.t(ft, fa, fb); 285 break; 286 } 287 case S1D:{ 288 void *ft, *fa; 289 290 DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 291 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 292 func.b(ft, fa); 293 break; 294 } 295 case CS:{ 296 unsigned int cmpop = NDS32Insn_OPCODE_BIT69(insn); 297 void *ft, *fa, *fb; 298 299 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 300 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 301 SPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn)); 302 if (cmpop < 0x8) { 303 cmpop = cmptab[cmpop]; 304 fcmps(ft, fa, fb, cmpop); 305 } else 306 return SIGILL; 307 break; 308 } 309 case D1D:{ 310 void *ft, *fa; 311 312 DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 313 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 314 func.b(ft, fa); 315 break; 316 } 317 case D2D:{ 318 void *ft, *fa, *fb; 319 320 DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 321 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 322 DPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn)); 323 func.t(ft, fa, fb); 324 break; 325 } 326 case D1S:{ 327 void *ft, *fa; 328 329 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 330 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 331 func.b(ft, fa); 332 break; 333 } 334 case CD:{ 335 unsigned int cmpop = NDS32Insn_OPCODE_BIT69(insn); 336 void *ft, *fa, *fb; 337 338 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn)); 339 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn)); 340 DPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn)); 341 if (cmpop < 0x8) { 342 cmpop = cmptab[cmpop]; 343 fcmpd(ft, fa, fb, cmpop); 344 } else 345 return SIGILL; 346 break; 347 } 348 default: 349 return SIGILL; 350 } 351 352 /* 353 * If an exception is required, generate a tidy SIGFPE exception. 354 */ 355#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC) 356 if (((fpu_reg->fpcsr << 5) & fpu_reg->fpcsr & FPCSR_mskALLE_NO_UDF_IEXE) 357 || ((fpu_reg->fpcsr << 5) & (fpu_reg->UDF_IEX_trap))) { 358#else 359 if ((fpu_reg->fpcsr << 5) & fpu_reg->fpcsr & FPCSR_mskALLE) { 360#endif 361 return SIGFPE; 362 } 363 return 0; 364} 365 366int do_fpuemu(struct pt_regs *regs, struct fpu_struct *fpu) 367{ 368 unsigned long insn = 0, addr = regs->ipc; 369 unsigned long emulpc, contpc; 370 unsigned char *pc = (void *)&insn; 371 char c; 372 int i = 0, ret; 373 374 for (i = 0; i < 4; i++) { 375 if (__get_user(c, (unsigned char *)addr++)) 376 return SIGBUS; 377 *pc++ = c; 378 } 379 380 insn = be32_to_cpu(insn); 381 382 emulpc = regs->ipc; 383 contpc = regs->ipc + 4; 384 385 if (NDS32Insn_OPCODE(insn) != cop0_op) 386 return SIGILL; 387 388 switch (NDS32Insn_OPCODE_COP0(insn)) { 389 case fs1_op: 390 case fs2_op: 391 case fd1_op: 392 case fd2_op: 393 { 394 /* a real fpu computation instruction */ 395 ret = fpu_emu(fpu, insn); 396 if (!ret) 397 regs->ipc = contpc; 398 } 399 break; 400 401 default: 402 return SIGILL; 403 } 404 405 return ret; 406} 407