1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Integer Unit simulator for Sparc FPU simulator. */ 30 31 #include <sys/fpu/fpu_simulator.h> 32 #include <sys/fpu/globals.h> 33 34 #include <sys/privregs.h> 35 #include <sys/vis_simulator.h> 36 #include <sys/asi.h> 37 #include <sys/simulate.h> 38 #include <sys/model.h> 39 40 #define FPU_REG_FIELD uint32_reg /* Coordinate with FPU_REGS_TYPE. */ 41 #define FPU_DREG_FIELD uint64_reg /* Coordinate with FPU_DREGS_TYPE. */ 42 #define FPU_FSR_FIELD uint64_reg /* Coordinate with V9_FPU_FSR_TYPE. */ 43 44 /* 45 * Simulator for loads and stores between floating-point unit and memory. 46 */ 47 enum ftt_type 48 fldst( 49 fp_simd_type *pfpsd, /* FPU simulator data. */ 50 fp_inst_type pinst, /* FPU instruction to simulate. */ 51 struct regs *pregs, /* Pointer to PCB image of registers. */ 52 void *prw) /* Pointer to locals and ins. */ 53 { 54 uint32_t sz_bits, asi = 0; 55 uint64_t fea, tea; 56 uint64_t *ea; 57 enum ftt_type ftt; 58 char *badaddr = (caddr_t)(-1); 59 union { 60 fp_inst_type inst; 61 int32_t i; 62 } fp; 63 64 fp.inst = pinst; 65 if ((pinst.op3 >> 4) & 1) { 66 if (pinst.ibit) { 67 asi = (uint32_t)((pregs->r_tstate >> TSTATE_ASI_SHIFT) & 68 TSTATE_ASI_MASK); 69 } else { 70 asi = (fp.i >> 5) & 0xff; 71 } 72 /* check for ld/st alternate and highest defined V9 asi */ 73 if (((pinst.op3 & 0x30) == 0x30) && (asi > ASI_SNFL)) 74 return (vis_fldst(pfpsd, pinst, pregs, prw, asi)); 75 } 76 77 if (pinst.ibit == 0) { /* effective address = rs1 + rs2 */ 78 ftt = read_iureg(pfpsd, pinst.rs1, pregs, prw, &fea); 79 if (ftt != ftt_none) 80 return (ftt); 81 ftt = read_iureg(pfpsd, pinst.rs2, pregs, prw, &tea); 82 if (ftt != ftt_none) 83 return (ftt); 84 ea = (uint64_t *)(fea + tea); 85 } else { /* effective address = rs1 + imm13 */ 86 /* Extract simm13 field. */ 87 fea = (uint64_t)((fp.i << 19) >> 19); 88 ftt = read_iureg(pfpsd, pinst.rs1, pregs, prw, &tea); 89 if (ftt != ftt_none) 90 return (ftt); 91 ea = (uint64_t *)(fea + tea); 92 } 93 sz_bits = pinst.op3 & 0x3; 94 switch (sz_bits) { /* map size bits to a number */ 95 case 0: /* ldf{a}/stf{a} */ 96 /* Must be word-aligned. */ 97 if (((uintptr_t)ea & 0x3) != 0) 98 return (ftt_alignment); 99 break; 100 case 1: if (pinst.rd == 0) { /* ldfsr/stfsr */ 101 /* Must be word-aligned. */ 102 if (((uintptr_t)ea & 0x3) != 0) 103 return (ftt_alignment); 104 } else { /* ldxfsr/stxfsr */ 105 /* Must be extword-aligned. */ 106 if (((uintptr_t)ea & 0x7) != 0) 107 return (ftt_alignment); 108 } 109 break; 110 case 2: /* ldqf{a}/stqf{a} */ 111 /* Require only word alignment. */ 112 if (((uintptr_t)ea & 0x3) != 0) 113 return (ftt_alignment); 114 break; 115 case 3: /* lddf{a}/stdf{a} */ 116 if (get_udatamodel() == DATAMODEL_ILP32) { 117 /* Require 64 bit-alignment. */ 118 if (((uintptr_t)ea & 0x7) != 0) 119 return (ftt_alignment); 120 } else { 121 if (((uintptr_t)ea & 0x3) != 0) 122 return (ftt_alignment); 123 } 124 } 125 126 pfpsd->fp_trapaddr = (caddr_t)ea; /* setup bad addr in case we trap */ 127 if ((pinst.op3 >> 2) & 1) /* store */ 128 pfpsd->fp_traprw = S_READ; 129 else 130 pfpsd->fp_traprw = S_WRITE; 131 132 switch (do_unaligned(pregs, &badaddr)) { 133 case SIMU_FAULT: 134 return (ftt_fault); 135 case SIMU_ILLEGAL: 136 return (ftt_unimplemented); 137 case SIMU_SUCCESS: 138 break; 139 } 140 pregs->r_pc = pregs->r_npc; /* Do not retry emulated instruction. */ 141 pregs->r_npc += 4; 142 return (ftt_none); 143 } 144 145 /* 146 * Floating-point conditional moves between floating point unit registers. 147 */ 148 static enum ftt_type 149 fmovcc_fcc( 150 fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ 151 fp_inst_type inst, /* FPU instruction to simulate. */ 152 fsr_type *pfsr, /* Pointer to image of FSR to read and write. */ 153 enum cc_type cc) /* FSR condition code field from fcc[0-3] */ 154 { 155 uint32_t moveit; 156 fsr_type fsr; 157 enum fcc_type fcc; 158 enum icc_type { 159 fmovn, fmovne, fmovlg, fmovul, fmovl, fmovug, fmovg, fmovu, 160 fmova, fmove, fmovue, fmovge, fmovuge, fmovle, fmovule, fmovo 161 } cond; 162 163 fsr = *pfsr; 164 switch (cc) { 165 case fcc_0: 166 fcc = fsr.fcc0; 167 break; 168 case fcc_1: 169 fcc = fsr.fcc1; 170 break; 171 case fcc_2: 172 fcc = fsr.fcc2; 173 break; 174 case fcc_3: 175 fcc = fsr.fcc3; 176 break; 177 default: 178 return (ftt_unimplemented); 179 } 180 181 cond = (enum icc_type) (inst.rs1 & 0xf); 182 switch (cond) { 183 case fmovn: 184 moveit = 0; 185 break; 186 case fmovl: 187 moveit = fcc == fcc_less; 188 break; 189 case fmovg: 190 moveit = fcc == fcc_greater; 191 break; 192 case fmovu: 193 moveit = fcc == fcc_unordered; 194 break; 195 case fmove: 196 moveit = fcc == fcc_equal; 197 break; 198 case fmovlg: 199 moveit = (fcc == fcc_less) || (fcc == fcc_greater); 200 break; 201 case fmovul: 202 moveit = (fcc == fcc_unordered) || (fcc == fcc_less); 203 break; 204 case fmovug: 205 moveit = (fcc == fcc_unordered) || (fcc == fcc_greater); 206 break; 207 case fmovue: 208 moveit = (fcc == fcc_unordered) || (fcc == fcc_equal); 209 break; 210 case fmovge: 211 moveit = (fcc == fcc_greater) || (fcc == fcc_equal); 212 break; 213 case fmovle: 214 moveit = (fcc == fcc_less) || (fcc == fcc_equal); 215 break; 216 case fmovne: 217 moveit = fcc != fcc_equal; 218 break; 219 case fmovuge: 220 moveit = fcc != fcc_less; 221 break; 222 case fmovule: 223 moveit = fcc != fcc_greater; 224 break; 225 case fmovo: 226 moveit = fcc != fcc_unordered; 227 break; 228 case fmova: 229 moveit = 1; 230 break; 231 default: 232 return (ftt_unimplemented); 233 } 234 if (moveit) { /* Move fpu register. */ 235 uint32_t nrs2, nrd; 236 uint32_t usr; 237 uint64_t lusr; 238 239 nrs2 = inst.rs2; 240 nrd = inst.rd; 241 if (inst.prec < 2) { /* fmovs */ 242 _fp_unpack_word(pfpsd, &usr, nrs2); 243 _fp_pack_word(pfpsd, &usr, nrd); 244 } else { /* fmovd */ 245 /* fix register encoding */ 246 if ((nrs2 & 1) == 1) 247 nrs2 = (nrs2 & 0x1e) | 0x20; 248 _fp_unpack_extword(pfpsd, &lusr, nrs2); 249 if ((nrd & 1) == 1) 250 nrd = (nrd & 0x1e) | 0x20; 251 _fp_pack_extword(pfpsd, &lusr, nrd); 252 if (inst.prec > 2) { /* fmovq */ 253 _fp_unpack_extword(pfpsd, &lusr, nrs2+2); 254 _fp_pack_extword(pfpsd, &lusr, nrd+2); 255 } 256 } 257 } 258 return (ftt_none); 259 } 260 261 /* 262 * Integer conditional moves between floating point unit registers. 263 */ 264 static enum ftt_type 265 fmovcc_icc( 266 fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ 267 fp_inst_type inst, /* FPU instruction to simulate. */ 268 enum cc_type cc) /* CCR condition code field from tstate */ 269 { 270 int moveit; 271 enum icc_type { 272 fmovn, fmove, fmovle, fmovl, fmovleu, fmovcs, fmovneg, fmovvs, 273 fmova, fmovne, fmovg, fmovge, fmovgu, fmovcc, fmovpos, fmovvc 274 } cond; 275 276 struct regs *pregs; 277 uint64_t tstate; 278 union { 279 uint32_t i; 280 ccr_type cc; 281 } ccr; 282 283 pregs = lwptoregs(curthread->t_lwp); 284 tstate = pregs->r_tstate; 285 switch (cc) { 286 case icc: 287 ccr.i = (uint32_t)((tstate >> TSTATE_CCR_SHIFT) & 0xf); 288 break; 289 case xcc: 290 ccr.i = (uint32_t)(((tstate >> TSTATE_CCR_SHIFT) & 0xf0) >> 4); 291 break; 292 } 293 294 cond = (enum icc_type) (inst.rs1 & 0xf); 295 switch (cond) { 296 case fmovn: 297 moveit = 0; 298 break; 299 case fmove: 300 moveit = (int)(ccr.cc.z); 301 break; 302 case fmovle: 303 moveit = (int)(ccr.cc.z | (ccr.cc.n ^ ccr.cc.v)); 304 break; 305 case fmovl: 306 moveit = (int)(ccr.cc.n ^ ccr.cc.v); 307 break; 308 case fmovleu: 309 moveit = (int)(ccr.cc.c | ccr.cc.z); 310 break; 311 case fmovcs: 312 moveit = (int)(ccr.cc.c); 313 break; 314 case fmovneg: 315 moveit = (int)(ccr.cc.n); 316 break; 317 case fmovvs: 318 moveit = (int)(ccr.cc.v); 319 break; 320 case fmova: 321 moveit = 1; 322 break; 323 case fmovne: 324 moveit = (int)(ccr.cc.z == 0); 325 break; 326 case fmovg: 327 moveit = (int)((ccr.cc.z | (ccr.cc.n ^ ccr.cc.v)) == 0); 328 break; 329 case fmovge: 330 moveit = (int)((ccr.cc.n ^ ccr.cc.v) == 0); 331 break; 332 case fmovgu: 333 moveit = (int)((ccr.cc.c | ccr.cc.z) == 0); 334 break; 335 case fmovcc: 336 moveit = (int)(ccr.cc.c == 0); 337 break; 338 case fmovpos: 339 moveit = (int)(ccr.cc.n == 0); 340 break; 341 case fmovvc: 342 moveit = (int)(ccr.cc.v == 0); 343 break; 344 default: 345 return (ftt_unimplemented); 346 } 347 if (moveit) { /* Move fpu register. */ 348 uint32_t nrs2, nrd; 349 uint32_t usr; 350 uint64_t lusr; 351 352 nrs2 = inst.rs2; 353 nrd = inst.rd; 354 if (inst.prec < 2) { /* fmovs */ 355 _fp_unpack_word(pfpsd, &usr, nrs2); 356 _fp_pack_word(pfpsd, &usr, nrd); 357 } else { /* fmovd */ 358 /* fix register encoding */ 359 if ((nrs2 & 1) == 1) 360 nrs2 = (nrs2 & 0x1e) | 0x20; 361 _fp_unpack_extword(pfpsd, &lusr, nrs2); 362 if ((nrd & 1) == 1) 363 nrd = (nrd & 0x1e) | 0x20; 364 _fp_pack_extword(pfpsd, &lusr, nrd); 365 if (inst.prec > 2) { /* fmovq */ 366 _fp_unpack_extword(pfpsd, &lusr, nrs2+2); 367 _fp_pack_extword(pfpsd, &lusr, nrd+2); 368 } 369 } 370 } 371 return (ftt_none); 372 } 373 374 /* 375 * Simulator for moving fp register on condition (FMOVcc). 376 * FMOVccq (Quad version of instruction) not supported by Ultra-1, so this 377 * code must always be present. 378 */ 379 enum ftt_type 380 fmovcc( 381 fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ 382 fp_inst_type inst, /* FPU instruction to simulate. */ 383 fsr_type *pfsr) /* Pointer to image of FSR to read and write. */ 384 { 385 enum cc_type opf_cc; 386 387 opf_cc = (enum cc_type) ((inst.ibit << 2) | (inst.opcode >> 4)); 388 if ((opf_cc == icc) || (opf_cc == xcc)) { 389 return (fmovcc_icc(pfpsd, inst, opf_cc)); 390 } else { 391 return (fmovcc_fcc(pfpsd, inst, pfsr, opf_cc)); 392 } 393 } 394 395 /* 396 * Simulator for moving fp register on integer register condition (FMOVr). 397 * FMOVrq (Quad version of instruction) not supported by Ultra-1, so this 398 * code must always be present. 399 */ 400 enum ftt_type 401 fmovr( 402 fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ 403 fp_inst_type inst) /* FPU instruction to simulate. */ 404 { 405 struct regs *pregs; 406 ulong_t *prw; 407 uint32_t nrs1; 408 enum ftt_type ftt; 409 enum rcond_type { 410 none, fmovre, fmovrlez, fmovrlz, 411 nnone, fmovrne, fmovrgz, fmovrgez 412 } rcond; 413 int64_t moveit, r; 414 415 nrs1 = inst.rs1; 416 if (nrs1 > 15) /* rs1 must be a global register */ 417 return (ftt_unimplemented); 418 if (inst.ibit) /* ibit must be unused */ 419 return (ftt_unimplemented); 420 pregs = lwptoregs(curthread->t_lwp); 421 prw = (ulong_t *)pregs->r_sp; 422 ftt = read_iureg(pfpsd, nrs1, pregs, prw, (uint64_t *)&r); 423 if (ftt != ftt_none) 424 return (ftt); 425 rcond = (enum rcond_type) (inst.opcode >> 3) & 7; 426 switch (rcond) { 427 case fmovre: 428 moveit = r == 0; 429 break; 430 case fmovrlez: 431 moveit = r <= 0; 432 break; 433 case fmovrlz: 434 moveit = r < 0; 435 break; 436 case fmovrne: 437 moveit = r != 0; 438 break; 439 case fmovrgz: 440 moveit = r > 0; 441 break; 442 case fmovrgez: 443 moveit = r >= 0; 444 break; 445 default: 446 return (ftt_unimplemented); 447 } 448 if (moveit) { /* Move fpu register. */ 449 uint32_t nrs2, nrd; 450 uint32_t usr; 451 uint64_t lusr; 452 453 nrs2 = inst.rs2; 454 nrd = inst.rd; 455 if (inst.prec < 2) { /* fmovs */ 456 _fp_unpack_word(pfpsd, &usr, nrs2); 457 _fp_pack_word(pfpsd, &usr, nrd); 458 } else { /* fmovd */ 459 _fp_unpack_extword(pfpsd, &lusr, nrs2); 460 _fp_pack_extword(pfpsd, &lusr, nrd); 461 if (inst.prec > 2) { /* fmovq */ 462 _fp_unpack_extword(pfpsd, &lusr, nrs2+2); 463 _fp_pack_extword(pfpsd, &lusr, nrd+2); 464 } 465 } 466 } 467 return (ftt_none); 468 } 469 470 /* 471 * Move integer register on condition (MOVcc). 472 */ 473 enum ftt_type 474 movcc( 475 fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ 476 fp_inst_type pinst, /* FPU instruction to simulate. */ 477 struct regs *pregs, /* Pointer to PCB image of registers. */ 478 void *prw, /* Pointer to locals and ins. */ 479 kfpu_t *pfpu) /* Pointer to FPU register block. */ 480 481 { 482 fsr_type fsr; 483 enum cc_type cc; 484 enum fcc_type fcc; 485 enum icc_type { 486 fmovn, fmovne, fmovlg, fmovul, fmovl, fmovug, fmovg, fmovu, 487 fmova, fmove, fmovue, fmovge, fmovuge, fmovle, fmovule, fmovo 488 } cond; 489 uint32_t moveit; 490 enum ftt_type ftt = ftt_none; 491 492 cc = (enum cc_type) (pinst.opcode >> 0x4) & 3; 493 fsr.ll = pfpu->fpu_fsr; 494 cond = (enum icc_type) (pinst.rs1 & 0xf); 495 switch (cc) { 496 case fcc_0: 497 fcc = fsr.fcc0; 498 break; 499 case fcc_1: 500 fcc = fsr.fcc1; 501 break; 502 case fcc_2: 503 fcc = fsr.fcc2; 504 break; 505 case fcc_3: 506 fcc = fsr.fcc3; 507 break; 508 default: 509 return (ftt_unimplemented); 510 } 511 512 switch (cond) { 513 case fmovn: 514 moveit = 0; 515 break; 516 case fmovl: 517 moveit = fcc == fcc_less; 518 break; 519 case fmovg: 520 moveit = fcc == fcc_greater; 521 break; 522 case fmovu: 523 moveit = fcc == fcc_unordered; 524 break; 525 case fmove: 526 moveit = fcc == fcc_equal; 527 break; 528 case fmovlg: 529 moveit = (fcc == fcc_less) || (fcc == fcc_greater); 530 break; 531 case fmovul: 532 moveit = (fcc == fcc_unordered) || (fcc == fcc_less); 533 break; 534 case fmovug: 535 moveit = (fcc == fcc_unordered) || (fcc == fcc_greater); 536 break; 537 case fmovue: 538 moveit = (fcc == fcc_unordered) || (fcc == fcc_equal); 539 break; 540 case fmovge: 541 moveit = (fcc == fcc_greater) || (fcc == fcc_equal); 542 break; 543 case fmovle: 544 moveit = (fcc == fcc_less) || (fcc == fcc_equal); 545 break; 546 case fmovne: 547 moveit = fcc != fcc_equal; 548 break; 549 case fmovuge: 550 moveit = fcc != fcc_less; 551 break; 552 case fmovule: 553 moveit = fcc != fcc_greater; 554 break; 555 case fmovo: 556 moveit = fcc != fcc_unordered; 557 break; 558 case fmova: 559 moveit = 1; 560 break; 561 default: 562 return (ftt_unimplemented); 563 } 564 if (moveit) { /* Move fpu register. */ 565 uint32_t nrd; 566 uint64_t r; 567 568 nrd = pinst.rd; 569 if (pinst.ibit == 0) { /* copy the value in r[rs2] */ 570 uint32_t nrs2; 571 572 nrs2 = pinst.rs2; 573 ftt = read_iureg(pfpsd, nrs2, pregs, prw, &r); 574 if (ftt != ftt_none) 575 return (ftt); 576 ftt = write_iureg(pfpsd, nrd, pregs, prw, &r); 577 } else { /* use sign_ext(simm11) */ 578 union { 579 fp_inst_type inst; 580 int32_t i; 581 } fp; 582 583 fp.inst = pinst; /* Extract simm11 field */ 584 r = (fp.i << 21) >> 21; 585 ftt = write_iureg(pfpsd, nrd, pregs, prw, &r); 586 } 587 } 588 pregs->r_pc = pregs->r_npc; /* Do not retry emulated instruction. */ 589 pregs->r_npc += 4; 590 return (ftt); 591 } 592