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