xref: /illumos-gate/usr/src/uts/sparc/v9/fpu/v9instr.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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