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 16struct task_struct *fpu_state_owner; 17 18/* 19 * handle an exception due to the FPU being disabled 20 */ 21asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code) 22{ 23 struct task_struct *tsk = current; 24 25 if (!user_mode(regs)) 26 die_if_no_fixup("An FPU Disabled exception happened in" 27 " kernel space\n", 28 regs, code); 29 30#ifdef CONFIG_FPU 31 preempt_disable(); 32 33 /* transfer the last process's FPU state to memory */ 34 if (fpu_state_owner) { 35 fpu_save(&fpu_state_owner->thread.fpu_state); 36 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 37 } 38 39 /* the current process now owns the FPU state */ 40 fpu_state_owner = tsk; 41 regs->epsw |= EPSW_FE; 42 43 /* load the FPU with the current process's FPU state or invent a new 44 * clean one if the process doesn't have one */ 45 if (is_using_fpu(tsk)) { 46 fpu_restore(&tsk->thread.fpu_state); 47 } else { 48 fpu_init_state(); 49 set_using_fpu(tsk); 50 } 51 52 preempt_enable(); 53#else 54 { 55 siginfo_t info; 56 57 info.si_signo = SIGFPE; 58 info.si_errno = 0; 59 info.si_addr = (void *) tsk->thread.uregs->pc; 60 info.si_code = FPE_FLTINV; 61 62 force_sig_info(SIGFPE, &info, tsk); 63 } 64#endif /* CONFIG_FPU */ 65} 66 67/* 68 * handle an FPU operational exception 69 * - there's a possibility that if the FPU is asynchronous, the signal might 70 * be meant for a process other than the current one 71 */ 72asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) 73{ 74 struct task_struct *tsk = fpu_state_owner; 75 siginfo_t info; 76 77 if (!user_mode(regs)) 78 die_if_no_fixup("An FPU Operation exception happened in" 79 " kernel space\n", 80 regs, code); 81 82 if (!tsk) 83 die_if_no_fixup("An FPU Operation exception happened," 84 " but the FPU is not in use", 85 regs, code); 86 87 info.si_signo = SIGFPE; 88 info.si_errno = 0; 89 info.si_addr = (void *) tsk->thread.uregs->pc; 90 info.si_code = FPE_FLTINV; 91 92#ifdef CONFIG_FPU 93 { 94 u32 fpcr; 95 96 /* get FPCR (we need to enable the FPU whilst we do this) */ 97 asm volatile(" or %1,epsw \n" 98#ifdef CONFIG_MN10300_PROC_MN103E010 99 " nop \n" 100 " nop \n" 101 " nop \n" 102#endif 103 " fmov fpcr,%0 \n" 104#ifdef CONFIG_MN10300_PROC_MN103E010 105 " nop \n" 106 " nop \n" 107 " nop \n" 108#endif 109 " and %2,epsw \n" 110 : "=&d"(fpcr) 111 : "i"(EPSW_FE), "i"(~EPSW_FE) 112 ); 113 114 if (fpcr & FPCR_EC_Z) 115 info.si_code = FPE_FLTDIV; 116 else if (fpcr & FPCR_EC_O) 117 info.si_code = FPE_FLTOVF; 118 else if (fpcr & FPCR_EC_U) 119 info.si_code = FPE_FLTUND; 120 else if (fpcr & FPCR_EC_I) 121 info.si_code = FPE_FLTRES; 122 } 123#endif 124 125 force_sig_info(SIGFPE, &info, tsk); 126} 127 128/* 129 * save the FPU state to a signal context 130 */ 131int fpu_setup_sigcontext(struct fpucontext *fpucontext) 132{ 133#ifdef CONFIG_FPU 134 struct task_struct *tsk = current; 135 136 if (!is_using_fpu(tsk)) 137 return 0; 138 139 /* transfer the current FPU state to memory and cause fpu_init() to be 140 * triggered by the next attempted FPU operation by the current 141 * process. 142 */ 143 preempt_disable(); 144 145 if (fpu_state_owner == tsk) { 146 fpu_save(&tsk->thread.fpu_state); 147 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 148 fpu_state_owner = NULL; 149 } 150 151 preempt_enable(); 152 153 /* we no longer have a valid current FPU state */ 154 clear_using_fpu(tsk); 155 156 /* transfer the saved FPU state onto the userspace stack */ 157 if (copy_to_user(fpucontext, 158 &tsk->thread.fpu_state, 159 min(sizeof(struct fpu_state_struct), 160 sizeof(struct fpucontext)))) 161 return -1; 162 163 return 1; 164#else 165 return 0; 166#endif 167} 168 169/* 170 * kill a process's FPU state during restoration after signal handling 171 */ 172void fpu_kill_state(struct task_struct *tsk) 173{ 174#ifdef CONFIG_FPU 175 /* disown anything left in the FPU */ 176 preempt_disable(); 177 178 if (fpu_state_owner == tsk) { 179 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; 180 fpu_state_owner = NULL; 181 } 182 183 preempt_enable(); 184#endif 185 /* we no longer have a valid current FPU state */ 186 clear_using_fpu(tsk); 187} 188 189/* 190 * restore the FPU state from a signal context 191 */ 192int fpu_restore_sigcontext(struct fpucontext *fpucontext) 193{ 194 struct task_struct *tsk = current; 195 int ret; 196 197 /* load up the old FPU state */ 198 ret = copy_from_user(&tsk->thread.fpu_state, 199 fpucontext, 200 min(sizeof(struct fpu_state_struct), 201 sizeof(struct fpucontext))); 202 if (!ret) 203 set_using_fpu(tsk); 204 205 return ret; 206} 207 208/* 209 * fill in the FPU structure for a core dump 210 */ 211int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg) 212{ 213 struct task_struct *tsk = current; 214 int fpvalid; 215 216 fpvalid = is_using_fpu(tsk); 217 if (fpvalid) { 218 unlazy_fpu(tsk); 219 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg)); 220 } 221 222 return fpvalid; 223} 224