]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/aim/trap.c
MFV illumos r266986:
[FreeBSD/FreeBSD.git] / sys / powerpc / aim / trap.c
1 /*-
2  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
3  * Copyright (C) 1995, 1996 TooLs GmbH.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by TooLs GmbH.
17  * 4. The name of TooLs GmbH may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $NetBSD: trap.c,v 1.58 2002/03/04 04:07:35 dbj Exp $
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/kdb.h>
39 #include <sys/proc.h>
40 #include <sys/ktr.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <sys/pioctl.h>
44 #include <sys/ptrace.h>
45 #include <sys/reboot.h>
46 #include <sys/syscall.h>
47 #include <sys/sysent.h>
48 #include <sys/systm.h>
49 #include <sys/uio.h>
50 #include <sys/signalvar.h>
51 #include <sys/vmmeter.h>
52
53 #include <security/audit/audit.h>
54
55 #include <vm/vm.h>
56 #include <vm/pmap.h>
57 #include <vm/vm_extern.h>
58 #include <vm/vm_param.h>
59 #include <vm/vm_kern.h>
60 #include <vm/vm_map.h>
61 #include <vm/vm_page.h>
62
63 #include <machine/_inttypes.h>
64 #include <machine/altivec.h>
65 #include <machine/cpu.h>
66 #include <machine/db_machdep.h>
67 #include <machine/fpu.h>
68 #include <machine/frame.h>
69 #include <machine/pcb.h>
70 #include <machine/pmap.h>
71 #include <machine/psl.h>
72 #include <machine/trap.h>
73 #include <machine/spr.h>
74 #include <machine/sr.h>
75
76 static void     trap_fatal(struct trapframe *frame);
77 static void     printtrap(u_int vector, struct trapframe *frame, int isfatal,
78                     int user);
79 static int      trap_pfault(struct trapframe *frame, int user);
80 static int      fix_unaligned(struct thread *td, struct trapframe *frame);
81 static int      handle_onfault(struct trapframe *frame);
82 static void     syscall(struct trapframe *frame);
83
84 #ifdef __powerpc64__
85        void     handle_kernel_slb_spill(int, register_t, register_t);
86 static int      handle_user_slb_spill(pmap_t pm, vm_offset_t addr);
87 extern int      n_slbs;
88 #endif
89
90 struct powerpc_exception {
91         u_int   vector;
92         char    *name;
93 };
94
95 #ifdef KDTRACE_HOOKS
96 #include <sys/dtrace_bsd.h>
97
98 int (*dtrace_invop_jump_addr)(struct trapframe *);
99 #endif
100
101 static struct powerpc_exception powerpc_exceptions[] = {
102         { 0x0100, "system reset" },
103         { 0x0200, "machine check" },
104         { 0x0300, "data storage interrupt" },
105         { 0x0380, "data segment exception" },
106         { 0x0400, "instruction storage interrupt" },
107         { 0x0480, "instruction segment exception" },
108         { 0x0500, "external interrupt" },
109         { 0x0600, "alignment" },
110         { 0x0700, "program" },
111         { 0x0800, "floating-point unavailable" },
112         { 0x0900, "decrementer" },
113         { 0x0c00, "system call" },
114         { 0x0d00, "trace" },
115         { 0x0e00, "floating-point assist" },
116         { 0x0f00, "performance monitoring" },
117         { 0x0f20, "altivec unavailable" },
118         { 0x1000, "instruction tlb miss" },
119         { 0x1100, "data load tlb miss" },
120         { 0x1200, "data store tlb miss" },
121         { 0x1300, "instruction breakpoint" },
122         { 0x1400, "system management" },
123         { 0x1600, "altivec assist" },
124         { 0x1700, "thermal management" },
125         { 0x2000, "run mode/trace" },
126         { 0x3000, NULL }
127 };
128
129 static const char *
130 trapname(u_int vector)
131 {
132         struct  powerpc_exception *pe;
133
134         for (pe = powerpc_exceptions; pe->vector != 0x3000; pe++) {
135                 if (pe->vector == vector)
136                         return (pe->name);
137         }
138
139         return ("unknown");
140 }
141
142 void
143 trap(struct trapframe *frame)
144 {
145         struct thread   *td;
146         struct proc     *p;
147 #ifdef KDTRACE_HOOKS
148         uint32_t inst;
149 #endif
150         int             sig, type, user;
151         u_int           ucode;
152         ksiginfo_t      ksi;
153
154         PCPU_INC(cnt.v_trap);
155
156         td = curthread;
157         p = td->td_proc;
158
159         type = ucode = frame->exc;
160         sig = 0;
161         user = frame->srr1 & PSL_PR;
162
163         CTR3(KTR_TRAP, "trap: %s type=%s (%s)", td->td_name,
164             trapname(type), user ? "user" : "kernel");
165
166 #ifdef KDTRACE_HOOKS
167         /*
168          * A trap can occur while DTrace executes a probe. Before
169          * executing the probe, DTrace blocks re-scheduling and sets
170          * a flag in it's per-cpu flags to indicate that it doesn't
171          * want to fault. On returning from the probe, the no-fault
172          * flag is cleared and finally re-scheduling is enabled.
173          *
174          * If the DTrace kernel module has registered a trap handler,
175          * call it and if it returns non-zero, assume that it has
176          * handled the trap and modified the trap frame so that this
177          * function can return normally.
178          */
179         if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, type))
180                 return;
181 #endif
182
183         if (user) {
184                 td->td_pticks = 0;
185                 td->td_frame = frame;
186                 if (td->td_ucred != p->p_ucred)
187                         cred_update_thread(td);
188
189                 /* User Mode Traps */
190                 switch (type) {
191                 case EXC_RUNMODETRC:
192                 case EXC_TRC:
193                         frame->srr1 &= ~PSL_SE;
194                         sig = SIGTRAP;
195                         break;
196
197 #ifdef __powerpc64__
198                 case EXC_ISE:
199                 case EXC_DSE:
200                         if (handle_user_slb_spill(&p->p_vmspace->vm_pmap,
201                             (type == EXC_ISE) ? frame->srr0 :
202                             frame->cpu.aim.dar) != 0)
203                                 sig = SIGSEGV;
204                         break;
205 #endif
206                 case EXC_DSI:
207                 case EXC_ISI:
208                         sig = trap_pfault(frame, 1);
209                         break;
210
211                 case EXC_SC:
212                         syscall(frame);
213                         break;
214
215                 case EXC_FPU:
216                         KASSERT((td->td_pcb->pcb_flags & PCB_FPU) != PCB_FPU,
217                             ("FPU already enabled for thread"));
218                         enable_fpu(td);
219                         break;
220
221                 case EXC_VEC:
222                         KASSERT((td->td_pcb->pcb_flags & PCB_VEC) != PCB_VEC,
223                             ("Altivec already enabled for thread"));
224                         enable_vec(td);
225                         break;
226
227                 case EXC_VECAST_G4:
228                 case EXC_VECAST_G5:
229                         /*
230                          * We get a VPU assist exception for IEEE mode
231                          * vector operations on denormalized floats.
232                          * Emulating this is a giant pain, so for now,
233                          * just switch off IEEE mode and treat them as
234                          * zero.
235                          */
236
237                         save_vec(td);
238                         td->td_pcb->pcb_vec.vscr |= ALTIVEC_VSCR_NJ;
239                         enable_vec(td);
240                         break;
241
242                 case EXC_ALI:
243                         if (fix_unaligned(td, frame) != 0)
244                                 sig = SIGBUS;
245                         else
246                                 frame->srr0 += 4;
247                         break;
248
249                 case EXC_PGM:
250                         /* Identify the trap reason */
251                         if (frame->srr1 & EXC_PGM_TRAP) {
252 #ifdef KDTRACE_HOOKS
253                                 inst = fuword32((const void *)frame->srr0);
254                                 if (inst == 0x0FFFDDDD && dtrace_pid_probe_ptr != NULL) {
255                                         struct reg regs;
256                                         fill_regs(td, &regs);
257                                         (*dtrace_pid_probe_ptr)(&regs);
258                                         break;
259                                 }
260 #endif
261                                 sig = SIGTRAP;
262                         } else {
263                                 sig = ppc_instr_emulate(frame, td->td_pcb);
264                         }
265                         break;
266
267                 default:
268                         trap_fatal(frame);
269                 }
270         } else {
271                 /* Kernel Mode Traps */
272
273                 KASSERT(cold || td->td_ucred != NULL,
274                     ("kernel trap doesn't have ucred"));
275                 switch (type) {
276 #ifdef KDTRACE_HOOKS
277                 case EXC_PGM:
278                         if (frame->srr1 & EXC_PGM_TRAP) {
279                                 if (*(uint32_t *)frame->srr0 == 0x7c810808) {
280                                         if (dtrace_invop_jump_addr != NULL) {
281                                                 dtrace_invop_jump_addr(frame);
282                                                 return;
283                                         }
284                                 }
285                         }
286                         break;
287 #endif
288 #ifdef __powerpc64__
289                 case EXC_DSE:
290                         if ((frame->cpu.aim.dar & SEGMENT_MASK) == USER_ADDR) {
291                                 __asm __volatile ("slbmte %0, %1" ::
292                                         "r"(td->td_pcb->pcb_cpu.aim.usr_vsid),
293                                         "r"(USER_SLB_SLBE));
294                                 return;
295                         }
296                         break;
297 #endif
298                 case EXC_DSI:
299                         if (trap_pfault(frame, 0) == 0)
300                                 return;
301                         break;
302                 case EXC_MCHK:
303                         if (handle_onfault(frame))
304                                 return;
305                         break;
306                 default:
307                         break;
308                 }
309                 trap_fatal(frame);
310         }
311
312         if (sig != 0) {
313                 if (p->p_sysent->sv_transtrap != NULL)
314                         sig = (p->p_sysent->sv_transtrap)(sig, type);
315                 ksiginfo_init_trap(&ksi);
316                 ksi.ksi_signo = sig;
317                 ksi.ksi_code = (int) ucode; /* XXX, not POSIX */
318                 /* ksi.ksi_addr = ? */
319                 ksi.ksi_trapno = type;
320                 trapsignal(td, &ksi);
321         }
322
323         userret(td, frame);
324 }
325
326 static void
327 trap_fatal(struct trapframe *frame)
328 {
329
330         printtrap(frame->exc, frame, 1, (frame->srr1 & PSL_PR));
331 #ifdef KDB
332         if ((debugger_on_panic || kdb_active) &&
333             kdb_trap(frame->exc, 0, frame))
334                 return;
335 #endif
336         panic("%s trap", trapname(frame->exc));
337 }
338
339 static void
340 printtrap(u_int vector, struct trapframe *frame, int isfatal, int user)
341 {
342
343         printf("\n");
344         printf("%s %s trap:\n", isfatal ? "fatal" : "handled",
345             user ? "user" : "kernel");
346         printf("\n");
347         printf("   exception       = 0x%x (%s)\n", vector, trapname(vector));
348         switch (vector) {
349         case EXC_DSE:
350         case EXC_DSI:
351                 printf("   virtual address = 0x%" PRIxPTR "\n",
352                     frame->cpu.aim.dar);
353                 printf("   dsisr           = 0x%" PRIxPTR "\n",
354                     frame->cpu.aim.dsisr);
355                 break;
356         case EXC_ISE:
357         case EXC_ISI:
358                 printf("   virtual address = 0x%" PRIxPTR "\n", frame->srr0);
359                 break;
360         }
361         printf("   srr0            = 0x%" PRIxPTR "\n", frame->srr0);
362         printf("   srr1            = 0x%" PRIxPTR "\n", frame->srr1);
363         printf("   lr              = 0x%" PRIxPTR "\n", frame->lr);
364         printf("   curthread       = %p\n", curthread);
365         if (curthread != NULL)
366                 printf("          pid = %d, comm = %s\n",
367                     curthread->td_proc->p_pid, curthread->td_name);
368         printf("\n");
369 }
370
371 /*
372  * Handles a fatal fault when we have onfault state to recover.  Returns
373  * non-zero if there was onfault recovery state available.
374  */
375 static int
376 handle_onfault(struct trapframe *frame)
377 {
378         struct          thread *td;
379         faultbuf        *fb;
380
381         td = curthread;
382         fb = td->td_pcb->pcb_onfault;
383         if (fb != NULL) {
384                 frame->srr0 = (*fb)[0];
385                 frame->fixreg[1] = (*fb)[1];
386                 frame->fixreg[2] = (*fb)[2];
387                 frame->fixreg[3] = 1;
388                 frame->cr = (*fb)[3];
389                 bcopy(&(*fb)[4], &frame->fixreg[13],
390                     19 * sizeof(register_t));
391                 return (1);
392         }
393         return (0);
394 }
395
396 int
397 cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa)
398 {
399         struct proc *p;
400         struct trapframe *frame;
401         caddr_t params;
402         size_t argsz;
403         int error, n, i;
404
405         p = td->td_proc;
406         frame = td->td_frame;
407
408         sa->code = frame->fixreg[0];
409         params = (caddr_t)(frame->fixreg + FIRSTARG);
410         n = NARGREG;
411
412         if (sa->code == SYS_syscall) {
413                 /*
414                  * code is first argument,
415                  * followed by actual args.
416                  */
417                 sa->code = *(register_t *) params;
418                 params += sizeof(register_t);
419                 n -= 1;
420         } else if (sa->code == SYS___syscall) {
421                 /*
422                  * Like syscall, but code is a quad,
423                  * so as to maintain quad alignment
424                  * for the rest of the args.
425                  */
426                 if (SV_PROC_FLAG(p, SV_ILP32)) {
427                         params += sizeof(register_t);
428                         sa->code = *(register_t *) params;
429                         params += sizeof(register_t);
430                         n -= 2;
431                 } else {
432                         sa->code = *(register_t *) params;
433                         params += sizeof(register_t);
434                         n -= 1;
435                 }
436         }
437
438         if (p->p_sysent->sv_mask)
439                 sa->code &= p->p_sysent->sv_mask;
440         if (sa->code >= p->p_sysent->sv_size)
441                 sa->callp = &p->p_sysent->sv_table[0];
442         else
443                 sa->callp = &p->p_sysent->sv_table[sa->code];
444
445         sa->narg = sa->callp->sy_narg;
446
447         if (SV_PROC_FLAG(p, SV_ILP32)) {
448                 argsz = sizeof(uint32_t);
449
450                 for (i = 0; i < n; i++)
451                         sa->args[i] = ((u_register_t *)(params))[i] &
452                             0xffffffff;
453         } else {
454                 argsz = sizeof(uint64_t);
455
456                 for (i = 0; i < n; i++)
457                         sa->args[i] = ((u_register_t *)(params))[i];
458         }
459
460         if (sa->narg > n)
461                 error = copyin(MOREARGS(frame->fixreg[1]), sa->args + n,
462                                (sa->narg - n) * argsz);
463         else
464                 error = 0;
465
466 #ifdef __powerpc64__
467         if (SV_PROC_FLAG(p, SV_ILP32) && sa->narg > n) {
468                 /* Expand the size of arguments copied from the stack */
469
470                 for (i = sa->narg; i >= n; i--)
471                         sa->args[i] = ((uint32_t *)(&sa->args[n]))[i-n];
472         }
473 #endif
474
475         if (error == 0) {
476                 td->td_retval[0] = 0;
477                 td->td_retval[1] = frame->fixreg[FIRSTARG + 1];
478         }
479         return (error);
480 }
481
482 #include "../../kern/subr_syscall.c"
483
484 void
485 syscall(struct trapframe *frame)
486 {
487         struct thread *td;
488         struct syscall_args sa;
489         int error;
490
491         td = curthread;
492         td->td_frame = frame;
493
494 #ifdef __powerpc64__
495         /*
496          * Speculatively restore last user SLB segment, which we know is
497          * invalid already, since we are likely to do copyin()/copyout().
498          */
499         __asm __volatile ("slbmte %0, %1; isync" ::
500             "r"(td->td_pcb->pcb_cpu.aim.usr_vsid), "r"(USER_SLB_SLBE));
501 #endif
502
503         error = syscallenter(td, &sa);
504         syscallret(td, error, &sa);
505 }
506
507 #ifdef __powerpc64__
508 /* Handle kernel SLB faults -- runs in real mode, all seat belts off */
509 void
510 handle_kernel_slb_spill(int type, register_t dar, register_t srr0)
511 {
512         struct slb *slbcache;
513         uint64_t slbe, slbv;
514         uint64_t esid, addr;
515         int i;
516
517         addr = (type == EXC_ISE) ? srr0 : dar;
518         slbcache = PCPU_GET(slb);
519         esid = (uintptr_t)addr >> ADDR_SR_SHFT;
520         slbe = (esid << SLBE_ESID_SHIFT) | SLBE_VALID;
521         
522         /* See if the hardware flushed this somehow (can happen in LPARs) */
523         for (i = 0; i < n_slbs; i++)
524                 if (slbcache[i].slbe == (slbe | (uint64_t)i))
525                         return;
526
527         /* Not in the map, needs to actually be added */
528         slbv = kernel_va_to_slbv(addr);
529         if (slbcache[USER_SLB_SLOT].slbe == 0) {
530                 for (i = 0; i < n_slbs; i++) {
531                         if (i == USER_SLB_SLOT)
532                                 continue;
533                         if (!(slbcache[i].slbe & SLBE_VALID))
534                                 goto fillkernslb;
535                 }
536
537                 if (i == n_slbs)
538                         slbcache[USER_SLB_SLOT].slbe = 1;
539         }
540
541         /* Sacrifice a random SLB entry that is not the user entry */
542         i = mftb() % n_slbs;
543         if (i == USER_SLB_SLOT)
544                 i = (i+1) % n_slbs;
545
546 fillkernslb:
547         /* Write new entry */
548         slbcache[i].slbv = slbv;
549         slbcache[i].slbe = slbe | (uint64_t)i;
550
551         /* Trap handler will restore from cache on exit */
552 }
553
554 static int 
555 handle_user_slb_spill(pmap_t pm, vm_offset_t addr)
556 {
557         struct slb *user_entry;
558         uint64_t esid;
559         int i;
560
561         esid = (uintptr_t)addr >> ADDR_SR_SHFT;
562
563         PMAP_LOCK(pm);
564         user_entry = user_va_to_slb_entry(pm, addr);
565
566         if (user_entry == NULL) {
567                 /* allocate_vsid auto-spills it */
568                 (void)allocate_user_vsid(pm, esid, 0);
569         } else {
570                 /*
571                  * Check that another CPU has not already mapped this.
572                  * XXX: Per-thread SLB caches would be better.
573                  */
574                 for (i = 0; i < pm->pm_slb_len; i++)
575                         if (pm->pm_slb[i] == user_entry)
576                                 break;
577
578                 if (i == pm->pm_slb_len)
579                         slb_insert_user(pm, user_entry);
580         }
581         PMAP_UNLOCK(pm);
582
583         return (0);
584 }
585 #endif
586
587 static int
588 trap_pfault(struct trapframe *frame, int user)
589 {
590         vm_offset_t     eva, va;
591         struct          thread *td;
592         struct          proc *p;
593         vm_map_t        map;
594         vm_prot_t       ftype;
595         int             rv;
596         register_t      user_sr;
597
598         td = curthread;
599         p = td->td_proc;
600         if (frame->exc == EXC_ISI) {
601                 eva = frame->srr0;
602                 ftype = VM_PROT_EXECUTE;
603                 if (frame->srr1 & SRR1_ISI_PFAULT)
604                         ftype |= VM_PROT_READ;
605         } else {
606                 eva = frame->cpu.aim.dar;
607                 if (frame->cpu.aim.dsisr & DSISR_STORE)
608                         ftype = VM_PROT_WRITE;
609                 else
610                         ftype = VM_PROT_READ;
611         }
612
613         if (user) {
614                 map = &p->p_vmspace->vm_map;
615         } else {
616                 if ((eva >> ADDR_SR_SHFT) == (USER_ADDR >> ADDR_SR_SHFT)) {
617                         if (p->p_vmspace == NULL)
618                                 return (SIGSEGV);
619
620                         map = &p->p_vmspace->vm_map;
621
622                         user_sr = td->td_pcb->pcb_cpu.aim.usr_segm;
623                         eva &= ADDR_PIDX | ADDR_POFF;
624                         eva |= user_sr << ADDR_SR_SHFT;
625                 } else {
626                         map = kernel_map;
627                 }
628         }
629         va = trunc_page(eva);
630
631         if (map != kernel_map) {
632                 /*
633                  * Keep swapout from messing with us during this
634                  *      critical time.
635                  */
636                 PROC_LOCK(p);
637                 ++p->p_lock;
638                 PROC_UNLOCK(p);
639
640                 /* Fault in the user page: */
641                 rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
642
643                 PROC_LOCK(p);
644                 --p->p_lock;
645                 PROC_UNLOCK(p);
646                 /*
647                  * XXXDTRACE: add dtrace_doubletrap_func here?
648                  */
649         } else {
650                 /*
651                  * Don't have to worry about process locking or stacks in the
652                  * kernel.
653                  */
654                 rv = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
655         }
656
657         if (rv == KERN_SUCCESS)
658                 return (0);
659
660         if (!user && handle_onfault(frame))
661                 return (0);
662
663         return (SIGSEGV);
664 }
665
666 /*
667  * For now, this only deals with the particular unaligned access case
668  * that gcc tends to generate.  Eventually it should handle all of the
669  * possibilities that can happen on a 32-bit PowerPC in big-endian mode.
670  */
671
672 static int
673 fix_unaligned(struct thread *td, struct trapframe *frame)
674 {
675         struct thread   *fputhread;
676         int             indicator, reg;
677         double          *fpr;
678
679         indicator = EXC_ALI_OPCODE_INDICATOR(frame->cpu.aim.dsisr);
680
681         switch (indicator) {
682         case EXC_ALI_LFD:
683         case EXC_ALI_STFD:
684                 reg = EXC_ALI_RST(frame->cpu.aim.dsisr);
685                 fpr = &td->td_pcb->pcb_fpu.fpr[reg];
686                 fputhread = PCPU_GET(fputhread);
687
688                 /* Juggle the FPU to ensure that we've initialized
689                  * the FPRs, and that their current state is in
690                  * the PCB.
691                  */
692                 if (fputhread != td) {
693                         if (fputhread)
694                                 save_fpu(fputhread);
695                         enable_fpu(td);
696                 }
697                 save_fpu(td);
698
699                 if (indicator == EXC_ALI_LFD) {
700                         if (copyin((void *)frame->cpu.aim.dar, fpr,
701                             sizeof(double)) != 0)
702                                 return -1;
703                         enable_fpu(td);
704                 } else {
705                         if (copyout(fpr, (void *)frame->cpu.aim.dar,
706                             sizeof(double)) != 0)
707                                 return -1;
708                 }
709                 return 0;
710                 break;
711         }
712
713         return -1;
714 }
715