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