2 * Copyright (C) 1996 Wolfgang Solfrank.
3 * Copyright (C) 1996 TooLs GmbH.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by TooLs GmbH.
17 * 4. The name of TooLs GmbH may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * $NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/limits.h>
42 #include <machine/altivec.h>
43 #include <machine/fpu.h>
44 #include <machine/ieeefp.h>
45 #include <machine/pcb.h>
46 #include <machine/psl.h>
48 #include <powerpc/fpu/fpu_arith.h>
49 #include <powerpc/fpu/fpu_emu.h>
50 #include <powerpc/fpu/fpu_extern.h>
52 void spe_handle_fpdata(struct trapframe *);
53 void spe_handle_fpround(struct trapframe *);
54 static int spe_emu_instr(uint32_t, struct fpemu *, struct fpn **, uint32_t *);
57 save_vec_int(struct thread *td)
65 * Temporarily re-enable the vector unit during the save
71 * Save the vector registers and SPEFSCR to the PCB
73 #define EVSTDW(n) __asm ("evstdw %1,0(%0)" \
74 :: "b"(pcb->pcb_vec.vr[n]), "n"(n));
75 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3);
76 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7);
77 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11);
78 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15);
79 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19);
80 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23);
81 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27);
82 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31);
85 __asm ( "evxor 0,0,0\n"
87 "evstdd 0,0(%0)" :: "b"(&pcb->pcb_vec.spare[0]));
88 pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
91 * Disable vector unit again
99 enable_vec(struct thread *td)
103 struct trapframe *tf;
109 * Save the thread's SPE CPU number, and set the CPU's current
112 td->td_pcb->pcb_veccpu = PCPU_GET(cpuid);
113 PCPU_SET(vecthread, td);
116 * Enable the vector unit for when the thread returns from the
117 * exception. If this is the first time the unit has been used by
118 * the thread, initialise the vector registers and VSCR to 0, and
119 * set the flag to indicate that the vector unit is in use.
122 if (!(pcb->pcb_flags & PCB_VEC)) {
123 memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec);
124 pcb->pcb_flags |= PCB_VEC;
125 pcb->pcb_vec.vscr = mfspr(SPR_SPEFSCR);
129 * Temporarily enable the vector unit so the registers
133 mtmsr(msr | PSL_VEC);
135 /* Restore SPEFSCR and ACC. Use %r0 as the scratch for ACC. */
136 mtspr(SPR_SPEFSCR, pcb->pcb_vec.vscr);
137 __asm __volatile("isync;evldd 0, 0(%0); evmra 0,0\n"
138 :: "b"(&pcb->pcb_vec.spare[0]));
141 * The lower half of each register will be restored on trap return. Use
142 * %r0 as a scratch register, and restore it last.
144 #define EVLDW(n) __asm __volatile("evldw 0, 0(%0); evmergehilo "#n",0,"#n \
145 :: "b"(&pcb->pcb_vec.vr[n]));
146 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4);
147 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8);
148 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12);
149 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16);
150 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20);
151 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24);
152 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28);
153 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0);
161 save_vec(struct thread *td)
170 * Clear the current vec thread and pcb's CPU id
171 * XXX should this be left clear to allow lazy save/restore ?
173 pcb->pcb_veccpu = INT_MAX;
174 PCPU_SET(vecthread, NULL);
178 * Save SPE state without dropping ownership. This will only save state if
179 * the current vector-thread is `td'. This is used for taking core dumps, so
180 * don't leak kernel information; overwrite the low words of each vector with
181 * their real value, taken from the thread's trap frame, unconditionally.
184 save_vec_nodrop(struct thread *td)
189 if (td == PCPU_GET(vecthread))
194 for (i = 0; i < 32; i++) {
195 pcb->pcb_vec.vr[i][1] =
196 td->td_frame ? td->td_frame->fixreg[i] : 0;
201 #define SPE_INST_MASK 0x31f
228 #define EVFSADD 0x280
229 #define EVFSSUB 0x281
230 #define EVFSABS 0x284
231 #define EVFSNABS 0x285
232 #define EVFSNEG 0x286
233 #define EVFSMUL 0x288
234 #define EVFSDIV 0x289
235 #define EVFSCMPGT 0x28c
236 #define EVFSCMPLT 0x28d
237 #define EVFSCMPEQ 0x28e
238 #define EVFSCFUI 0x290
239 #define EVFSCFSI 0x291
240 #define EVFSCTUI 0x294
241 #define EVFSCTSI 0x295
242 #define EVFSCTUF 0x296
243 #define EVFSCTSF 0x297
244 #define EVFSCTUIZ 0x298
245 #define EVFSCTSIZ 0x29a
250 #define EFSNABS 0x2c5
254 #define EFSCMPGT 0x2cc
255 #define EFSCMPLT 0x2cd
256 #define EFSCMPEQ 0x2ce
258 #define EFSCFUI 0x2d0
259 #define EFSCFSI 0x2d1
260 #define EFSCTUI 0x2d4
261 #define EFSCTSI 0x2d5
262 #define EFSCTUF 0x2d6
263 #define EFSCTSF 0x2d7
264 #define EFSCTUIZ 0x2d8
265 #define EFSCTSIZ 0x2da
270 #define EFDNABS 0x2e5
274 #define EFDCMPGT 0x2ec
275 #define EFDCMPLT 0x2ed
276 #define EFDCMPEQ 0x2ee
278 #define EFDCFUI 0x2f0
279 #define EFDCFSI 0x2f1
280 #define EFDCTUI 0x2f4
281 #define EFDCTSI 0x2f5
282 #define EFDCTUF 0x2f6
283 #define EFDCTSF 0x2f7
284 #define EFDCTUIZ 0x2f8
285 #define EFDCTSIZ 0x2fa
294 static uint32_t fpscr_to_spefscr(uint32_t fpscr)
300 if (fpscr & FPSCR_VX)
301 spefscr |= SPEFSCR_FINV;
302 if (fpscr & FPSCR_OX)
303 spefscr |= SPEFSCR_FOVF;
304 if (fpscr & FPSCR_UX)
305 spefscr |= SPEFSCR_FUNF;
306 if (fpscr & FPSCR_ZX)
307 spefscr |= SPEFSCR_FDBZ;
308 if (fpscr & FPSCR_XX)
309 spefscr |= SPEFSCR_FX;
314 /* Sign is 0 for unsigned, 1 for signed. */
316 spe_to_int(struct fpemu *fpemu, struct fpn *fpn, uint32_t *val, int sign)
320 res[0] = fpu_ftox(fpemu, fpn, res);
321 if (res[0] != UINT_MAX && res[0] != 0)
322 fpemu->fe_cx |= FPSCR_OX;
323 else if (sign == 0 && res[0] != 0)
324 fpemu->fe_cx |= FPSCR_UX;
331 /* Masked instruction */
333 * For compare instructions, returns 1 if success, 0 if not. For all others,
334 * returns -1, or -2 if no result needs recorded.
337 spe_emu_instr(uint32_t instr, struct fpemu *fpemu,
338 struct fpn **result, uint32_t *iresult)
340 switch (instr & SPE_INST_MASK) {
344 /* Taken care of elsewhere. */
347 fpemu->fe_cx &= ~FPSCR_RN;
348 fpemu->fe_cx |= FP_RZ;
350 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 0);
353 fpemu->fe_cx &= ~FPSCR_RN;
354 fpemu->fe_cx |= FP_RZ;
356 spe_to_int(fpemu, &fpemu->fe_f2, iresult, 1);
359 *result = fpu_add(fpemu);
362 *result = fpu_sub(fpemu);
365 *result = fpu_mul(fpemu);
368 *result = fpu_div(fpemu);
371 fpu_compare(fpemu, 0);
372 if (fpemu->fe_cx & FPSCR_FG)
376 fpu_compare(fpemu, 0);
377 if (fpemu->fe_cx & FPSCR_FL)
381 fpu_compare(fpemu, 0);
382 if (fpemu->fe_cx & FPSCR_FE)
386 printf("Unknown instruction %x\n", instr);
393 spe_explode(struct fpemu *fe, struct fpn *fp, uint32_t type,
394 uint32_t hi, uint32_t lo)
398 fp->fp_sign = hi >> 31;
402 s = fpu_stof(fp, hi);
406 s = fpu_dtof(fp, hi, lo);
410 if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) {
412 * Input is a signalling NaN. All operations that return
413 * an input NaN operand put it through a ``NaN conversion'',
414 * which basically just means ``turn on the quiet bit''.
415 * We do this here so that all NaNs internally look quiet
416 * (we can tell signalling ones by their class).
418 fp->fp_mant[0] |= FP_QUIETBIT;
419 fe->fe_cx = FPSCR_VXSNAN; /* assert invalid operand */
428 * Save the high word of a 64-bit GPR for manipulation in the exception handler.
431 spe_save_reg_high(int reg)
434 #define EVSTDW(n) case n: __asm __volatile ("evstdw %1,0(%0)" \
435 :: "b"(vec), "n"(n) : "memory"); break;
437 EVSTDW(0); EVSTDW(1); EVSTDW(2); EVSTDW(3);
438 EVSTDW(4); EVSTDW(5); EVSTDW(6); EVSTDW(7);
439 EVSTDW(8); EVSTDW(9); EVSTDW(10); EVSTDW(11);
440 EVSTDW(12); EVSTDW(13); EVSTDW(14); EVSTDW(15);
441 EVSTDW(16); EVSTDW(17); EVSTDW(18); EVSTDW(19);
442 EVSTDW(20); EVSTDW(21); EVSTDW(22); EVSTDW(23);
443 EVSTDW(24); EVSTDW(25); EVSTDW(26); EVSTDW(27);
444 EVSTDW(28); EVSTDW(29); EVSTDW(30); EVSTDW(31);
452 * Load the given value into the high word of the requested register.
455 spe_load_reg_high(int reg, uint32_t val)
457 #define EVLDW(n) case n: __asm __volatile("evmergelo "#n",%0,"#n \
460 EVLDW(1); EVLDW(2); EVLDW(3); EVLDW(4);
461 EVLDW(5); EVLDW(6); EVLDW(7); EVLDW(8);
462 EVLDW(9); EVLDW(10); EVLDW(11); EVLDW(12);
463 EVLDW(13); EVLDW(14); EVLDW(15); EVLDW(16);
464 EVLDW(17); EVLDW(18); EVLDW(19); EVLDW(20);
465 EVLDW(21); EVLDW(22); EVLDW(23); EVLDW(24);
466 EVLDW(25); EVLDW(26); EVLDW(27); EVLDW(28);
467 EVLDW(29); EVLDW(30); EVLDW(31); EVLDW(0);
474 spe_handle_fpdata(struct trapframe *frame)
478 uint32_t instr, instr_sec_op;
479 uint32_t cr_shift, ra, rb, rd, src;
480 uint32_t high, low, res, tmp; /* For vector operations. */
481 uint32_t spefscr = 0;
482 uint32_t ftod_res[2];
483 int width; /* Single, Double, Vector, Integer */
487 err = fueword32((void *)frame->srr0, &instr);
493 if ((instr >> OPC_SHIFT) != SPE_OPC)
498 * 'cr' field is the upper 3 bits of rd. Magically, since a) rd is 5
499 * bits, b) each 'cr' field is 4 bits, and c) Only the 'GT' bit is
500 * modified for most compare operations, the full value of rd can be
501 * used as a shift value.
503 rd = (instr >> 21) & 0x1f;
504 ra = (instr >> 16) & 0x1f;
505 rb = (instr >> 11) & 0x1f;
506 src = (instr >> 5) & 0x7;
507 cr_shift = 28 - (rd & 0x1f);
509 instr_sec_op = (instr & 0x7ff);
511 memset(&fpemu, 0, sizeof(fpemu));
516 mtmsr(msr | PSL_VEC);
517 switch (instr_sec_op) {
519 high = spe_save_reg_high(ra) & ~(1U << 31);
520 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
521 spe_load_reg_high(rd, high);
524 high = spe_save_reg_high(ra) | (1U << 31);
525 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
526 spe_load_reg_high(rd, high);
529 high = spe_save_reg_high(ra) ^ (1U << 31);
530 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
531 spe_load_reg_high(rd, high);
535 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
536 spe_save_reg_high(ra), 0);
537 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
538 spe_save_reg_high(rb), 0);
539 high = spe_emu_instr(instr_sec_op, &fpemu, &result,
543 spe_load_reg_high(rd, tmp);
545 spefscr = fpscr_to_spefscr(fpemu.fe_cx) << 16;
546 /* Clear the fpemu to start over on the lower bits. */
547 memset(&fpemu, 0, sizeof(fpemu));
550 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
551 frame->fixreg[ra], 0);
552 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
553 frame->fixreg[rb], 0);
554 spefscr |= fpscr_to_spefscr(fpemu.fe_cx);
555 low = spe_emu_instr(instr_sec_op, &fpemu, &result,
557 if (instr_sec_op == EVFSCMPEQ ||
558 instr_sec_op == EVFSCMPGT ||
559 instr_sec_op == EVFSCMPLT) {
560 res = (high << 3) | (low << 2) |
561 ((high | low) << 1) | (high & low);
570 switch (instr_sec_op) {
572 frame->fixreg[rd] = frame->fixreg[ra] & ~(1U << 31);
575 frame->fixreg[rd] = frame->fixreg[ra] | (1U << 31);
578 frame->fixreg[rd] = frame->fixreg[ra] ^ (1U << 31);
581 mtmsr(msr | PSL_VEC);
582 spe_explode(&fpemu, &fpemu.fe_f3, DOUBLE,
583 spe_save_reg_high(rb), frame->fixreg[rb]);
584 result = &fpemu.fe_f3;
588 spe_explode(&fpemu, &fpemu.fe_f1, SINGLE,
589 frame->fixreg[ra], 0);
590 spe_explode(&fpemu, &fpemu.fe_f2, SINGLE,
591 frame->fixreg[rb], 0);
596 mtmsr(msr | PSL_VEC);
597 switch (instr_sec_op) {
599 high = spe_save_reg_high(ra) & ~(1U << 31);
600 frame->fixreg[rd] = frame->fixreg[ra];
601 spe_load_reg_high(rd, high);
604 high = spe_save_reg_high(ra) | (1U << 31);
605 frame->fixreg[rd] = frame->fixreg[ra];
606 spe_load_reg_high(rd, high);
609 high = spe_save_reg_high(ra) ^ (1U << 31);
610 frame->fixreg[rd] = frame->fixreg[ra];
611 spe_load_reg_high(rd, high);
614 spe_explode(&fpemu, &fpemu.fe_f3, SINGLE,
615 frame->fixreg[rb], 0);
616 result = &fpemu.fe_f3;
620 spe_explode(&fpemu, &fpemu.fe_f1, DOUBLE,
621 spe_save_reg_high(ra), frame->fixreg[ra]);
622 spe_explode(&fpemu, &fpemu.fe_f2, DOUBLE,
623 spe_save_reg_high(rb), frame->fixreg[rb]);
628 switch (instr_sec_op) {
631 /* Already handled. */
634 res = spe_emu_instr(instr_sec_op, &fpemu, &result,
641 switch (instr_sec_op & SPE_INST_MASK) {
645 frame->cr &= ~(0xf << cr_shift);
646 frame->cr |= (res << cr_shift);
659 frame->fixreg[rd] = fpu_ftos(&fpemu, result);
662 spe_load_reg_high(rd, fpu_ftod(&fpemu, result, ftod_res));
663 frame->fixreg[rd] = ftod_res[1];
666 panic("Unknown storage width %d", width);
672 spefscr |= (mfspr(SPR_SPEFSCR) & ~SPEFSCR_FINVS);
673 mtspr(SPR_SPEFSCR, spefscr);
681 spe_handle_fpround(struct trapframe *frame)
685 * Punt fpround exceptions for now. This leaves the truncated result in
686 * the register. We'll deal with overflow/underflow later.