1/* MN10300 FPU management 2 * 3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public Licence 8 * as published by the Free Software Foundation; either version 9 * 2 of the Licence, or (at your option) any later version. 10 */ 11#include <asm/uaccess.h> 12#include <asm/fpu.h> 13#include <asm/elf.h> 14#include <asm/exceptions.h> 15 16#ifdef CONFIG_LAZY_SAVE_FPU 17struct task_struct *fpu_state_owner; 18#endif 19 20/* 21 * error functions in FPU disabled exception 22 */ 23asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs) 24{ 25 die_if_no_fixup("An FPU Disabled exception happened in kernel space\n", 26 regs, EXCEP_FPU_DISABLED); 27} 28 29/* 30 * handle an FPU operational exception 31 * - there's a possibility that if the FPU is asynchronous, the signal might 32 * be meant for a process other than the current one 33 */ 34asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) 35{ 36 struct task_struct *tsk = current; 37 siginfo_t info; 38 u32 fpcr; 39 40 if (!user_mode(regs)) 41 die_if_no_fixup("An FPU Operation exception happened in" 42 " kernel space\n", 43 regs, code); 44 45 if (!is_using_fpu(tsk)) 46 die_if_no_fixup("An FPU Operation exception happened," 47 " but the FPU is not in use", 48 regs, code); 49 50 info.si_signo = SIGFPE; 51 info.si_errno = 0; 52 info.si_addr = (void *) tsk->thread.uregs->pc; 53 info.si_code = FPE_FLTINV; 54 55 unlazy_fpu(tsk); 56 57 fpcr = tsk->thread.fpu_state.fpcr; 58 59 if (fpcr & FPCR_EC_Z) 60 info.si_code = FPE_FLTDIV; 61 else if (fpcr & FPCR_EC_O) 62 info.si_code = FPE_FLTOVF; 63 else if (fpcr & FPCR_EC_U) 64 info.si_code = FPE_FLTUND; 65 else if (fpcr & FPCR_EC_I) 66 info.si_code = FPE_FLTRES; 67 68 force_sig_info(SIGFPE, &info, tsk); 69} 70 71/* 72 * save the FPU state to a signal context 73 */ 74int fpu_setup_sigcontext(struct fpucontext *fpucontext) 75{ 76 struct task_struct *tsk = current; 77 78 if (!is_using_fpu(tsk)) 79 return 0; 80 81 /* transfer the current FPU state to memory and cause fpu_init() to be 82 * triggered by the next attempted FPU operation by the current 83 * process. 84 */ 85 preempt_disable(); 86 87#ifndef CONFIG_LAZY_SAVE_FPU 88 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { 89 fpu_save(&tsk->thread.fpu_state); 90 tsk->thread.uregs->epsw &= ~EPSW_FE; 91 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; 92 } 93#else /* !CONFIG_LAZY_SAVE_FPU */ 94 if (fpu_state_owner == tsk) { 95 fpu_save(&tsk->thread.fpu_state); 96 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 97 fpu_state_owner = NULL; 98 } 99#endif /* !CONFIG_LAZY_SAVE_FPU */ 100 101 preempt_enable(); 102 103 /* we no longer have a valid current FPU state */ 104 clear_using_fpu(tsk); 105 106 /* transfer the saved FPU state onto the userspace stack */ 107 if (copy_to_user(fpucontext, 108 &tsk->thread.fpu_state, 109 min(sizeof(struct fpu_state_struct), 110 sizeof(struct fpucontext)))) 111 return -1; 112 113 return 1; 114} 115 116/* 117 * kill a process's FPU state during restoration after signal handling 118 */ 119void fpu_kill_state(struct task_struct *tsk) 120{ 121 /* disown anything left in the FPU */ 122 preempt_disable(); 123 124#ifndef CONFIG_LAZY_SAVE_FPU 125 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { 126 tsk->thread.uregs->epsw &= ~EPSW_FE; 127 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; 128 } 129#else /* !CONFIG_LAZY_SAVE_FPU */ 130 if (fpu_state_owner == tsk) { 131 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 132 fpu_state_owner = NULL; 133 } 134#endif /* !CONFIG_LAZY_SAVE_FPU */ 135 136 preempt_enable(); 137 138 /* we no longer have a valid current FPU state */ 139 clear_using_fpu(tsk); 140} 141 142/* 143 * restore the FPU state from a signal context 144 */ 145int fpu_restore_sigcontext(struct fpucontext *fpucontext) 146{ 147 struct task_struct *tsk = current; 148 int ret; 149 150 /* load up the old FPU state */ 151 ret = copy_from_user(&tsk->thread.fpu_state, fpucontext, 152 min(sizeof(struct fpu_state_struct), 153 sizeof(struct fpucontext))); 154 if (!ret) 155 set_using_fpu(tsk); 156 157 return ret; 158} 159 160/* 161 * fill in the FPU structure for a core dump 162 */ 163int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg) 164{ 165 struct task_struct *tsk = current; 166 int fpvalid; 167 168 fpvalid = is_using_fpu(tsk); 169 if (fpvalid) { 170 unlazy_fpu(tsk); 171 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg)); 172 } 173 174 return fpvalid; 175} 176