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
fldst(fp_simd_type * pfpsd,fp_inst_type pinst,struct regs * pregs,void * prw)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
fmovcc_fcc(fp_simd_type * pfpsd,fp_inst_type inst,fsr_type * pfsr,enum cc_type cc)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
fmovcc_icc(fp_simd_type * pfpsd,fp_inst_type inst,enum cc_type cc)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
fmovcc(fp_simd_type * pfpsd,fp_inst_type inst,fsr_type * pfsr)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
fmovr(fp_simd_type * pfpsd,fp_inst_type inst)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
movcc(fp_simd_type * pfpsd,fp_inst_type pinst,struct regs * pregs,void * prw,kfpu_t * pfpu)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