1 /*
2 * Mach Operating System
3 * Copyright (c) 1991 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 * HISTORY
29 * $Log: fpe_linkage.c,v $
30 * Revision 2.4 93/01/14 17:28:53 danner
31 * Added include of mach/std_types.h.
32 * [92/12/04 af]
33 *
34 * Revision 2.3 92/03/05 15:39:05 rpd
35 * Fixed disable_fpe to clear fs & gs.
36 * [92/03/05 rpd]
37 *
38 * Revision 2.2 92/01/03 20:05:28 dbg
39 * Changed trap gate for interrupt 7 to PL_Kernel. The privilege level
40 * affects INT $7 instructions, not coprocessor-not-present traps.
41 * [91/12/19 dbg]
42 *
43 * Created.
44 * [91/10/19 dbg]
45 *
46 */
47
48 /*
49 * Support routines for FP emulator.
50 */
51
52 #include <fpe.h>
53
54 #include <cpus.h>
55
56 #include <mach/std_types.h>
57 #include <mach/exception.h>
58 #include <mach/thread_status.h>
59
60 #include <kern/cpu_number.h>
61 #include <kern/thread.h>
62
63 #include <vm/vm_kern.h>
64
65 #include <i386/eflags.h>
66 #include <i386/proc_reg.h>
67 #include <i386/pmap.h>
68 #include <i386/seg.h>
69 #include <i386/thread.h>
70 #include <i386/fpu.h>
71
72 #if NCPUS > 1
73 #include <i386/mp_desc.h>
74 #endif
75
76 extern vm_offset_t kvtophys();
77
78 /*
79 * Symbols exported from FPE emulator.
80 */
81 extern char fpe_start[]; /* start of emulator text;
82 also emulation entry point */
83 extern char fpe_end[]; /* end of emulator text */
84 extern int fpe_reg_segment;
85 /* word holding segment number for
86 FPE register/status area */
87 extern char fpe_recover[]; /* emulation fault recovery entry point */
88
89 /*
90 * Descriptor tables to be modified for FPE.
91 */
92 extern struct fake_descriptor idt[];
93 extern struct fake_descriptor gdt[];
94
95 extern void fix_desc();
96
97 #if NCPUS > 1
98 #define curr_gdt(mycpu) (mp_gdt[mycpu])
99 #define curr_idt(mycpu) (mp_desc_table[mycpu]->idt)
100 #else
101 #define curr_gdt(mycpu) (gdt)
102 #define curr_idt(mycpu) (idt)
103 #endif
104
105 #define gdt_desc_p(mycpu,sel) \
106 ((struct real_descriptor *)&curr_gdt(mycpu)[sel_idx(sel)])
107 #define idt_desc_p(mycpu,idx) \
108 ((struct real_gate *)&curr_idt(mycpu)[idx])
109
110 void set_user_access(); /* forward */
111
112 /*
113 * long pointer for calling FPE register recovery routine.
114 */
115 struct long_ptr {
116 unsigned long offset;
117 unsigned short segment;
118 };
119
120 struct long_ptr fpe_recover_ptr;
121
122 /*
123 * Initialize descriptors for FP emulator.
124 */
125 void
126 fpe_init()
127 {
128 register struct real_descriptor *gdt_p;
129 register struct real_gate *idt_p;
130
131 /*
132 * Map in the pages for the FP emulator:
133 * read-only, user-accessible.
134 */
135 set_user_access(pmap_kernel(),
136 (vm_offset_t)fpe_start,
137 (vm_offset_t)fpe_end,
138 FALSE);
139
140 /*
141 * Put the USER_FPREGS segment value in the FP emulator.
142 */
143 fpe_reg_segment = USER_FPREGS;
144
145 /*
146 * Change exception 7 gate (coprocessor not present)
147 * to a trap gate to the FPE code segment.
148 */
149 idt_p = idt_desc_p(cpu_number(), 7);
150 idt_p->offset_low = 0; /* offset of FPE entry */
151 idt_p->offset_high = 0;
152 idt_p->selector = FPE_CS; /* FPE code segment */
153 idt_p->word_count = 0;
154 idt_p->access = ACC_P|ACC_PL_K|ACC_TRAP_GATE;
155 /* trap gate */
156 /* kernel privileges only,
157 so INT $7 does not call
158 the emulator */
159
160 /*
161 * Build GDT entry for FP code segment.
162 */
163 gdt_p = gdt_desc_p(cpu_number(), FPE_CS);
164 gdt_p->base_low = ((vm_offset_t) fpe_start) & 0xffff;
165 gdt_p->base_med = (((vm_offset_t) fpe_start) >> 16) & 0xff;
166 gdt_p->base_high = ((vm_offset_t) fpe_start) >> 24;
167 gdt_p->limit_low = (vm_offset_t) fpe_end
168 - (vm_offset_t) fpe_start
169 - 1;
170 gdt_p->limit_high = 0;
171 gdt_p->granularity = SZ_32;
172 gdt_p->access = ACC_P|ACC_PL_K|ACC_CODE_CR;
173 /* conforming segment,
174 usable by kernel */
175
176 /*
177 * Build GDT entry for user FP state area - template,
178 * since each thread has its own.
179 */
180 gdt_p = gdt_desc_p(cpu_number(), USER_FPREGS);
181 /* descriptor starts as 0 */
182 gdt_p->limit_low = sizeof(struct i386_fp_save)
183 + sizeof(struct i386_fp_regs)
184 - 1;
185 gdt_p->limit_high = 0;
186 gdt_p->granularity = 0;
187 gdt_p->access = ACC_PL_U|ACC_DATA_W;
188 /* start as "not present" */
189
190 /*
191 * Set up the recovery routine pointer
192 */
193 fpe_recover_ptr.offset = fpe_recover - fpe_start;
194 fpe_recover_ptr.segment = FPE_CS;
195
196 /*
197 * Set i386 to emulate coprocessor.
198 */
199 set_cr0((get_cr0() & ~CR0_MP) | CR0_EM);
200 }
201
202 /*
203 * Enable FPE use for a new thread.
204 * Allocates the FP save area.
205 */
206 boolean_t
207 fp_emul_error(regs)
208 struct i386_saved_state *regs;
209 {
210 register struct i386_fpsave_state *ifps;
211 register vm_offset_t start_va;
212
213 if ((regs->err & 0xfffc) != (USER_FPREGS & ~SEL_PL))
214 return FALSE;
215
216 /*
217 * Make the FPU save area user-accessible (by FPE)
218 */
219 ifps = current_thread()->pcb->ims.ifps;
220 if (ifps == 0) {
221 /*
222 * No FP register state yet - allocate it.
223 */
224 fp_state_alloc();
225 ifps = current_thread()->pcb->ims.ifps;
226 }
227
228 start_va = (vm_offset_t) &ifps->fp_save_state;
229 set_user_access(current_map()->pmap,
230 start_va,
231 start_va + sizeof(struct i386_fp_save),
232 TRUE);
233
234 /*
235 * Enable FPE use for this thread
236 */
237 enable_fpe(ifps);
238
239 return TRUE;
240 }
241
242 /*
243 * Enable FPE use. ASSUME that kernel does NOT use FPU
244 * except to handle user exceptions.
245 */
246 void
247 enable_fpe(ifps)
248 register struct i386_fpsave_state *ifps;
249 {
250 struct real_descriptor *dp;
251 vm_offset_t start_va;
252
253 dp = gdt_desc_p(cpu_number(), USER_FPREGS);
254 start_va = (vm_offset_t)&ifps->fp_save_state;
255
256 dp->base_low = start_va & 0xffff;
257 dp->base_med = (start_va >> 16) & 0xff;
258 dp->base_high = start_va >> 24;
259 dp->access |= ACC_P;
260 }
261
262 void
263 disable_fpe()
264 {
265 /*
266 * The kernel might be running with fs & gs segments
267 * which refer to USER_FPREGS, if we entered the kernel
268 * from a FP-using thread. We have to clear these segments
269 * lest we get a Segment Not Present trap. This would happen
270 * if the kernel took an interrupt or fault after clearing
271 * the present bit but before exiting to user space (which
272 * would reset fs & gs from the current user thread).
273 */
274
275 asm volatile("xorl %eax, %eax");
276 asm volatile("movw %ax, %fs");
277 asm volatile("movw %ax, %gs");
278
279 gdt_desc_p(cpu_number(), USER_FPREGS)->access &= ~ACC_P;
280 }
281
282 void
283 set_user_access(pmap, start, end, writable)
284 pmap_t pmap;
285 vm_offset_t start;
286 vm_offset_t end;
287 boolean_t writable;
288 {
289 register vm_offset_t va;
290 register pt_entry_t * dirbase = pmap->dirbase;
291 register pt_entry_t * ptep;
292 register pt_entry_t * pdep;
293
294 start = i386_trunc_page(start);
295 end = i386_round_page(end);
296
297 for (va = start; va < end; va += I386_PGBYTES) {
298
299 pdep = &dirbase[pdenum(va)];
300 *pdep |= INTEL_PTE_USER;
301 ptep = (pt_entry_t *)ptetokv(*pdep);
302 ptep = &ptep[ptenum(va)];
303 *ptep |= INTEL_PTE_USER;
304 if (!writable)
305 *ptep &= ~INTEL_PTE_WRITE;
306 }
307 }
308
309 /*
310 * Route exception through emulator fixup routine if
311 * it occured within the emulator.
312 */
313 extern void exception();
314
315 void
316 fpe_exception_fixup(exc, code, subcode)
317 int exc, code, subcode;
318 {
319 thread_t thread = current_thread();
320 pcb_t pcb = thread->pcb;
321
322 if (pcb->iss.efl & EFL_VM) {
323 /*
324 * The emulator doesn`t handle V86 mode.
325 * If this is a GP fault on the emulator`s
326 * code segment, change it to an FP not present
327 * fault.
328 */
329 if (exc == EXC_BAD_INSTRUCTION
330 && code == EXC_I386_GPFLT
331 && subcode == FPE_CS + 1)
332 {
333 exc = EXC_ARITHMETIC; /* arithmetic error: */
334 code = EXC_I386_NOEXT; /* no FPU */
335 subcode = 0;
336 }
337 }
338 else
339 if ((pcb->iss.cs & 0xfffc) == FPE_CS) {
340 /*
341 * Pass registers to emulator,
342 * to let it fix them up.
343 * The emulator fixup routine knows about
344 * an i386_thread_state.
345 */
346 struct i386_thread_state tstate;
347 unsigned int count;
348
349 count = i386_THREAD_STATE_COUNT;
350 (void) thread_getstatus(thread,
351 i386_REGS_SEGS_STATE,
352 (thread_state_t) &tstate,
353 &count);
354
355 /*
356 * long call to emulator register recovery routine
357 */
358 asm volatile("pushl %0; lcall %1; addl $4,%%esp"
359 :
360 : "r" (&tstate),
361 "m" (*(char *)&fpe_recover_ptr) );
362
363 (void) thread_setstatus(thread,
364 i386_REGS_SEGS_STATE,
365 (thread_state_t) &tstate,
366 count);
367 /*
368 * In addition, check for a GP fault on 'int 16' in
369 * the emulator, since the interrupt gate is protected.
370 * If so, change it to an arithmetic error.
371 */
372 if (exc == EXC_BAD_INSTRUCTION
373 && code == EXC_I386_GPFLT
374 && subcode == 8*16+2) /* idt[16] */
375 {
376 exc = EXC_ARITHMETIC;
377 code = EXC_I386_EXTERR;
378 subcode = pcb->ims.ifps->fp_save_state.fp_status;
379 }
380 }
381 exception(exc, code, subcode);
382 }
Cache object: e7414191a8d6923c474d52830fb260a7
|