• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17 struct task_struct *fpu_state_owner;
18 #endif
19 
20 /*
21  * error functions in FPU disabled exception
22  */
fpu_disabled_in_kernel(struct pt_regs * regs)23 asmlinkage 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  */
fpu_exception(struct pt_regs * regs,enum exception_code code)34 asmlinkage 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  */
fpu_setup_sigcontext(struct fpucontext * fpucontext)74 int 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  */
fpu_kill_state(struct task_struct * tsk)119 void 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  */
fpu_restore_sigcontext(struct fpucontext * fpucontext)145 int 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  */
dump_fpu(struct pt_regs * regs,elf_fpregset_t * fpreg)163 int 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