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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/asm_linkage.h>
29 #include <sys/trap.h>
30 #include <sys/machpcb.h>
31 #include <sys/machtrap.h>
32 #include <sys/machsig.h>
33 #include <sys/machthread.h>
34
35 #if !defined(lint) && !defined(__lint)
36 #include "assym.h"
37 #endif /* lint */
38
39 /*
40 * Floating point trap handling.
41 *
42 * The FPU is always in a V9 current configuration.
43 *
44 * When a user process is first started via exec,
45 * floating point operations will be disabled by default.
46 * Upon execution of the first floating point instruction,
47 * a fp_disabled trap will be generated; then a word in
48 * the uarea is written signifying use of the floating point
49 * registers so that subsequent context switches will save
50 * and restore the floating point them. The trapped instruction
51 * will be restarted and processing will continue as normal.
52 *
53 * When a operation occurs that the hardware cannot properly
54 * handle, an unfinshed fp_op exception will be generated.
55 * Software routines in the kernel will be executed to
56 * simulate proper handling of such conditions.
57 *
58 * Exception handling will emulate all instructions
59 * in the floating point address queue. Note that there
60 * is no %fq in sun4u, because it has precise FP traps.
61 *
62 * Floating point queues are now machine dependent, and std %fq
63 * is an illegal V9 instruction. The fp_exception code has been
64 * moved to sun4u/ml/machfloat.s.
65 *
66 * NOTE: This code DOES NOT SUPPORT KERNEL (DEVICE DRIVER)
67 * USE OF THE FPU
68 *
69 * Instructions for running without the hardware fpu:
70 * 1. Setting fpu_exists to 0 now only works on a DEBUG kernel.
71 * 2. adb -w unix and set fpu_exists, use_hw_bcopy, use_hw_copyio, and
72 * use_hw_bzero to 0 and rename libc_psr.so.1 in
73 * /usr/platform/sun4u/lib so that it will not get used by
74 * the libc bcopy routines. Then reboot the system and you
75 * should see the bootup message "FPU not in use".
76 * 3. To run kaos, you must comment out the code which sets the
77 * version number of the fsr to 7, in fldst: stfsr/stxfsr
78 * (unless you are running against a comparison system that
79 * has the same fsr version number).
80 * 4. The stqf{a}/ldqf{a} instructions cause kaos errors, for reasons
81 * that appear to be a kaos bug, so don't use them!
82 */
83
84 #if defined(lint) || defined(__lint)
85
86 #ifdef FP_DISABLED
87 int fpu_exists = 0;
88 #else
89 int fpu_exists = 1;
90 #endif
91
92 #else /* lint */
93
94 .section ".data"
95 .align 8
96 fsrholder:
97 .word 0 ! dummy place to write fsr
98 .word 0
99
100 DGDEF(fpu_exists) ! always exists for V9
101 #ifdef FP_DISABLED
102 .word 0
103 #else
104 .word 1 ! sundiag (gack) uses this variable
105 #endif
106
107 DGDEF(fpu_version)
108 .word -1
109
110 #endif /* lint */
111
112 /*
113 * FPU probe - read the %fsr and get fpu_version.
114 * Called from autoconf. If a %fq is created for
115 * future cpu versions, a fq_exists variable
116 * could be created by this function.
117 */
118
119 #if defined(lint) || defined(__lint)
120
121 /*ARGSUSED*/
122 void
123 fpu_probe(void)
124 {}
125
126 #else /* lint */
127
128 ENTRY_NP(fpu_probe)
129 wr %g0, FPRS_FEF, %fprs ! enable fpu in fprs
130 rdpr %pstate, %g2 ! read pstate, save value in %g2
131 or %g2, PSTATE_PEF, %g1 ! new pstate with fpu enabled
132 wrpr %g1, %g0, %pstate ! write pstate
133
134 sethi %hi(fsrholder), %g2
135 stx %fsr, [%g2 + %lo(fsrholder)]
136 ldx [%g2 + %lo(fsrholder)], %g2 ! snarf the FSR
137 set FSR_VER, %g1
138 and %g2, %g1, %g2 ! get version
139 srl %g2, FSR_VER_SHIFT, %g2 ! and shift it down
140 sethi %hi(fpu_version), %g3 ! save the FPU version
141 st %g2, [%g3 + %lo(fpu_version)]
142
143 ba fp_kstat_init ! initialize the fpu_kstat
144 wr %g0, %g0, %fprs ! disable fpu and clear fprs
145 SET_SIZE(fpu_probe)
146
147 #endif /* lint */
148
149 /*
150 * fp_clearregs(fp)
151 * struct v9_fpu *fp;
152 *
153 * Initialization for the hardware fpu.
154 * Clear the fsr and initialize registers to NaN (-1)
155 * The caller (fp_disabled) is supposed to update the fprs
156 * so when the return to userland is made, the fpu is enabled.
157 */
158
159 #if defined(lint) || defined(__lint)
160
161 /*ARGSUSED*/
162 void
163 fp_clearregs(kfpu_t *fp)
164 {}
165
166 #else /* lint */
167
168 ENTRY_NP(fp_clearregs)
169 ldx [%o0 + FPU_FSR], %fsr ! load fsr
170
171 mov -1, %g2 ! -1 is NaN
172 stx %g2, [%o0] ! initialize %f0
173 ldd [%o0], %d0
174 ldd [%o0], %d2
175 ldd [%o0], %d4
176 ldd [%o0], %d6
177 ldd [%o0], %d8
178 ldd [%o0], %d10
179 ldd [%o0], %d12
180 ldd [%o0], %d14
181 ldd [%o0], %d16
182 ldd [%o0], %d18
183 ldd [%o0], %d20
184 ldd [%o0], %d22
185 ldd [%o0], %d24
186 ldd [%o0], %d26
187 ldd [%o0], %d28
188 ldd [%o0], %d30
189 ldd [%o0], %d32
190 ldd [%o0], %d34
191 ldd [%o0], %d36
192 ldd [%o0], %d38
193 ldd [%o0], %d40
194 ldd [%o0], %d42
195 ldd [%o0], %d44
196 ldd [%o0], %d46
197 ldd [%o0], %d48
198 ldd [%o0], %d50
199 ldd [%o0], %d52
200 ldd [%o0], %d54
201 ldd [%o0], %d56
202 ldd [%o0], %d58
203 ldd [%o0], %d60
204 retl
205 ldd [%o0], %d62
206 SET_SIZE(fp_clearregs)
207
208 #endif /* lint */
209
210 /*
211 * void _fp_read_pfreg(pf, n)
212 * uint32_t *pf; Old freg value.
213 * unsigned n; Want to read register n
214 *
215 * {
216 * *pf = %f[n];
217 * }
218 *
219 * void
220 * _fp_write_pfreg(pf, n)
221 * uint32_t *pf; New freg value.
222 * unsigned n; Want to write register n.
223 *
224 * {
225 * %f[n] = *pf;
226 * }
227 */
228
229 #if defined(lint) || defined(__lint)
230
231 /*ARGSUSED*/
232 void
233 _fp_read_pfreg(uint32_t *pf, u_int n)
234 {}
235
236 /*ARGSUSED*/
237 void
238 _fp_write_pfreg(uint32_t *pf, u_int n)
239 {}
240
241 #else /* lint */
242
243 ENTRY_NP(_fp_read_pfreg)
244 sll %o1, 3, %o1 ! Table entries are 8 bytes each.
245 set .stable, %g1 ! g1 gets base of table.
246 jmp %g1 + %o1 ! Jump into table
247 nop ! Can't follow CTI by CTI.
248
249 ENTRY_NP(_fp_write_pfreg)
250 sll %o1, 3, %o1 ! Table entries are 8 bytes each.
251 set .ltable, %g1 ! g1 gets base of table.
252 jmp %g1 + %o1 ! Jump into table
253 nop ! Can't follow CTI by CTI.
254
255 #define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0]
256
257 .stable:
258 STOREFP(0)
259 STOREFP(1)
260 STOREFP(2)
261 STOREFP(3)
262 STOREFP(4)
263 STOREFP(5)
264 STOREFP(6)
265 STOREFP(7)
266 STOREFP(8)
267 STOREFP(9)
268 STOREFP(10)
269 STOREFP(11)
270 STOREFP(12)
271 STOREFP(13)
272 STOREFP(14)
273 STOREFP(15)
274 STOREFP(16)
275 STOREFP(17)
276 STOREFP(18)
277 STOREFP(19)
278 STOREFP(20)
279 STOREFP(21)
280 STOREFP(22)
281 STOREFP(23)
282 STOREFP(24)
283 STOREFP(25)
284 STOREFP(26)
285 STOREFP(27)
286 STOREFP(28)
287 STOREFP(29)
288 STOREFP(30)
289 STOREFP(31)
290
291 #define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n
292
293 .ltable:
294 LOADFP(0)
295 LOADFP(1)
296 LOADFP(2)
297 LOADFP(3)
298 LOADFP(4)
299 LOADFP(5)
300 LOADFP(6)
301 LOADFP(7)
302 LOADFP(8)
303 LOADFP(9)
304 LOADFP(10)
305 LOADFP(11)
306 LOADFP(12)
307 LOADFP(13)
308 LOADFP(14)
309 LOADFP(15)
310 LOADFP(16)
311 LOADFP(17)
312 LOADFP(18)
313 LOADFP(19)
314 LOADFP(20)
315 LOADFP(21)
316 LOADFP(22)
317 LOADFP(23)
318 LOADFP(24)
319 LOADFP(25)
320 LOADFP(26)
321 LOADFP(27)
322 LOADFP(28)
323 LOADFP(29)
324 LOADFP(30)
325 LOADFP(31)
326 SET_SIZE(_fp_read_pfreg)
327 SET_SIZE(_fp_write_pfreg)
328
329 #endif /* lint */
330
331 /*
332 * void _fp_read_pdreg(
333 * uint64_t *pd, Old dreg value.
334 * u_int n) Want to read register n
335 *
336 * {
337 * *pd = %d[n];
338 * }
339 *
340 * void
341 * _fp_write_pdreg(
342 * uint64_t *pd, New dreg value.
343 * u_int n) Want to write register n.
344 *
345 * {
346 * %d[n] = *pd;
347 * }
348 */
349
350 #if defined(lint) || defined(__lint)
351
352 /*ARGSUSED*/
353 void
354 _fp_read_pdreg(uint64_t *pd, u_int n)
355 {}
356
357 /*ARGSUSED*/
358 void
359 _fp_write_pdreg(uint64_t *pd, u_int n)
360 {}
361
362 #else /* lint */
363
364 ENTRY_NP(_fp_read_pdreg)
365 sll %o1, 3, %o1 ! Table entries are 8 bytes each.
366 set .dstable, %g1 ! g1 gets base of table.
367 jmp %g1 + %o1 ! Jump into table
368 nop ! Can't follow CTI by CTI.
369
370 ENTRY_NP(_fp_write_pdreg)
371 sll %o1, 3, %o1 ! Table entries are 8 bytes each.
372 set .dltable, %g1 ! g1 gets base of table.
373 jmp %g1 + %o1 ! Jump into table
374 nop ! Can't follow CTI by CTI.
375
376 #define STOREDP(n) jmp %o7+8 ; std %d/**/n, [%o0]
377
378 .dstable:
379 STOREDP(0)
380 STOREDP(2)
381 STOREDP(4)
382 STOREDP(6)
383 STOREDP(8)
384 STOREDP(10)
385 STOREDP(12)
386 STOREDP(14)
387 STOREDP(16)
388 STOREDP(18)
389 STOREDP(20)
390 STOREDP(22)
391 STOREDP(24)
392 STOREDP(26)
393 STOREDP(28)
394 STOREDP(30)
395 STOREDP(32)
396 STOREDP(34)
397 STOREDP(36)
398 STOREDP(38)
399 STOREDP(40)
400 STOREDP(42)
401 STOREDP(44)
402 STOREDP(46)
403 STOREDP(48)
404 STOREDP(50)
405 STOREDP(52)
406 STOREDP(54)
407 STOREDP(56)
408 STOREDP(58)
409 STOREDP(60)
410 STOREDP(62)
411
412 #define LOADDP(n) jmp %o7+8 ; ldd [%o0],%d/**/n
413
414 .dltable:
415 LOADDP(0)
416 LOADDP(2)
417 LOADDP(4)
418 LOADDP(6)
419 LOADDP(8)
420 LOADDP(10)
421 LOADDP(12)
422 LOADDP(14)
423 LOADDP(16)
424 LOADDP(18)
425 LOADDP(20)
426 LOADDP(22)
427 LOADDP(24)
428 LOADDP(26)
429 LOADDP(28)
430 LOADDP(30)
431 LOADDP(32)
432 LOADDP(34)
433 LOADDP(36)
434 LOADDP(38)
435 LOADDP(40)
436 LOADDP(42)
437 LOADDP(44)
438 LOADDP(46)
439 LOADDP(48)
440 LOADDP(50)
441 LOADDP(52)
442 LOADDP(54)
443 LOADDP(56)
444 LOADDP(58)
445 LOADDP(60)
446 LOADDP(62)
447 SET_SIZE(_fp_read_pdreg)
448 SET_SIZE(_fp_write_pdreg)
449
450 #endif /* lint */
451
452 #if defined(lint) || defined(__lint)
453
454 /*ARGSUSED*/
455 void
456 _fp_write_pfsr(uint64_t *fsr)
457 {}
458
459 #else /* lint */
460
461 ENTRY_NP(_fp_write_pfsr)
462 retl
463 ldx [%o0], %fsr
464 SET_SIZE(_fp_write_pfsr)
465
466 #endif /* lint */
467
468 #if defined(lint) || defined(__lint)
469
470 /*ARGSUSED*/
471 void
472 _fp_read_pfsr(uint64_t *fsr)
473 {}
474
475 #else /* lint */
476
477 ENTRY_NP(_fp_read_pfsr)
478 retl
479 stx %fsr, [%o0]
480 SET_SIZE(_fp_read_pfsr)
481
482 #endif /* lint */
483
484 #if defined(lint) || defined(__lint)
485
486 /*ARGSUSED*/
487 void
488 _fp_write_fprs(u_int fprs_val)
489 {}
490
491 #else /* lint */
492
493 ENTRY_NP(_fp_write_fprs)
494 retl
495 wr %o0, %g0, %fprs ! write fprs
496 SET_SIZE(_fp_write_fprs)
497
498 #endif /* lint */
499
500 #if defined(lint) || defined(__lint)
501
502 unsigned
503 _fp_read_fprs(void)
504 {return 0;}
505
506 #else /* lint */
507
508 ENTRY_NP(_fp_read_fprs)
509 retl
510 rd %fprs, %o0 ! save fprs
511 SET_SIZE(_fp_read_fprs)
512
513 #endif /* lint */
514
515 #if defined(lint) || defined(__lint)
516
517 unsigned
518 _fp_subcc_ccr(void)
519 {return 0;}
520
521 #else /* lint */
522
523 ENTRY_NP(_fp_subcc_ccr)
524 subcc %o0, %o1, %g0
525 retl
526 rd %ccr, %o0 ! save ccr
527 SET_SIZE(_fp_subcc_ccr)
528
529 #endif /* lint */
530
531 /*
532 * Floating Point Exceptions handled according to type:
533 * 2) unfinished_fpop
534 * re-execute the faulty instruction(s) using
535 * software emulation (must do every instruction in FQ)
536 * 3) unimplemented_fpop
537 * an unimplemented instruction, if it is legal,
538 * will cause emulation of the instruction (and all
539 * other instuctions in the FQ)
540 * 4) sequence_error
541 * panic, this should not happen, and if it does it
542 * it is the result of a kernel bug
543 *
544 * This code assumes the trap preamble has set up the window environment
545 * for execution of kernel code.
546 * Note: this code could be changed to be part of the cpu-specific
547 * (ie, Spitfire-specific) module code before final release.
548 */
549
550 #if defined(lint)
551
552 /* ARGSUSED */
553 void
554 _fp_exception(struct regs *rp, uint64_t fsr)
555 {}
556
557 #else /* lint */
558
559 ENTRY_NP(_fp_exception)
560 mov %o7, %l0 ! saved return address
561 mov %o0, %l1 ! saved *rp
562 set FSR_FTT, %o4 ! put FSR_FTT in %o4
563 xor %o4, 0xffffffffffffffff, %o3 ! xor FSR_FTT to get
564 and %o1, %o3, %o2 ! an fsr with a zero'd ftt
565 ldn [THREAD_REG + T_LWP], %o3 ! get lwp
566 ldn [%o3 + LWP_FPU], %l3 ! get lwp_fpu
567 stx %o2, [%l3 + FPU_FSR] ! save floating point status
568 and %o1, %o4, %g2 ! get the ftt trap type
569 #ifdef DEBUG
570 brnz,a,pt %g2, fttok
571 nop
572 set .badfpfttmsg, %o0 ! panic message
573 call panic ! %o1 has the fsr w/ftt value
574 nop
575 fttok:
576 #endif /* DEBUG */
577 srl %g2, FSR_FTT_SHIFT, %o4 ! check ftt
578 cmp %o4, FTT_SEQ ! sanity check for bogus exceptions
579 !
580 ! traps are already enabled to allow other
581 ! interrupts while emulating floating point instructions
582 !
583 blt,a,pt %xcc, fpeok
584 nop
585 !
586 ! Sequence error or unknown ftt exception.
587 !
588 seq_error:
589 set .badfpexcpmsg, %o0 ! panic if bad ftt
590 call panic
591 sra %o4, 0, %o1 ! mov ftt to o1 for panic message
592
593 fpeok:
594 call fp_kstat_update ! fp_kstat_update(ftt)
595 mov %o4, %o0 ! ftt
596 !
597 ! Get the floating point instruction, and run the floating
598 ! point simulator. There is no floating point queue, so we fake one.
599 !
600 call fp_precise ! fp_precise(®s)
601 mov %l1, %o0 ! saved *rp
602
603 fp_ret:
604 rd %fprs, %g1 ! read fprs, save value in %g1
605 st %g1, [%l3 + FPU_FPRS] ! save fprs
606 jmp %l0 + 8 ! jump to saved return address
607 stx %fsr, [%l3 + FPU_FSR] ! save fsr
608 SET_SIZE(_fp_exception)
609
610 .badfpexcpmsg:
611 .asciz "unexpected floating point exception %x"
612
613 #ifdef DEBUG
614 .badfpfttmsg:
615 .asciz "No floating point ftt, fsr %llx"
616 #endif /* DEBUG */
617
618 #endif /* lint */
619
620 /*
621 * Floating Point Exceptions.
622 * handled according to type:
623 * 1) IEEE_exception
624 * re-execute the faulty instruction(s) using
625 * software emulation (must do every instruction in FQ)
626 *
627 * This code assumes the trap preamble has set up the window environment
628 * for execution of kernel code.
629 */
630
631 #if defined(lint)
632
633 /* ARGSUSED */
634 void
635 _fp_ieee_exception(struct regs *rp, uint64_t fsr)
636 {}
637
638 #else /* lint */
639
640 ENTRY_NP(_fp_ieee_exception)
641 mov %o7, %l0 ! saved return address
642 mov %o0, %l1 ! saved *rp
643 mov %o1, %l2 ! saved fsr
644 set FSR_FTT, %o4 ! put FSR_FTT in %o4
645 xor %o4, 0xffffffffffffffff, %o3 ! ! xor FSR_FTT to get
646 and %o1, %o3, %o2 ! an fsr with a zero'd ftt
647 ldn [THREAD_REG + T_LWP], %o3 ! get lwp
648 ldn [%o3 + LWP_FPU], %l3 ! get lwp_fpu
649 stx %o2, [%l3 + FPU_FSR] ! save floating point status
650 stub %g0, [%l3 + FPU_QCNT] ! clear fpu_qcnt
651 and %o1, %o4, %g2 ! mask out trap type
652 #ifdef DEBUG
653 brnz,a,pt %g2, fttgd
654 nop
655 set .badfpfttmsg, %o0 ! panic message
656 call panic ! %o1 has the fsr w/ftt value
657 nop
658 fttgd:
659 #endif /* DEBUG */
660 srl %g2, FSR_FTT_SHIFT, %o4 ! check ftt
661 cmp %o4, FTT_SEQ ! sanity check for bogus exceptions
662 !
663 ! traps are already enabled to allow other
664 ! interrupts while emulating floating point instructions
665 !
666 blt,a,pt %xcc, fpegd
667 nop
668 !
669 ! Sequence error or unknown ftt exception.
670 !
671 seq_err:
672 set .badfpexcpmsg, %o0 ! panic if bad ftt
673 call panic
674 sra %o4, 0, %o1 ! mov ftt to o1 for panic message
675
676 fpegd:
677 call fp_kstat_update ! fp_kstat_update(ftt)
678 mov %o4, %o0 ! ftt
679 !
680 ! Call fpu_trap directly, don't bother to run the fp simulator.
681 ! The *rp is already in %o0. Clear fpu_qcnt.
682 !
683 set (T_FP_EXCEPTION_IEEE), %o2 ! trap type
684
685 set FSR_CEXC, %o3
686 and %l2, %o3, %g2 ! mask out cexc
687
688 andcc %g2, FSR_CEXC_NX, %g0 ! check for inexact
689 bnz,a,pt %xcc, fpok
690 or %g0, FPE_FLTRES, %o3 ! fp inexact code
691
692 andcc %g2, FSR_CEXC_DZ, %g0 ! check for divide-by-zero
693 bnz,a,pt %xcc, fpok
694 or %g0, FPE_FLTDIV, %o3 ! fp divide by zero code
695
696 andcc %g2, FSR_CEXC_UF, %g0 ! check for underflow
697 bnz,a,pt %xcc, fpok
698 or %g0, FPE_FLTUND, %o3 ! fp underflow code
699
700 andcc %g2, FSR_CEXC_OF, %g0 ! check for overflow
701 bnz,a,pt %xcc, fpok
702 or %g0, FPE_FLTOVF, %o3 ! fp overflow code
703
704 andcc %g2, FSR_CEXC_NV, %g0 ! check for invalid
705 bnz,a,pn %xcc, fpok
706 or %g0, FPE_FLTINV, %o3 ! fp invalid code
707
708 cexec_err:
709 set .badfpcexcmsg, %o0 ! panic message
710 call panic ! panic if no cexc bit set
711 mov %g1, %o1
712 fpok:
713 mov %l1, %o0 ! saved *rp
714 call fpu_trap ! fpu_trap(®s, addr, type, code)
715 ldn [%o0 + PC_OFF], %o1 ! address of trapping instruction
716
717 rd %fprs, %g1 ! read fprs, save value in %g1
718 st %g1, [%l3 + FPU_FPRS] ! save fprs
719 jmp %l0 + 8 ! jump to saved return address
720 stx %fsr, [%l3 + FPU_FSR] ! save fsr
721 SET_SIZE(_fp_ieee_exception)
722
723 .badfpcexcmsg:
724 .asciz "No floating point exception, fsr %llx"
725
726 #endif /* lint */