diff options
Diffstat (limited to 'arch/mn10300/kernel/fpu.c')
-rw-r--r-- | arch/mn10300/kernel/fpu.c | 141 |
1 files changed, 56 insertions, 85 deletions
diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c index e705f25ad5ff..5f9c3fa19a85 100644 --- a/arch/mn10300/kernel/fpu.c +++ b/arch/mn10300/kernel/fpu.c | |||
@@ -12,56 +12,19 @@ | |||
12 | #include <asm/fpu.h> | 12 | #include <asm/fpu.h> |
13 | #include <asm/elf.h> | 13 | #include <asm/elf.h> |
14 | #include <asm/exceptions.h> | 14 | #include <asm/exceptions.h> |
15 | #include <asm/system.h> | ||
15 | 16 | ||
17 | #ifdef CONFIG_LAZY_SAVE_FPU | ||
16 | struct task_struct *fpu_state_owner; | 18 | struct task_struct *fpu_state_owner; |
19 | #endif | ||
17 | 20 | ||
18 | /* | 21 | /* |
19 | * handle an exception due to the FPU being disabled | 22 | * error functions in FPU disabled exception |
20 | */ | 23 | */ |
21 | asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code) | 24 | asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs) |
22 | { | 25 | { |
23 | struct task_struct *tsk = current; | 26 | die_if_no_fixup("An FPU Disabled exception happened in kernel space\n", |
24 | 27 | regs, EXCEP_FPU_DISABLED); | |
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 | } | 28 | } |
66 | 29 | ||
67 | /* | 30 | /* |
@@ -71,15 +34,16 @@ asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code) | |||
71 | */ | 34 | */ |
72 | asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) | 35 | asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) |
73 | { | 36 | { |
74 | struct task_struct *tsk = fpu_state_owner; | 37 | struct task_struct *tsk = current; |
75 | siginfo_t info; | 38 | siginfo_t info; |
39 | u32 fpcr; | ||
76 | 40 | ||
77 | if (!user_mode(regs)) | 41 | if (!user_mode(regs)) |
78 | die_if_no_fixup("An FPU Operation exception happened in" | 42 | die_if_no_fixup("An FPU Operation exception happened in" |
79 | " kernel space\n", | 43 | " kernel space\n", |
80 | regs, code); | 44 | regs, code); |
81 | 45 | ||
82 | if (!tsk) | 46 | if (!is_using_fpu(tsk)) |
83 | die_if_no_fixup("An FPU Operation exception happened," | 47 | die_if_no_fixup("An FPU Operation exception happened," |
84 | " but the FPU is not in use", | 48 | " but the FPU is not in use", |
85 | regs, code); | 49 | regs, code); |
@@ -89,48 +53,45 @@ asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) | |||
89 | info.si_addr = (void *) tsk->thread.uregs->pc; | 53 | info.si_addr = (void *) tsk->thread.uregs->pc; |
90 | info.si_code = FPE_FLTINV; | 54 | info.si_code = FPE_FLTINV; |
91 | 55 | ||
92 | #ifdef CONFIG_FPU | 56 | unlazy_fpu(tsk); |
93 | { | ||
94 | u32 fpcr; | ||
95 | 57 | ||
96 | /* get FPCR (we need to enable the FPU whilst we do this) */ | 58 | fpcr = tsk->thread.fpu_state.fpcr; |
97 | asm volatile(" or %1,epsw \n" | 59 | |
98 | #ifdef CONFIG_MN10300_PROC_MN103E010 | 60 | if (fpcr & FPCR_EC_Z) |
99 | " nop \n" | 61 | info.si_code = FPE_FLTDIV; |
100 | " nop \n" | 62 | else if (fpcr & FPCR_EC_O) |
101 | " nop \n" | 63 | info.si_code = FPE_FLTOVF; |
102 | #endif | 64 | else if (fpcr & FPCR_EC_U) |
103 | " fmov fpcr,%0 \n" | 65 | info.si_code = FPE_FLTUND; |
104 | #ifdef CONFIG_MN10300_PROC_MN103E010 | 66 | else if (fpcr & FPCR_EC_I) |
105 | " nop \n" | 67 | info.si_code = FPE_FLTRES; |
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 | 68 | ||
125 | force_sig_info(SIGFPE, &info, tsk); | 69 | force_sig_info(SIGFPE, &info, tsk); |
126 | } | 70 | } |
127 | 71 | ||
128 | /* | 72 | /* |
73 | * handle an FPU invalid_op exception | ||
74 | * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c | ||
75 | */ | ||
76 | asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code) | ||
77 | { | ||
78 | siginfo_t info; | ||
79 | |||
80 | if (!user_mode(regs)) | ||
81 | die_if_no_fixup("FPU invalid opcode", regs, code); | ||
82 | |||
83 | info.si_signo = SIGILL; | ||
84 | info.si_errno = 0; | ||
85 | info.si_code = ILL_COPROC; | ||
86 | info.si_addr = (void *) regs->pc; | ||
87 | force_sig_info(info.si_signo, &info, current); | ||
88 | } | ||
89 | |||
90 | /* | ||
129 | * save the FPU state to a signal context | 91 | * save the FPU state to a signal context |
130 | */ | 92 | */ |
131 | int fpu_setup_sigcontext(struct fpucontext *fpucontext) | 93 | int fpu_setup_sigcontext(struct fpucontext *fpucontext) |
132 | { | 94 | { |
133 | #ifdef CONFIG_FPU | ||
134 | struct task_struct *tsk = current; | 95 | struct task_struct *tsk = current; |
135 | 96 | ||
136 | if (!is_using_fpu(tsk)) | 97 | if (!is_using_fpu(tsk)) |
@@ -142,11 +103,19 @@ int fpu_setup_sigcontext(struct fpucontext *fpucontext) | |||
142 | */ | 103 | */ |
143 | preempt_disable(); | 104 | preempt_disable(); |
144 | 105 | ||
106 | #ifndef CONFIG_LAZY_SAVE_FPU | ||
107 | if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { | ||
108 | fpu_save(&tsk->thread.fpu_state); | ||
109 | tsk->thread.uregs->epsw &= ~EPSW_FE; | ||
110 | tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; | ||
111 | } | ||
112 | #else /* !CONFIG_LAZY_SAVE_FPU */ | ||
145 | if (fpu_state_owner == tsk) { | 113 | if (fpu_state_owner == tsk) { |
146 | fpu_save(&tsk->thread.fpu_state); | 114 | fpu_save(&tsk->thread.fpu_state); |
147 | fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; | 115 | fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; |
148 | fpu_state_owner = NULL; | 116 | fpu_state_owner = NULL; |
149 | } | 117 | } |
118 | #endif /* !CONFIG_LAZY_SAVE_FPU */ | ||
150 | 119 | ||
151 | preempt_enable(); | 120 | preempt_enable(); |
152 | 121 | ||
@@ -161,9 +130,6 @@ int fpu_setup_sigcontext(struct fpucontext *fpucontext) | |||
161 | return -1; | 130 | return -1; |
162 | 131 | ||
163 | return 1; | 132 | return 1; |
164 | #else | ||
165 | return 0; | ||
166 | #endif | ||
167 | } | 133 | } |
168 | 134 | ||
169 | /* | 135 | /* |
@@ -171,17 +137,23 @@ int fpu_setup_sigcontext(struct fpucontext *fpucontext) | |||
171 | */ | 137 | */ |
172 | void fpu_kill_state(struct task_struct *tsk) | 138 | void fpu_kill_state(struct task_struct *tsk) |
173 | { | 139 | { |
174 | #ifdef CONFIG_FPU | ||
175 | /* disown anything left in the FPU */ | 140 | /* disown anything left in the FPU */ |
176 | preempt_disable(); | 141 | preempt_disable(); |
177 | 142 | ||
143 | #ifndef CONFIG_LAZY_SAVE_FPU | ||
144 | if (tsk->thread.fpu_flags & THREAD_HAS_FPU) { | ||
145 | tsk->thread.uregs->epsw &= ~EPSW_FE; | ||
146 | tsk->thread.fpu_flags &= ~THREAD_HAS_FPU; | ||
147 | } | ||
148 | #else /* !CONFIG_LAZY_SAVE_FPU */ | ||
178 | if (fpu_state_owner == tsk) { | 149 | if (fpu_state_owner == tsk) { |
179 | fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; | 150 | fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; |
180 | fpu_state_owner = NULL; | 151 | fpu_state_owner = NULL; |
181 | } | 152 | } |
153 | #endif /* !CONFIG_LAZY_SAVE_FPU */ | ||
182 | 154 | ||
183 | preempt_enable(); | 155 | preempt_enable(); |
184 | #endif | 156 | |
185 | /* we no longer have a valid current FPU state */ | 157 | /* we no longer have a valid current FPU state */ |
186 | clear_using_fpu(tsk); | 158 | clear_using_fpu(tsk); |
187 | } | 159 | } |
@@ -195,8 +167,7 @@ int fpu_restore_sigcontext(struct fpucontext *fpucontext) | |||
195 | int ret; | 167 | int ret; |
196 | 168 | ||
197 | /* load up the old FPU state */ | 169 | /* load up the old FPU state */ |
198 | ret = copy_from_user(&tsk->thread.fpu_state, | 170 | ret = copy_from_user(&tsk->thread.fpu_state, fpucontext, |
199 | fpucontext, | ||
200 | min(sizeof(struct fpu_state_struct), | 171 | min(sizeof(struct fpu_state_struct), |
201 | sizeof(struct fpucontext))); | 172 | sizeof(struct fpucontext))); |
202 | if (!ret) | 173 | if (!ret) |