File: | obj-scan-build/../i386/i386/fpu.c |
Location: | line 677, column 14 |
Description: | Dereference of null pointer |
1 | /* | |||
2 | * Mach Operating System | |||
3 | * Copyright (c) 1992-1990 Carnegie Mellon University | |||
4 | * All Rights Reserved. | |||
5 | * | |||
6 | * Permission to use, copy, modify and distribute this software and its | |||
7 | * documentation is hereby granted, provided that both the copyright | |||
8 | * notice and this permission notice appear in all copies of the | |||
9 | * software, derivative works or modified versions, and any portions | |||
10 | * thereof, and that both notices appear in supporting documentation. | |||
11 | * | |||
12 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |||
13 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |||
14 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |||
15 | * | |||
16 | * Carnegie Mellon requests users of this software to return to | |||
17 | * | |||
18 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |||
19 | * School of Computer Science | |||
20 | * Carnegie Mellon University | |||
21 | * Pittsburgh PA 15213-3890 | |||
22 | * | |||
23 | * any improvements or extensions that they make and grant Carnegie Mellon | |||
24 | * the rights to redistribute these changes. | |||
25 | */ | |||
26 | ||||
27 | /* | |||
28 | * Copyright (C) 1994 Linus Torvalds | |||
29 | * | |||
30 | * Pentium III FXSR, SSE support | |||
31 | * General FPU state handling cleanups | |||
32 | * Gareth Hughes <gareth@valinux.com>, May 2000 | |||
33 | */ | |||
34 | ||||
35 | /* | |||
36 | * Support for 80387 floating point or FP emulator. | |||
37 | */ | |||
38 | ||||
39 | #include <string.h> | |||
40 | ||||
41 | #include <mach/exception.h> | |||
42 | #include <mach/machine/thread_status.h> | |||
43 | #include <mach/machine/fp_reg.h> | |||
44 | ||||
45 | #include <kern/debug.h> | |||
46 | #include <machine/machspl.h> /* spls */ | |||
47 | #include <kern/printf.h> | |||
48 | #include <kern/thread.h> | |||
49 | #include <kern/slab.h> | |||
50 | ||||
51 | #include <i3861/thread.h> | |||
52 | #include <i3861/fpu.h> | |||
53 | #include <i3861/pio.h> | |||
54 | #include <i3861/pic.h> | |||
55 | #include <i3861/locore.h> | |||
56 | #include <i3861/trap.h> | |||
57 | #include "cpu_number.h" | |||
58 | ||||
59 | #if 0 | |||
60 | #include <i3861/ipl.h> | |||
61 | #define ASSERT_IPL(L) \ | |||
62 | { \ | |||
63 | if (curr_ipl != L) { \ | |||
64 | printf("IPL is %d, expected %d\n", curr_ipl, L); \ | |||
65 | panic("fpu: wrong ipl"); \ | |||
66 | } \ | |||
67 | } | |||
68 | #else | |||
69 | #define ASSERT_IPL(L) | |||
70 | #endif | |||
71 | ||||
72 | int fp_kind = FP_3873; /* 80387 present */ | |||
73 | struct kmem_cache ifps_cache; /* cache for FPU save area */ | |||
74 | static unsigned long mxcsr_feature_mask = 0xffffffff; /* Always AND user-provided mxcsr with this security mask */ | |||
75 | ||||
76 | #if NCPUS1 == 1 | |||
77 | volatile thread_t fp_thread = THREAD_NULL((thread_t) 0); | |||
78 | /* thread whose state is in FPU */ | |||
79 | /* always THREAD_NULL if emulating | |||
80 | FPU */ | |||
81 | volatile thread_t fp_intr_thread = THREAD_NULL((thread_t) 0); | |||
82 | ||||
83 | ||||
84 | #define clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); } \ | |||
85 | { \ | |||
86 | set_ts()({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); \ | |||
87 | fp_thread = THREAD_NULL((thread_t) 0); \ | |||
88 | } | |||
89 | ||||
90 | #else /* NCPUS > 1 */ | |||
91 | #define clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); } \ | |||
92 | { \ | |||
93 | set_ts()({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); \ | |||
94 | } | |||
95 | ||||
96 | #endif | |||
97 | ||||
98 | ||||
99 | /* | |||
100 | * Look for FPU and initialize it. | |||
101 | * Called on each CPU. | |||
102 | */ | |||
103 | void | |||
104 | init_fpu(void) | |||
105 | { | |||
106 | unsigned short status, control; | |||
107 | ||||
108 | #ifdef MACH_RING1 | |||
109 | clear_ts()asm volatile("clts"); | |||
110 | #else /* MACH_RING1 */ | |||
111 | unsigned int native = 0; | |||
112 | ||||
113 | if (machine_slot[cpu_number()(0)].cpu_type >= CPU_TYPE_I486((cpu_type_t) 17)) | |||
114 | native = CR0_NE0x00000020; | |||
115 | ||||
116 | /* | |||
117 | * Check for FPU by initializing it, | |||
118 | * then trying to read the correct bit patterns from | |||
119 | * the control and status registers. | |||
120 | */ | |||
121 | set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | native)({ register unsigned long _temp__ = ((({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) & ~(0x00000004|0x00000008)) | native); asm volatile( "mov %0, %%cr0" : : "r" (_temp__)); }); /* allow use of FPU */ | |||
122 | #endif /* MACH_RING1 */ | |||
123 | ||||
124 | fninit()asm volatile("fninit"); | |||
125 | status = fnstsw()({ unsigned short _status__; asm("fnstsw %0" : "=ma" (_status__ )); _status__; }); | |||
126 | fnstcw(&control)asm("fnstcw %0" : "=m" (*(unsigned short *)(&control))); | |||
127 | ||||
128 | if ((status & 0xff) == 0 && | |||
129 | (control & 0x103f) == 0x3f) | |||
130 | { | |||
131 | /* | |||
132 | * We have a FPU of some sort. | |||
133 | * Compare -infinity against +infinity | |||
134 | * to check whether we have a 287 or a 387. | |||
135 | */ | |||
136 | volatile double fp_infinity, fp_one, fp_zero; | |||
137 | fp_one = 1.0; | |||
138 | fp_zero = 0.0; | |||
139 | fp_infinity = fp_one / fp_zero; | |||
140 | if (fp_infinity == -fp_infinity) { | |||
141 | /* | |||
142 | * We have an 80287. | |||
143 | */ | |||
144 | fp_kind = FP_2872; | |||
145 | asm volatile(".byte 0xdb; .byte 0xe4"); /* fnsetpm */ | |||
146 | } | |||
147 | else { | |||
148 | /* | |||
149 | * We have a 387. | |||
150 | */ | |||
151 | if (CPU_HAS_FEATURE(CPU_FEATURE_FXSR)(cpu_features[(24) / 32] & (1 << ((24) % 32)))) { | |||
152 | static /* because we _need_ alignment */ | |||
153 | struct i386_xfp_save save; | |||
154 | unsigned long mask; | |||
155 | fp_kind = FP_387X4; | |||
156 | #ifndef MACH_RING1 | |||
157 | set_cr4(get_cr4() | CR4_OSFXSR)({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr4, %0" : "=r" (_temp__)); _temp__ ; }) | 0x0200); asm volatile("mov %0, %%cr4" : : "r" (_temp__ )); }); | |||
158 | #endif /* MACH_RING1 */ | |||
159 | fxsave(&save)asm volatile("fxsave %0" : "=m" (*&save)); | |||
160 | mask = save.fp_mxcsr_mask; | |||
161 | if (!mask) | |||
162 | mask = 0x0000ffbf; | |||
163 | mxcsr_feature_mask &= mask; | |||
164 | } else | |||
165 | fp_kind = FP_3873; | |||
166 | } | |||
167 | #ifdef MACH_RING1 | |||
168 | set_ts()({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); | |||
169 | #else /* MACH_RING1 */ | |||
170 | /* | |||
171 | * Trap wait instructions. Turn off FPU for now. | |||
172 | */ | |||
173 | set_cr0(get_cr0() | CR0_TS | CR0_MP)({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008 | 0x00000002); asm volatile("mov %0, %%cr0" : : "r" (_temp__)); }); | |||
174 | #endif /* MACH_RING1 */ | |||
175 | } | |||
176 | else { | |||
177 | /* | |||
178 | * NO FPU. | |||
179 | */ | |||
180 | panic("No FPU!"); | |||
181 | } | |||
182 | } | |||
183 | ||||
184 | /* | |||
185 | * Initialize FP handling. | |||
186 | */ | |||
187 | void | |||
188 | fpu_module_init(void) | |||
189 | { | |||
190 | kmem_cache_init(&ifps_cache, "i386_fpsave_state", | |||
191 | sizeof(struct i386_fpsave_state), 16, | |||
192 | NULL((void *) 0), NULL((void *) 0), NULL((void *) 0), 0); | |||
193 | } | |||
194 | ||||
195 | /* | |||
196 | * Free a FPU save area. | |||
197 | * Called only when thread terminating - no locking necessary. | |||
198 | */ | |||
199 | void | |||
200 | fp_free(fps) | |||
201 | struct i386_fpsave_state *fps; | |||
202 | { | |||
203 | ASSERT_IPL(SPL0); | |||
204 | #if NCPUS1 == 1 | |||
205 | if ((fp_thread != THREAD_NULL((thread_t) 0)) && (fp_thread->pcb->ims.ifps == fps)) { | |||
206 | /* | |||
207 | * Make sure we don't get FPU interrupts later for | |||
208 | * this thread | |||
209 | */ | |||
210 | clear_ts()asm volatile("clts"); | |||
211 | fwait()asm("fwait");; | |||
212 | ||||
213 | /* Mark it free and disable access */ | |||
214 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
215 | } | |||
216 | #endif /* NCPUS == 1 */ | |||
217 | kmem_cache_free(&ifps_cache, (vm_offset_t) fps); | |||
218 | } | |||
219 | ||||
220 | /* The two following functions were stolen from Linux's i387.c */ | |||
221 | static inline unsigned short | |||
222 | twd_i387_to_fxsr (unsigned short twd) | |||
223 | { | |||
224 | unsigned int tmp; /* to avoid 16 bit prefixes in the code */ | |||
225 | ||||
226 | /* Transform each pair of bits into 01 (valid) or 00 (empty) */ | |||
227 | tmp = ~twd; | |||
228 | tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ | |||
229 | /* and move the valid bits to the lower byte. */ | |||
230 | tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ | |||
231 | tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ | |||
232 | tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ | |||
233 | return tmp; | |||
234 | } | |||
235 | ||||
236 | static inline unsigned long | |||
237 | twd_fxsr_to_i387 (struct i386_xfp_save *fxsave) | |||
238 | { | |||
239 | struct { | |||
240 | unsigned short significand[4]; | |||
241 | unsigned short exponent; | |||
242 | unsigned short padding[3]; | |||
243 | } *st = NULL((void *) 0); | |||
244 | unsigned long tos = (fxsave->fp_status >> 11) & 7; | |||
245 | unsigned long twd = (unsigned long) fxsave->fp_tag; | |||
246 | unsigned long tag; | |||
247 | unsigned long ret = 0xffff0000u; | |||
248 | int i; | |||
249 | ||||
250 | #define FPREG_ADDR(f, n)((void *)&(f)->fp_reg_word + (n) * 16); ((void *)&(f)->fp_reg_word + (n) * 16); | |||
251 | ||||
252 | for (i = 0 ; i < 8 ; i++) { | |||
253 | if (twd & 0x1) { | |||
254 | st = FPREG_ADDR (fxsave, (i - tos) & 7)((void *)&(fxsave)->fp_reg_word + ((i - tos) & 7) * 16);; | |||
255 | ||||
256 | switch (st->exponent & 0x7fff) { | |||
257 | case 0x7fff: | |||
258 | tag = 2; /* Special */ | |||
259 | break; | |||
260 | case 0x0000: | |||
261 | if (!st->significand[0] && | |||
262 | !st->significand[1] && | |||
263 | !st->significand[2] && | |||
264 | !st->significand[3] ) { | |||
265 | tag = 1; /* Zero */ | |||
266 | } else { | |||
267 | tag = 2; /* Special */ | |||
268 | } | |||
269 | break; | |||
270 | default: | |||
271 | if (st->significand[3] & 0x8000) { | |||
272 | tag = 0; /* Valid */ | |||
273 | } else { | |||
274 | tag = 2; /* Special */ | |||
275 | } | |||
276 | break; | |||
277 | } | |||
278 | } else { | |||
279 | tag = 3; /* Empty */ | |||
280 | } | |||
281 | ret |= (tag << (2 * i)); | |||
282 | twd = twd >> 1; | |||
283 | } | |||
284 | return ret; | |||
285 | } | |||
286 | ||||
287 | /* | |||
288 | * Set the floating-point state for a thread. | |||
289 | * If the thread is not the current thread, it is | |||
290 | * not running (held). Locking needed against | |||
291 | * concurrent fpu_set_state or fpu_get_state. | |||
292 | */ | |||
293 | kern_return_t | |||
294 | fpu_set_state(thread, state) | |||
295 | const thread_t thread; | |||
296 | struct i386_float_state *state; | |||
297 | { | |||
298 | pcb_t pcb = thread->pcb; | |||
299 | struct i386_fpsave_state *ifps; | |||
300 | struct i386_fpsave_state *new_ifps; | |||
301 | ||||
302 | ASSERT_IPL(SPL0); | |||
303 | if (fp_kind == FP_NO0) | |||
304 | return KERN_FAILURE5; | |||
305 | ||||
306 | #if NCPUS1 == 1 | |||
307 | ||||
308 | /* | |||
309 | * If this thread`s state is in the FPU, | |||
310 | * discard it; we are replacing the entire | |||
311 | * FPU state. | |||
312 | */ | |||
313 | if (fp_thread == thread) { | |||
314 | clear_ts()asm volatile("clts"); | |||
315 | fwait()asm("fwait");; /* wait for possible interrupt */ | |||
316 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; /* no state in FPU */ | |||
317 | } | |||
318 | #endif | |||
319 | ||||
320 | if (state->initialized == 0) { | |||
321 | /* | |||
322 | * new FPU state is 'invalid'. | |||
323 | * Deallocate the fp state if it exists. | |||
324 | */ | |||
325 | simple_lock(&pcb->lock); | |||
326 | ifps = pcb->ims.ifps; | |||
327 | pcb->ims.ifps = 0; | |||
328 | simple_unlock(&pcb->lock)((void)(&pcb->lock)); | |||
329 | ||||
330 | if (ifps != 0) { | |||
331 | kmem_cache_free(&ifps_cache, (vm_offset_t) ifps); | |||
332 | } | |||
333 | } | |||
334 | else { | |||
335 | /* | |||
336 | * Valid state. Allocate the fp state if there is none. | |||
337 | */ | |||
338 | struct i386_fp_save *user_fp_state; | |||
339 | struct i386_fp_regs *user_fp_regs; | |||
340 | ||||
341 | user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; | |||
342 | user_fp_regs = (struct i386_fp_regs *) | |||
343 | &state->hw_state[sizeof(struct i386_fp_save)]; | |||
344 | ||||
345 | new_ifps = 0; | |||
346 | Retry: | |||
347 | simple_lock(&pcb->lock); | |||
348 | ifps = pcb->ims.ifps; | |||
349 | if (ifps == 0) { | |||
350 | if (new_ifps == 0) { | |||
351 | simple_unlock(&pcb->lock)((void)(&pcb->lock)); | |||
352 | new_ifps = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache); | |||
353 | goto Retry; | |||
354 | } | |||
355 | ifps = new_ifps; | |||
356 | new_ifps = 0; | |||
357 | pcb->ims.ifps = ifps; | |||
358 | } | |||
359 | ||||
360 | /* | |||
361 | * Ensure that reserved parts of the environment are 0. | |||
362 | */ | |||
363 | memset(&ifps->fp_save_state, 0, sizeof(struct i386_fp_save)); | |||
364 | ||||
365 | if (fp_kind == FP_387X4) { | |||
366 | int i; | |||
367 | ||||
368 | ifps->xfp_save_state.fp_control = user_fp_state->fp_control; | |||
369 | ifps->xfp_save_state.fp_status = user_fp_state->fp_status; | |||
370 | ifps->xfp_save_state.fp_tag = twd_i387_to_fxsr(user_fp_state->fp_tag); | |||
371 | ifps->xfp_save_state.fp_eip = user_fp_state->fp_eip; | |||
372 | ifps->xfp_save_state.fp_cs = user_fp_state->fp_cs; | |||
373 | ifps->xfp_save_state.fp_opcode = user_fp_state->fp_opcode; | |||
374 | ifps->xfp_save_state.fp_dp = user_fp_state->fp_dp; | |||
375 | ifps->xfp_save_state.fp_ds = user_fp_state->fp_ds; | |||
376 | for (i=0; i<8; i++) | |||
377 | memcpy(&ifps->xfp_save_state.fp_reg_word[i], &user_fp_regs->fp_reg_word[i], sizeof(user_fp_regs->fp_reg_word[i])); | |||
378 | } else { | |||
379 | ifps->fp_save_state.fp_control = user_fp_state->fp_control; | |||
380 | ifps->fp_save_state.fp_status = user_fp_state->fp_status; | |||
381 | ifps->fp_save_state.fp_tag = user_fp_state->fp_tag; | |||
382 | ifps->fp_save_state.fp_eip = user_fp_state->fp_eip; | |||
383 | ifps->fp_save_state.fp_cs = user_fp_state->fp_cs; | |||
384 | ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode; | |||
385 | ifps->fp_save_state.fp_dp = user_fp_state->fp_dp; | |||
386 | ifps->fp_save_state.fp_ds = user_fp_state->fp_ds; | |||
387 | ifps->fp_regs = *user_fp_regs; | |||
388 | } | |||
389 | ||||
390 | simple_unlock(&pcb->lock)((void)(&pcb->lock)); | |||
391 | if (new_ifps != 0) | |||
392 | kmem_cache_free(&ifps_cache, (vm_offset_t) new_ifps); | |||
393 | } | |||
394 | ||||
395 | return KERN_SUCCESS0; | |||
396 | } | |||
397 | ||||
398 | /* | |||
399 | * Get the floating-point state for a thread. | |||
400 | * If the thread is not the current thread, it is | |||
401 | * not running (held). Locking needed against | |||
402 | * concurrent fpu_set_state or fpu_get_state. | |||
403 | */ | |||
404 | kern_return_t | |||
405 | fpu_get_state(thread, state) | |||
406 | const thread_t thread; | |||
407 | struct i386_float_state *state; | |||
408 | { | |||
409 | pcb_t pcb = thread->pcb; | |||
410 | struct i386_fpsave_state *ifps; | |||
411 | ||||
412 | ASSERT_IPL(SPL0); | |||
413 | if (fp_kind == FP_NO0) | |||
414 | return KERN_FAILURE5; | |||
415 | ||||
416 | simple_lock(&pcb->lock); | |||
417 | ifps = pcb->ims.ifps; | |||
418 | if (ifps == 0) { | |||
419 | /* | |||
420 | * No valid floating-point state. | |||
421 | */ | |||
422 | simple_unlock(&pcb->lock)((void)(&pcb->lock)); | |||
423 | memset(state, 0, sizeof(struct i386_float_state)); | |||
424 | return KERN_SUCCESS0; | |||
425 | } | |||
426 | ||||
427 | /* Make sure we`ve got the latest fp state info */ | |||
428 | /* If the live fpu state belongs to our target */ | |||
429 | #if NCPUS1 == 1 | |||
430 | if (thread == fp_thread) | |||
431 | #else | |||
432 | if (thread == current_thread()(active_threads[(0)])) | |||
433 | #endif | |||
434 | { | |||
435 | clear_ts()asm volatile("clts"); | |||
436 | fp_save(thread); | |||
437 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
438 | } | |||
439 | ||||
440 | state->fpkind = fp_kind; | |||
441 | state->exc_status = 0; | |||
442 | ||||
443 | { | |||
444 | struct i386_fp_save *user_fp_state; | |||
445 | struct i386_fp_regs *user_fp_regs; | |||
446 | ||||
447 | state->initialized = ifps->fp_valid; | |||
448 | ||||
449 | user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; | |||
450 | user_fp_regs = (struct i386_fp_regs *) | |||
451 | &state->hw_state[sizeof(struct i386_fp_save)]; | |||
452 | ||||
453 | /* | |||
454 | * Ensure that reserved parts of the environment are 0. | |||
455 | */ | |||
456 | memset(user_fp_state, 0, sizeof(struct i386_fp_save)); | |||
457 | ||||
458 | if (fp_kind == FP_387X4) { | |||
459 | int i; | |||
460 | ||||
461 | user_fp_state->fp_control = ifps->xfp_save_state.fp_control; | |||
462 | user_fp_state->fp_status = ifps->xfp_save_state.fp_status; | |||
463 | user_fp_state->fp_tag = twd_fxsr_to_i387(&ifps->xfp_save_state); | |||
464 | user_fp_state->fp_eip = ifps->xfp_save_state.fp_eip; | |||
465 | user_fp_state->fp_cs = ifps->xfp_save_state.fp_cs; | |||
466 | user_fp_state->fp_opcode = ifps->xfp_save_state.fp_opcode; | |||
467 | user_fp_state->fp_dp = ifps->xfp_save_state.fp_dp; | |||
468 | user_fp_state->fp_ds = ifps->xfp_save_state.fp_ds; | |||
469 | for (i=0; i<8; i++) | |||
470 | memcpy(&user_fp_regs->fp_reg_word[i], &ifps->xfp_save_state.fp_reg_word[i], sizeof(user_fp_regs->fp_reg_word[i])); | |||
471 | } else { | |||
472 | user_fp_state->fp_control = ifps->fp_save_state.fp_control; | |||
473 | user_fp_state->fp_status = ifps->fp_save_state.fp_status; | |||
474 | user_fp_state->fp_tag = ifps->fp_save_state.fp_tag; | |||
475 | user_fp_state->fp_eip = ifps->fp_save_state.fp_eip; | |||
476 | user_fp_state->fp_cs = ifps->fp_save_state.fp_cs; | |||
477 | user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode; | |||
478 | user_fp_state->fp_dp = ifps->fp_save_state.fp_dp; | |||
479 | user_fp_state->fp_ds = ifps->fp_save_state.fp_ds; | |||
480 | *user_fp_regs = ifps->fp_regs; | |||
481 | } | |||
482 | } | |||
483 | simple_unlock(&pcb->lock)((void)(&pcb->lock)); | |||
484 | ||||
485 | return KERN_SUCCESS0; | |||
486 | } | |||
487 | ||||
488 | /* | |||
489 | * Initialize FPU. | |||
490 | * | |||
491 | * Raise exceptions for: | |||
492 | * invalid operation | |||
493 | * divide by zero | |||
494 | * overflow | |||
495 | * | |||
496 | * Use 53-bit precision. | |||
497 | */ | |||
498 | void fpinit(void) | |||
499 | { | |||
500 | unsigned short control; | |||
501 | ||||
502 | ASSERT_IPL(SPL0); | |||
503 | clear_ts()asm volatile("clts"); | |||
504 | fninit()asm volatile("fninit"); | |||
505 | fnstcw(&control)asm("fnstcw %0" : "=m" (*(unsigned short *)(&control))); | |||
506 | control &= ~(FPC_PC0x0300|FPC_RC0x0c00); /* Clear precision & rounding control */ | |||
507 | control |= (FPC_PC_530x0200 | /* Set precision */ | |||
508 | FPC_RC_RN0x0000 | /* round-to-nearest */ | |||
509 | FPC_ZE0x0004 | /* Suppress zero-divide */ | |||
510 | FPC_OE0x0008 | /* and overflow */ | |||
511 | FPC_UE0x0010 | /* underflow */ | |||
512 | FPC_IE0x0001 | /* Allow NaNQs and +-INF */ | |||
513 | FPC_DE0x0002 | /* Allow denorms as operands */ | |||
514 | FPC_PE0x0020); /* No trap for precision loss */ | |||
515 | fldcw(control)asm volatile("fldcw %0" : : "m" (*(unsigned short *) &(control )) ); | |||
516 | } | |||
517 | ||||
518 | /* | |||
519 | * Coprocessor not present. | |||
520 | */ | |||
521 | void | |||
522 | fpnoextflt(void) | |||
523 | { | |||
524 | /* | |||
525 | * Enable FPU use. | |||
526 | */ | |||
527 | ASSERT_IPL(SPL0); | |||
528 | clear_ts()asm volatile("clts"); | |||
529 | #if NCPUS1 == 1 | |||
530 | ||||
531 | /* | |||
532 | * If this thread`s state is in the FPU, we are done. | |||
533 | */ | |||
534 | if (fp_thread == current_thread()(active_threads[(0)])) | |||
535 | return; | |||
536 | ||||
537 | /* Make sure we don't do fpsave() in fp_intr while doing fpsave() | |||
538 | * here if the current fpu instruction generates an error. | |||
539 | */ | |||
540 | fwait()asm("fwait");; | |||
541 | /* | |||
542 | * If another thread`s state is in the FPU, save it. | |||
543 | */ | |||
544 | if (fp_thread != THREAD_NULL((thread_t) 0)) { | |||
545 | fp_save(fp_thread); | |||
546 | } | |||
547 | ||||
548 | /* | |||
549 | * Give this thread the FPU. | |||
550 | */ | |||
551 | fp_thread = current_thread()(active_threads[(0)]); | |||
552 | ||||
553 | #endif /* NCPUS == 1 */ | |||
554 | ||||
555 | /* | |||
556 | * Load this thread`s state into the FPU. | |||
557 | */ | |||
558 | fp_load(current_thread()(active_threads[(0)])); | |||
559 | } | |||
560 | ||||
561 | /* | |||
562 | * FPU overran end of segment. | |||
563 | * Re-initialize FPU. Floating point state is not valid. | |||
564 | */ | |||
565 | void | |||
566 | fpextovrflt(void) | |||
567 | { | |||
568 | thread_t thread = current_thread()(active_threads[(0)]); | |||
569 | pcb_t pcb; | |||
570 | struct i386_fpsave_state *ifps; | |||
571 | ||||
572 | #if NCPUS1 == 1 | |||
573 | ||||
574 | /* | |||
575 | * Is exception for the currently running thread? | |||
576 | */ | |||
577 | if (fp_thread != thread) { | |||
578 | /* Uh oh... */ | |||
579 | panic("fpextovrflt"); | |||
580 | } | |||
581 | #endif | |||
582 | ||||
583 | /* | |||
584 | * This is a non-recoverable error. | |||
585 | * Invalidate the thread`s FPU state. | |||
586 | */ | |||
587 | pcb = thread->pcb; | |||
588 | simple_lock(&pcb->lock); | |||
589 | ifps = pcb->ims.ifps; | |||
590 | pcb->ims.ifps = 0; | |||
591 | simple_unlock(&pcb->lock)((void)(&pcb->lock)); | |||
592 | ||||
593 | /* | |||
594 | * Re-initialize the FPU. | |||
595 | */ | |||
596 | clear_ts()asm volatile("clts"); | |||
597 | fninit()asm volatile("fninit"); | |||
598 | ||||
599 | /* | |||
600 | * And disable access. | |||
601 | */ | |||
602 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
603 | ||||
604 | if (ifps) | |||
605 | kmem_cache_free(&ifps_cache, (vm_offset_t) ifps); | |||
606 | ||||
607 | /* | |||
608 | * Raise exception. | |||
609 | */ | |||
610 | i386_exception(EXC_BAD_ACCESS1, VM_PROT_READ((vm_prot_t) 0x01)|VM_PROT_EXECUTE((vm_prot_t) 0x04), 0); | |||
611 | /*NOTREACHED*/ | |||
612 | } | |||
613 | ||||
614 | static int | |||
615 | fphandleerr(void) | |||
616 | { | |||
617 | thread_t thread = current_thread()(active_threads[(0)]); | |||
618 | ||||
619 | /* | |||
620 | * Save the FPU context to the thread using it. | |||
621 | */ | |||
622 | #if NCPUS1 == 1 | |||
623 | if (fp_thread == THREAD_NULL((thread_t) 0)) { | |||
624 | printf("fphandleerr: FPU not belonging to anyone!\n"); | |||
625 | clear_ts()asm volatile("clts"); | |||
626 | fninit()asm volatile("fninit"); | |||
627 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
628 | return 1; | |||
629 | } | |||
630 | ||||
631 | if (fp_thread != thread) { | |||
632 | /* | |||
633 | * FPU exception is for a different thread. | |||
634 | * When that thread again uses the FPU an exception will be | |||
635 | * raised in fp_load. Remember the condition in fp_valid (== 2). | |||
636 | */ | |||
637 | clear_ts()asm volatile("clts"); | |||
638 | fp_save(fp_thread); | |||
639 | fp_thread->pcb->ims.ifps->fp_valid = 2; | |||
640 | fninit()asm volatile("fninit"); | |||
641 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
642 | /* leave fp_intr_thread THREAD_NULL */ | |||
643 | return 1; | |||
644 | } | |||
645 | #endif /* NCPUS == 1 */ | |||
646 | ||||
647 | /* | |||
648 | * Save the FPU state and turn off the FPU. | |||
649 | */ | |||
650 | clear_ts()asm volatile("clts"); | |||
651 | fp_save(thread); | |||
652 | fninit()asm volatile("fninit"); | |||
653 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
654 | ||||
655 | return 0; | |||
656 | } | |||
657 | ||||
658 | /* | |||
659 | * FPU error. Called by exception handler. | |||
660 | */ | |||
661 | void | |||
662 | fpexterrflt(void) | |||
663 | { | |||
664 | thread_t thread = current_thread()(active_threads[(0)]); | |||
665 | ||||
666 | if (fphandleerr()) | |||
| ||||
667 | return; | |||
668 | ||||
669 | /* | |||
670 | * Raise FPU exception. | |||
671 | * Locking not needed on pcb->ims.ifps, | |||
672 | * since thread is running. | |||
673 | */ | |||
674 | i386_exception(EXC_ARITHMETIC3, | |||
675 | EXC_I386_EXTERR5, | |||
676 | fp_kind == FP_387X4 ? | |||
677 | thread->pcb->ims.ifps->xfp_save_state.fp_status : | |||
| ||||
678 | thread->pcb->ims.ifps->fp_save_state.fp_status); | |||
679 | /*NOTREACHED*/ | |||
680 | } | |||
681 | ||||
682 | #ifndef MACH_RING1 | |||
683 | /* | |||
684 | * FPU error. Called by AST. | |||
685 | */ | |||
686 | void | |||
687 | fpastintr(void) | |||
688 | { | |||
689 | thread_t thread = current_thread()(active_threads[(0)]); | |||
690 | ||||
691 | ASSERT_IPL(SPL0); | |||
692 | #if NCPUS1 == 1 | |||
693 | /* | |||
694 | * Since FPU errors only occur on ESC or WAIT instructions, | |||
695 | * the current thread should own the FPU. If it didn`t, | |||
696 | * we should have gotten the task-switched interrupt first. | |||
697 | */ | |||
698 | if (fp_thread != THREAD_NULL((thread_t) 0)) { | |||
699 | panic("fpexterrflt"); | |||
700 | return; | |||
701 | } | |||
702 | ||||
703 | /* | |||
704 | * Check if we got a context switch between the interrupt and the AST | |||
705 | * This can happen if the interrupt arrived after the FPU AST was | |||
706 | * checked. In this case, raise the exception in fp_load when this | |||
707 | * thread next time uses the FPU. Remember exception condition in | |||
708 | * fp_valid (extended boolean 2). | |||
709 | */ | |||
710 | if (fp_intr_thread != thread) { | |||
711 | if (fp_intr_thread == THREAD_NULL((thread_t) 0)) { | |||
712 | panic("fpexterrflt: fp_intr_thread == THREAD_NULL"); | |||
713 | return; | |||
714 | } | |||
715 | fp_intr_thread->pcb->ims.ifps->fp_valid = 2; | |||
716 | fp_intr_thread = THREAD_NULL((thread_t) 0); | |||
717 | return; | |||
718 | } | |||
719 | fp_intr_thread = THREAD_NULL((thread_t) 0); | |||
720 | #else /* NCPUS == 1 */ | |||
721 | /* | |||
722 | * Save the FPU state and turn off the FPU. | |||
723 | */ | |||
724 | fp_save(thread); | |||
725 | #endif /* NCPUS == 1 */ | |||
726 | ||||
727 | /* | |||
728 | * Raise FPU exception. | |||
729 | * Locking not needed on pcb->ims.ifps, | |||
730 | * since thread is running. | |||
731 | */ | |||
732 | i386_exception(EXC_ARITHMETIC3, | |||
733 | EXC_I386_EXTERR5, | |||
734 | fp_kind == FP_387X4 ? | |||
735 | thread->pcb->ims.ifps->xfp_save_state.fp_status : | |||
736 | thread->pcb->ims.ifps->fp_save_state.fp_status); | |||
737 | /*NOTREACHED*/ | |||
738 | } | |||
739 | #endif /* MACH_RING1 */ | |||
740 | ||||
741 | /* | |||
742 | * Save FPU state. | |||
743 | * | |||
744 | * Locking not needed: | |||
745 | * . if called from fpu_get_state, pcb already locked. | |||
746 | * . if called from fpnoextflt or fp_intr, we are single-cpu | |||
747 | * . otherwise, thread is running. | |||
748 | */ | |||
749 | void | |||
750 | fp_save(thread) | |||
751 | thread_t thread; | |||
752 | { | |||
753 | pcb_t pcb = thread->pcb; | |||
754 | struct i386_fpsave_state *ifps = pcb->ims.ifps; | |||
755 | ||||
756 | if (ifps != 0 && !ifps->fp_valid) { | |||
757 | /* registers are in FPU */ | |||
758 | ifps->fp_valid = TRUE((boolean_t) 1); | |||
759 | if (fp_kind == FP_387X4) | |||
760 | fxsave(&ifps->xfp_save_state)asm volatile("fxsave %0" : "=m" (*&ifps->xfp_save_state )); | |||
761 | else | |||
762 | fnsave(&ifps->fp_save_state)asm volatile("fnsave %0" : "=m" (*&ifps->fp_save_state )); | |||
763 | } | |||
764 | } | |||
765 | ||||
766 | /* | |||
767 | * Restore FPU state from PCB. | |||
768 | * | |||
769 | * Locking not needed; always called on the current thread. | |||
770 | */ | |||
771 | void | |||
772 | fp_load(thread) | |||
773 | thread_t thread; | |||
774 | { | |||
775 | pcb_t pcb = thread->pcb; | |||
776 | struct i386_fpsave_state *ifps; | |||
777 | ||||
778 | ASSERT_IPL(SPL0); | |||
779 | ifps = pcb->ims.ifps; | |||
780 | if (ifps == 0) { | |||
781 | ifps = (struct i386_fpsave_state *) kmem_cache_alloc(&ifps_cache); | |||
782 | memset(ifps, 0, sizeof *ifps); | |||
783 | pcb->ims.ifps = ifps; | |||
784 | fpinit(); | |||
785 | #if 1 | |||
786 | /* | |||
787 | * I'm not sure this is needed. Does the fpu regenerate the interrupt in | |||
788 | * frstor or not? Without this code we may miss some exceptions, with it | |||
789 | * we might send too many exceptions. | |||
790 | */ | |||
791 | } else if (ifps->fp_valid == 2) { | |||
792 | /* delayed exception pending */ | |||
793 | ||||
794 | ifps->fp_valid = TRUE((boolean_t) 1); | |||
795 | clear_fpu(){ ({ register unsigned long _temp__ = (({ register unsigned long _temp__; asm volatile("mov %%cr0, %0" : "=r" (_temp__)); _temp__ ; }) | 0x00000008); asm volatile("mov %0, %%cr0" : : "r" (_temp__ )); }); fp_thread = ((thread_t) 0); }; | |||
796 | /* | |||
797 | * Raise FPU exception. | |||
798 | * Locking not needed on pcb->ims.ifps, | |||
799 | * since thread is running. | |||
800 | */ | |||
801 | i386_exception(EXC_ARITHMETIC3, | |||
802 | EXC_I386_EXTERR5, | |||
803 | fp_kind == FP_387X4 ? | |||
804 | thread->pcb->ims.ifps->xfp_save_state.fp_status : | |||
805 | thread->pcb->ims.ifps->fp_save_state.fp_status); | |||
806 | /*NOTREACHED*/ | |||
807 | #endif | |||
808 | } else if (! ifps->fp_valid) { | |||
809 | printf("fp_load: invalid FPU state!\n"); | |||
810 | fninit ()asm volatile("fninit"); | |||
811 | } else { | |||
812 | if (fp_kind == FP_387X4) | |||
813 | fxrstor(ifps->xfp_save_state)asm volatile("fxrstor %0" : : "m" (ifps->xfp_save_state)); | |||
814 | else | |||
815 | frstor(ifps->fp_save_state)asm volatile("frstor %0" : : "m" (ifps->fp_save_state)); | |||
816 | } | |||
817 | ifps->fp_valid = FALSE((boolean_t) 0); /* in FPU */ | |||
818 | } | |||
819 | ||||
820 | /* | |||
821 | * Allocate and initialize FP state for current thread. | |||
822 | * Don't load state. | |||
823 | * | |||
824 | * Locking not needed; always called on the current thread. | |||
825 | */ | |||
826 | void | |||
827 | fp_state_alloc(void) | |||
828 | { | |||
829 | pcb_t pcb = current_thread()(active_threads[(0)])->pcb; | |||
830 | struct i386_fpsave_state *ifps; | |||
831 | ||||
832 | ifps = (struct i386_fpsave_state *)kmem_cache_alloc(&ifps_cache); | |||
833 | memset(ifps, 0, sizeof *ifps); | |||
834 | pcb->ims.ifps = ifps; | |||
835 | ||||
836 | ifps->fp_valid = TRUE((boolean_t) 1); | |||
837 | ||||
838 | if (fp_kind == FP_387X4) { | |||
839 | ifps->xfp_save_state.fp_control = (0x037f | |||
840 | & ~(FPC_IM0x0001|FPC_ZM0x0004|FPC_OM0x0008|FPC_PC0x0300)) | |||
841 | | (FPC_PC_530x0200|FPC_IC_AFF0x1000); | |||
842 | ifps->xfp_save_state.fp_status = 0; | |||
843 | ifps->xfp_save_state.fp_tag = 0xffff; /* all empty */ | |||
844 | if (CPU_HAS_FEATURE(CPU_FEATURE_SSE)(cpu_features[(25) / 32] & (1 << ((25) % 32)))) | |||
845 | ifps->xfp_save_state.fp_mxcsr = 0x1f80; | |||
846 | } else { | |||
847 | ifps->fp_save_state.fp_control = (0x037f | |||
848 | & ~(FPC_IM0x0001|FPC_ZM0x0004|FPC_OM0x0008|FPC_PC0x0300)) | |||
849 | | (FPC_PC_530x0200|FPC_IC_AFF0x1000); | |||
850 | ifps->fp_save_state.fp_status = 0; | |||
851 | ifps->fp_save_state.fp_tag = 0xffff; /* all empty */ | |||
852 | } | |||
853 | } | |||
854 | ||||
855 | #if AT3861 && !defined(MACH_XEN) | |||
856 | /* | |||
857 | * Handle a coprocessor error interrupt on the AT386. | |||
858 | * This comes in on line 5 of the slave PIC at SPL1. | |||
859 | */ | |||
860 | void | |||
861 | fpintr(int unit) | |||
862 | { | |||
863 | spl_t s; | |||
864 | thread_t thread = current_thread()(active_threads[(0)]); | |||
865 | ||||
866 | ASSERT_IPL(SPL1); | |||
867 | /* | |||
868 | * Turn off the extended 'busy' line. | |||
869 | */ | |||
870 | outb(0xf0, 0){ asm volatile("outb %0, %1" : : "a" ((unsigned char)(0)) , "d" ((unsigned short)(0xf0))); }; | |||
871 | ||||
872 | if (fphandleerr()) | |||
873 | return; | |||
874 | ||||
875 | #if NCPUS1 == 1 | |||
876 | if (fp_intr_thread != THREAD_NULL((thread_t) 0) && fp_intr_thread != thread) | |||
877 | panic("fp_intr: already caught intr"); | |||
878 | fp_intr_thread = thread; | |||
879 | #endif /* NCPUS == 1 */ | |||
880 | ||||
881 | /* | |||
882 | * Since we are running on the interrupt stack, we must | |||
883 | * signal the thread to take the exception when we return | |||
884 | * to user mode. Use an AST to do this. | |||
885 | * | |||
886 | * Don`t set the thread`s AST field. If the thread is | |||
887 | * descheduled before it takes the AST, it will notice | |||
888 | * the FPU error when it reloads its FPU state. | |||
889 | */ | |||
890 | s = splsched(); | |||
891 | ast_on(cpu_number(), AST_I386_FP)({ if ((need_ast[(0)] |= (0x80000000)) != 0x0) { ; } }); | |||
892 | splx(s); | |||
893 | } | |||
894 | #endif /* AT386 */ |