1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 NetWinder Floating Point Emulator 4 (c) Rebel.com, 1998-1999 5 (c) Philip Blundell, 1998, 2001 6 7 Direct questions, comments to Scott Bambrough <scottb@netwinder.org> 8 9 */ 10 11 #include "fpa11.h" 12 #include "softfloat.h" 13 #include "fpopcode.h" 14 #include "fpmodule.h" 15 #include "fpmodule.inl" 16 17 #include <linux/uaccess.h> 18 19 static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem) 20 { 21 FPA11 *fpa11 = GET_FPA11(); 22 fpa11->fType[Fn] = typeSingle; 23 get_user(fpa11->fpreg[Fn].fSingle, pMem); 24 } 25 26 static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem) 27 { 28 FPA11 *fpa11 = GET_FPA11(); 29 unsigned int *p; 30 p = (unsigned int *) &fpa11->fpreg[Fn].fDouble; 31 fpa11->fType[Fn] = typeDouble; 32 #ifdef __ARMEB__ 33 get_user(p[0], &pMem[0]); /* sign & exponent */ 34 get_user(p[1], &pMem[1]); 35 #else 36 get_user(p[0], &pMem[1]); 37 get_user(p[1], &pMem[0]); /* sign & exponent */ 38 #endif 39 } 40 41 #ifdef CONFIG_FPE_NWFPE_XP 42 static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem) 43 { 44 FPA11 *fpa11 = GET_FPA11(); 45 unsigned int *p; 46 p = (unsigned int *) &fpa11->fpreg[Fn].fExtended; 47 fpa11->fType[Fn] = typeExtended; 48 get_user(p[0], &pMem[0]); /* sign & exponent */ 49 #ifdef __ARMEB__ 50 get_user(p[1], &pMem[1]); /* ms bits */ 51 get_user(p[2], &pMem[2]); /* ls bits */ 52 #else 53 get_user(p[1], &pMem[2]); /* ls bits */ 54 get_user(p[2], &pMem[1]); /* ms bits */ 55 #endif 56 } 57 #endif 58 59 static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem) 60 { 61 FPA11 *fpa11 = GET_FPA11(); 62 register unsigned int *p; 63 unsigned long x; 64 65 p = (unsigned int *) &(fpa11->fpreg[Fn]); 66 get_user(x, &pMem[0]); 67 fpa11->fType[Fn] = (x >> 14) & 0x00000003; 68 69 switch (fpa11->fType[Fn]) { 70 case typeSingle: 71 case typeDouble: 72 { 73 get_user(p[0], &pMem[2]); /* Single */ 74 get_user(p[1], &pMem[1]); /* double msw */ 75 p[2] = 0; /* empty */ 76 } 77 break; 78 79 #ifdef CONFIG_FPE_NWFPE_XP 80 case typeExtended: 81 { 82 get_user(p[1], &pMem[2]); 83 get_user(p[2], &pMem[1]); /* msw */ 84 p[0] = (x & 0x80003fff); 85 } 86 break; 87 #endif 88 } 89 } 90 91 static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem) 92 { 93 FPA11 *fpa11 = GET_FPA11(); 94 union { 95 float32 f; 96 unsigned int i[1]; 97 } val; 98 99 switch (fpa11->fType[Fn]) { 100 case typeDouble: 101 val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble); 102 break; 103 104 #ifdef CONFIG_FPE_NWFPE_XP 105 case typeExtended: 106 val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended); 107 break; 108 #endif 109 110 default: 111 val.f = fpa11->fpreg[Fn].fSingle; 112 } 113 114 put_user(val.i[0], pMem); 115 } 116 117 static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem) 118 { 119 FPA11 *fpa11 = GET_FPA11(); 120 union { 121 float64 f; 122 unsigned int i[2]; 123 } val; 124 125 switch (fpa11->fType[Fn]) { 126 case typeSingle: 127 val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle); 128 break; 129 130 #ifdef CONFIG_FPE_NWFPE_XP 131 case typeExtended: 132 val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended); 133 break; 134 #endif 135 136 default: 137 val.f = fpa11->fpreg[Fn].fDouble; 138 } 139 140 #ifdef __ARMEB__ 141 put_user(val.i[0], &pMem[0]); /* msw */ 142 put_user(val.i[1], &pMem[1]); /* lsw */ 143 #else 144 put_user(val.i[1], &pMem[0]); /* msw */ 145 put_user(val.i[0], &pMem[1]); /* lsw */ 146 #endif 147 } 148 149 #ifdef CONFIG_FPE_NWFPE_XP 150 static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem) 151 { 152 FPA11 *fpa11 = GET_FPA11(); 153 union { 154 floatx80 f; 155 unsigned int i[3]; 156 } val; 157 158 switch (fpa11->fType[Fn]) { 159 case typeSingle: 160 val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); 161 break; 162 163 case typeDouble: 164 val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); 165 break; 166 167 default: 168 val.f = fpa11->fpreg[Fn].fExtended; 169 } 170 171 put_user(val.i[0], &pMem[0]); /* sign & exp */ 172 #ifdef __ARMEB__ 173 put_user(val.i[1], &pMem[1]); /* msw */ 174 put_user(val.i[2], &pMem[2]); 175 #else 176 put_user(val.i[1], &pMem[2]); 177 put_user(val.i[2], &pMem[1]); /* msw */ 178 #endif 179 } 180 #endif 181 182 static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem) 183 { 184 FPA11 *fpa11 = GET_FPA11(); 185 register unsigned int nType, *p; 186 187 p = (unsigned int *) &(fpa11->fpreg[Fn]); 188 nType = fpa11->fType[Fn]; 189 190 switch (nType) { 191 case typeSingle: 192 case typeDouble: 193 { 194 put_user(p[0], &pMem[2]); /* single */ 195 put_user(p[1], &pMem[1]); /* double msw */ 196 put_user(nType << 14, &pMem[0]); 197 } 198 break; 199 200 #ifdef CONFIG_FPE_NWFPE_XP 201 case typeExtended: 202 { 203 put_user(p[2], &pMem[1]); /* msw */ 204 put_user(p[1], &pMem[2]); 205 put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]); 206 } 207 break; 208 #endif 209 } 210 } 211 212 unsigned int PerformLDF(const unsigned int opcode) 213 { 214 unsigned int __user *pBase, *pAddress, *pFinal; 215 unsigned int nRc = 1, write_back = WRITE_BACK(opcode); 216 217 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 218 if (REG_PC == getRn(opcode)) { 219 pBase += 2; 220 write_back = 0; 221 } 222 223 pFinal = pBase; 224 if (BIT_UP_SET(opcode)) 225 pFinal += getOffset(opcode); 226 else 227 pFinal -= getOffset(opcode); 228 229 if (PREINDEXED(opcode)) 230 pAddress = pFinal; 231 else 232 pAddress = pBase; 233 234 switch (opcode & MASK_TRANSFER_LENGTH) { 235 case TRANSFER_SINGLE: 236 loadSingle(getFd(opcode), pAddress); 237 break; 238 case TRANSFER_DOUBLE: 239 loadDouble(getFd(opcode), pAddress); 240 break; 241 #ifdef CONFIG_FPE_NWFPE_XP 242 case TRANSFER_EXTENDED: 243 loadExtended(getFd(opcode), pAddress); 244 break; 245 #endif 246 default: 247 nRc = 0; 248 } 249 250 if (write_back) 251 writeRegister(getRn(opcode), (unsigned long) pFinal); 252 return nRc; 253 } 254 255 unsigned int PerformSTF(const unsigned int opcode) 256 { 257 unsigned int __user *pBase, *pAddress, *pFinal; 258 unsigned int nRc = 1, write_back = WRITE_BACK(opcode); 259 struct roundingData roundData; 260 261 roundData.mode = SetRoundingMode(opcode); 262 roundData.precision = SetRoundingPrecision(opcode); 263 roundData.exception = 0; 264 265 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 266 if (REG_PC == getRn(opcode)) { 267 pBase += 2; 268 write_back = 0; 269 } 270 271 pFinal = pBase; 272 if (BIT_UP_SET(opcode)) 273 pFinal += getOffset(opcode); 274 else 275 pFinal -= getOffset(opcode); 276 277 if (PREINDEXED(opcode)) 278 pAddress = pFinal; 279 else 280 pAddress = pBase; 281 282 switch (opcode & MASK_TRANSFER_LENGTH) { 283 case TRANSFER_SINGLE: 284 storeSingle(&roundData, getFd(opcode), pAddress); 285 break; 286 case TRANSFER_DOUBLE: 287 storeDouble(&roundData, getFd(opcode), pAddress); 288 break; 289 #ifdef CONFIG_FPE_NWFPE_XP 290 case TRANSFER_EXTENDED: 291 storeExtended(getFd(opcode), pAddress); 292 break; 293 #endif 294 default: 295 nRc = 0; 296 } 297 298 if (roundData.exception) 299 float_raise(roundData.exception); 300 301 if (write_back) 302 writeRegister(getRn(opcode), (unsigned long) pFinal); 303 return nRc; 304 } 305 306 unsigned int PerformLFM(const unsigned int opcode) 307 { 308 unsigned int __user *pBase, *pAddress, *pFinal; 309 unsigned int i, Fd, write_back = WRITE_BACK(opcode); 310 311 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 312 if (REG_PC == getRn(opcode)) { 313 pBase += 2; 314 write_back = 0; 315 } 316 317 pFinal = pBase; 318 if (BIT_UP_SET(opcode)) 319 pFinal += getOffset(opcode); 320 else 321 pFinal -= getOffset(opcode); 322 323 if (PREINDEXED(opcode)) 324 pAddress = pFinal; 325 else 326 pAddress = pBase; 327 328 Fd = getFd(opcode); 329 for (i = getRegisterCount(opcode); i > 0; i--) { 330 loadMultiple(Fd, pAddress); 331 pAddress += 3; 332 Fd++; 333 if (Fd == 8) 334 Fd = 0; 335 } 336 337 if (write_back) 338 writeRegister(getRn(opcode), (unsigned long) pFinal); 339 return 1; 340 } 341 342 unsigned int PerformSFM(const unsigned int opcode) 343 { 344 unsigned int __user *pBase, *pAddress, *pFinal; 345 unsigned int i, Fd, write_back = WRITE_BACK(opcode); 346 347 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 348 if (REG_PC == getRn(opcode)) { 349 pBase += 2; 350 write_back = 0; 351 } 352 353 pFinal = pBase; 354 if (BIT_UP_SET(opcode)) 355 pFinal += getOffset(opcode); 356 else 357 pFinal -= getOffset(opcode); 358 359 if (PREINDEXED(opcode)) 360 pAddress = pFinal; 361 else 362 pAddress = pBase; 363 364 Fd = getFd(opcode); 365 for (i = getRegisterCount(opcode); i > 0; i--) { 366 storeMultiple(Fd, pAddress); 367 pAddress += 3; 368 Fd++; 369 if (Fd == 8) 370 Fd = 0; 371 } 372 373 if (write_back) 374 writeRegister(getRn(opcode), (unsigned long) pFinal); 375 return 1; 376 } 377 378 unsigned int EmulateCPDT(const unsigned int opcode) 379 { 380 unsigned int nRc = 0; 381 382 if (LDF_OP(opcode)) { 383 nRc = PerformLDF(opcode); 384 } else if (LFM_OP(opcode)) { 385 nRc = PerformLFM(opcode); 386 } else if (STF_OP(opcode)) { 387 nRc = PerformSTF(opcode); 388 } else if (SFM_OP(opcode)) { 389 nRc = PerformSFM(opcode); 390 } else { 391 nRc = 0; 392 } 393 394 return nRc; 395 } 396
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.