]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/alpha/alpha/fp_emulate.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / sys / alpha / alpha / fp_emulate.c
1 /*-
2  * Copyright (c) 1998 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/sysproto.h>
33 #include <sys/sysent.h>
34 #include <sys/proc.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37
38 #include <vm/vm.h>
39 #include <vm/vm_kern.h>
40 #include <vm/vm_page.h>
41 #include <vm/vm_map.h>
42 #include <vm/vm_extern.h>
43 #include <vm/vm_object.h>
44 #include <vm/vm_pager.h>
45
46 #include <machine/fpu.h>
47 #include <machine/inst.h>
48 #include <machine/md_var.h>
49 #include <machine/pcb.h>
50 #include <machine/reg.h>
51
52 #include <alpha/alpha/ieee_float.h>
53
54 #define GETREG(regs, i)         (*(fp_register_t*) &regs->fpr_regs[i])
55 #define PUTREG(regs, i, v)      (*(fp_register_t*) &regs->fpr_regs[i] = v)
56
57 typedef fp_register_t fp_opcode_handler(union alpha_instruction ins,
58                                         int src, int rnd,
59                                         u_int64_t fp_control,
60                                         u_int64_t *status,
61                                         struct fpreg *fpregs);
62
63 static fp_register_t fp_add(union alpha_instruction ins,
64                             int src, int rnd,
65                             u_int64_t control, u_int64_t *status,
66                             struct fpreg *fpregs)
67 {
68         return ieee_add(GETREG(fpregs, ins.f_format.fa),
69                         GETREG(fpregs, ins.f_format.fb),
70                         src, rnd, control, status);
71 }
72
73 static fp_register_t fp_sub(union alpha_instruction ins,
74                             int src, int rnd,
75                             u_int64_t control, u_int64_t *status,
76                             struct fpreg *fpregs)
77 {
78     return ieee_sub(GETREG(fpregs, ins.f_format.fa),
79                     GETREG(fpregs, ins.f_format.fb),
80                     src, rnd, control, status);
81 }
82
83 static fp_register_t fp_mul(union alpha_instruction ins,
84                             int src, int rnd,
85                             u_int64_t control, u_int64_t *status,
86                             struct fpreg *fpregs)
87 {
88     return ieee_mul(GETREG(fpregs, ins.f_format.fa),
89                     GETREG(fpregs, ins.f_format.fb),
90                     src, rnd, control, status);
91 }
92
93 static fp_register_t fp_div(union alpha_instruction ins,
94                             int src, int rnd,
95                             u_int64_t control, u_int64_t *status,
96                             struct fpreg *fpregs)
97 {
98     return ieee_div(GETREG(fpregs, ins.f_format.fa),
99                     GETREG(fpregs, ins.f_format.fb),
100                     src, rnd, control, status);
101 }
102
103 static fp_register_t fp_cmpun(union alpha_instruction ins,
104                               int src, int rnd,
105                               u_int64_t control, u_int64_t *status,
106                               struct fpreg *fpregs)
107 {
108     return ieee_cmpun(GETREG(fpregs, ins.f_format.fa),
109                       GETREG(fpregs, ins.f_format.fb),
110                       status);
111 }
112
113 static fp_register_t fp_cmpeq(union alpha_instruction ins,
114                               int src, int rnd,
115                               u_int64_t control, u_int64_t *status,
116                               struct fpreg *fpregs)
117 {
118     return ieee_cmpeq(GETREG(fpregs, ins.f_format.fa),
119                       GETREG(fpregs, ins.f_format.fb),
120                       status);
121 }
122
123 static fp_register_t fp_cmplt(union alpha_instruction ins,
124                               int src, int rnd,
125                               u_int64_t control, u_int64_t *status,
126                               struct fpreg *fpregs)
127 {
128     return ieee_cmplt(GETREG(fpregs, ins.f_format.fa),
129                       GETREG(fpregs, ins.f_format.fb),
130                       status);
131 }
132
133 static fp_register_t fp_cmple(union alpha_instruction ins,
134                               int src, int rnd,
135                               u_int64_t control, u_int64_t *status,
136                               struct fpreg *fpregs)
137 {
138     return ieee_cmple(GETREG(fpregs, ins.f_format.fa),
139                       GETREG(fpregs, ins.f_format.fb),
140                       status);
141 }
142
143 static fp_register_t fp_cvts(union alpha_instruction ins,
144                              int src, int rnd,
145                              u_int64_t control, u_int64_t *status,
146                              struct fpreg *fpregs)
147 {
148         switch (src) {
149         case T_FORMAT:
150             return ieee_convert_T_S(GETREG(fpregs, ins.f_format.fb),
151                                     rnd, control, status);
152
153         case Q_FORMAT:
154             return ieee_convert_Q_S(GETREG(fpregs, ins.f_format.fb),
155                                     rnd, control, status);
156
157         default:
158             *status |= FPCR_INV;
159             return GETREG(fpregs, ins.f_format.fc);
160         }
161 }
162
163 static fp_register_t fp_cvtt(union alpha_instruction ins,
164                              int src, int rnd,
165                              u_int64_t control, u_int64_t *status,
166                              struct fpreg *fpregs)
167 {
168         switch (src) {
169         case S_FORMAT:
170             return ieee_convert_S_T(GETREG(fpregs, ins.f_format.fb),
171                                     rnd, control, status);
172             break;
173
174         case Q_FORMAT:
175             return ieee_convert_Q_T(GETREG(fpregs, ins.f_format.fb),
176                                     rnd, control, status);
177             break;
178
179         default:
180             *status |= FPCR_INV;
181             return GETREG(fpregs, ins.f_format.fc);
182         }
183 }
184
185 static fp_register_t fp_cvtq(union alpha_instruction ins,
186                              int src, int rnd,
187                              u_int64_t control, u_int64_t *status,
188                              struct fpreg *fpregs)
189 {
190         switch (src) {
191         case S_FORMAT:
192             return ieee_convert_S_Q(GETREG(fpregs, ins.f_format.fb),
193                                     rnd, control, status);
194             break;
195
196         case T_FORMAT:
197             return ieee_convert_T_Q(GETREG(fpregs, ins.f_format.fb),
198                                     rnd, control, status);
199             break;
200             
201         default:
202             *status |= FPCR_INV;
203             return GETREG(fpregs, ins.f_format.fc);
204         }
205 }
206
207 static fp_register_t fp_reserved(union alpha_instruction ins,
208                                  int src, int rnd,
209                                  u_int64_t control, u_int64_t *status,
210                                  struct fpreg *fpregs)
211 {
212     *status |= FPCR_INV;
213     return GETREG(fpregs, ins.f_format.fc);
214 }
215
216 static fp_register_t fp_cvtql(union alpha_instruction ins,
217                                  int src, int rnd,
218                                  u_int64_t control, u_int64_t *status,
219                                  struct fpreg *fpregs)
220
221 {
222     fp_register_t fb = GETREG(fpregs, ins.f_format.fb);
223     fp_register_t ret;
224     *status |= FPCR_INV;
225     ret.q = ((fb.q & 0xc0000000) << 32 | (fb.q & 0x3fffffff) << 29);
226     return ret;
227 }
228
229 static int fp_emulate(union alpha_instruction ins, struct thread *td)
230 {
231         u_int64_t control = td->td_pcb->pcb_fp_control;
232         struct fpreg *fpregs = &td->td_pcb->pcb_fp;
233         static fp_opcode_handler *ops[16] = {
234                 fp_add,         /* 0 */
235                 fp_sub,         /* 1 */
236                 fp_mul,         /* 2 */
237                 fp_div,         /* 3 */
238                 fp_cmpun,       /* 4 */
239                 fp_cmpeq,       /* 5 */
240                 fp_cmplt,       /* 6 */
241                 fp_cmple,       /* 7 */
242                 fp_reserved,    /* 8 */
243                 fp_reserved,    /* 9 */
244                 fp_reserved,    /* 10 */
245                 fp_reserved,    /* 11 */
246                 fp_cvts,        /* 12 */
247                 fp_reserved,    /* 13 */
248                 fp_cvtt,        /* 14 */
249                 fp_cvtq,        /* 15 */
250         };
251         int src, rnd;
252         fp_register_t result;
253         u_int64_t status;
254
255         /*
256          * Only attempt to emulate ieee instructions & integer overflow
257          */
258         if ((ins.common.opcode != op_flti) &&
259             (ins.f_format.function != fltl_cvtqlsv)){
260                 printf("fp_emulate: unhandled opcode = 0x%x, fun = 0x%x\n",ins.common.opcode,ins.f_format.function);
261                 return 0;
262         }
263
264         /*
265          * Dump the float registers into the pcb so we can get at
266          * them. We are potentially going to modify the fp state, so
267          * cancel fpcurproc too.
268          */
269         alpha_fpstate_save(td, 1);
270
271         /*
272          * Decode and execute the instruction.
273          */
274         src = (ins.f_format.function >> 4) & 3;
275         rnd = (ins.f_format.function >> 6) & 3;
276         if (rnd == 3)
277                 rnd = (fpregs->fpr_cr >> FPCR_DYN_SHIFT) & 3;
278         status = 0;
279         if (ins.common.opcode == op_fltl
280            && ins.f_format.function == fltl_cvtqlsv)
281                 result = fp_cvtql(ins, src, rnd, control, &status,
282                                   fpregs);
283         else
284                 result = ops[ins.f_format.function & 0xf](ins, src, rnd,
285                                                           control, &status,
286                                                           fpregs);
287         
288         /*
289          * Handle exceptions.
290          */
291         if (status) {
292                 u_int64_t fpcr;
293
294                 /* Record the exception in the software control word. */
295                 control |= (status >> IEEE_STATUS_TO_FPCR_SHIFT);
296                 td->td_pcb->pcb_fp_control = control;
297
298                 /* Regenerate the control register */
299                 fpcr = fpregs->fpr_cr & (FPCR_DYN_MASK | FPCR_STATUS_MASK);
300                 fpcr |= ((control & IEEE_STATUS_MASK)
301                          << IEEE_STATUS_TO_FPCR_SHIFT);
302                 if (!(control & IEEE_TRAP_ENABLE_INV))
303                         fpcr |= FPCR_INVD;
304                 if (!(control & IEEE_TRAP_ENABLE_DZE))
305                         fpcr |= FPCR_DZED;
306                 if (!(control & IEEE_TRAP_ENABLE_OVF))
307                         fpcr |= FPCR_OVFD;
308                 if (!(control & IEEE_TRAP_ENABLE_UNF))
309                         fpcr |= FPCR_UNFD;
310                 if (!(control & IEEE_TRAP_ENABLE_INE))
311                         fpcr |= FPCR_INED;
312                 if (control & IEEE_STATUS_MASK)
313                         fpcr |= FPCR_SUM;
314                 fpregs->fpr_cr = fpcr;
315
316                 /* Check the trap enable */
317                 if ((control >> IEEE_STATUS_TO_EXCSUM_SHIFT)
318                     & (control & IEEE_TRAP_ENABLE_MASK))
319                         return 0;
320         }
321
322         PUTREG(fpregs, ins.f_format.fc, result);
323         return 1;
324 }
325
326 /*
327  * Attempt to complete a floating point instruction which trapped by
328  * emulating it in software.  Return non-zero if the completion was
329  * successful, otherwise zero.
330  */
331 int fp_software_completion(u_int64_t regmask, struct thread *td)
332 {
333         struct trapframe *frame = td->td_frame;
334         u_int64_t pc = frame->tf_regs[FRAME_PC];
335         int error;
336
337         /*
338          * First we must search back through the trap shadow to find which
339          * instruction was responsible for generating the trap.
340          */
341         pc -= 4;
342         while (regmask) {
343                 union alpha_instruction ins;
344
345                 /*
346                  * Read the instruction and figure out the destination
347                  * register and opcode.
348                  */
349                 error = copyin((caddr_t) pc, &ins, sizeof(ins));
350                 if (error)
351                         return 0;
352                 
353                 switch (ins.common.opcode) {
354                 case op_call_pal:
355                 case op_jsr:
356                 case op_br ... op_bgt:
357                         /*
358                          * Condition 6: the trap shadow may not
359                          * include any branch instructions.   Also,
360                          * the trap shadow is bounded by EXCB, TRAPB
361                          * and CALL_PAL.
362                          */
363                         return 0;
364
365                 case op_misc:
366                         switch (ins.memory_format.function) {
367                         case misc_trapb:
368                         case misc_excb:
369                                 return 0;
370                         }
371                         break;
372
373                 case op_inta:
374                 case op_intl:
375                 case op_ints:
376                         /*
377                          * The first 32 bits of the register mask
378                          * represents integer registers which were
379                          * modified in the trap shadow.
380                          */
381                         regmask &= ~(1LL << ins.o_format.rc);
382                         break;
383
384                 case op_fltv:
385                 case op_flti:
386                 case op_fltl:
387                         /*
388                          * The second 32 bits of the register mask
389                          * represents float registers which were
390                          * modified in the trap shadow.
391                          */
392                         regmask &= ~(1LL << (ins.f_format.fc + 32));
393                         break;
394                 }
395
396                 if (regmask == 0) {
397                         /*
398                          * We have traced back through all the
399                          * instructions in the trap shadow, so this
400                          * must be the one which generated the trap.
401                          */
402                         if (fp_emulate(ins, td)) {
403                                 /*
404                                  * Restore pc to the first instruction
405                                  * in the trap shadow.
406                                  */
407                                 frame->tf_regs[FRAME_PC] = pc + 4;
408                                 return 1;
409                         } else
410                                 return 0;
411                 }
412                 pc -= 4;
413         }
414         return 0;
415 }