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