]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/arm/vfp/vfpmodule.c | |
3 | * | |
4 | * Copyright (C) 2004 ARM Limited. | |
5 | * Written by Deep Blue Solutions Limited. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include <linux/module.h> | |
1da177e4 LT |
12 | #include <linux/types.h> |
13 | #include <linux/kernel.h> | |
14 | #include <linux/signal.h> | |
15 | #include <linux/sched.h> | |
16 | #include <linux/init.h> | |
d6551e88 RK |
17 | |
18 | #include <asm/thread_notify.h> | |
1da177e4 LT |
19 | #include <asm/vfp.h> |
20 | ||
21 | #include "vfpinstr.h" | |
22 | #include "vfp.h" | |
23 | ||
24 | /* | |
25 | * Our undef handlers (in entry.S) | |
26 | */ | |
27 | void vfp_testing_entry(void); | |
28 | void vfp_support_entry(void); | |
29 | ||
30 | void (*vfp_vector)(void) = vfp_testing_entry; | |
31 | union vfp_state *last_VFP_context; | |
32 | ||
33 | /* | |
34 | * Dual-use variable. | |
35 | * Used in startup: set to non-zero if VFP checks fail | |
36 | * After startup, holds VFP architecture | |
37 | */ | |
38 | unsigned int VFP_arch; | |
39 | ||
d6551e88 | 40 | static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) |
1da177e4 | 41 | { |
d6551e88 RK |
42 | struct thread_info *thread = v; |
43 | union vfp_state *vfp = &thread->vfpstate; | |
1da177e4 | 44 | |
d6551e88 RK |
45 | switch (cmd) { |
46 | case THREAD_NOTIFY_FLUSH: | |
47 | /* | |
48 | * Per-thread VFP initialisation. | |
49 | */ | |
50 | memset(vfp, 0, sizeof(union vfp_state)); | |
1da177e4 | 51 | |
d6551e88 RK |
52 | vfp->hard.fpexc = FPEXC_ENABLE; |
53 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; | |
1da177e4 | 54 | |
d6551e88 RK |
55 | /* |
56 | * Disable VFP to ensure we initialise it first. | |
57 | */ | |
58 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); | |
1da177e4 | 59 | |
d6551e88 RK |
60 | /* |
61 | * FALLTHROUGH: Ensure we don't try to overwrite our newly | |
62 | * initialised state information on the first fault. | |
63 | */ | |
64 | ||
65 | case THREAD_NOTIFY_RELEASE: | |
66 | /* | |
67 | * Per-thread VFP cleanup. | |
68 | */ | |
69 | if (last_VFP_context == vfp) | |
70 | last_VFP_context = NULL; | |
71 | break; | |
72 | ||
73 | case THREAD_NOTIFY_SWITCH: | |
74 | /* | |
75 | * Always disable VFP so we can lazily save/restore the | |
76 | * old state. | |
77 | */ | |
78 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); | |
79 | break; | |
80 | } | |
81 | ||
82 | return NOTIFY_DONE; | |
1da177e4 LT |
83 | } |
84 | ||
d6551e88 RK |
85 | static struct notifier_block vfp_notifier_block = { |
86 | .notifier_call = vfp_notifier, | |
87 | }; | |
88 | ||
1da177e4 LT |
89 | /* |
90 | * Raise a SIGFPE for the current process. | |
91 | * sicode describes the signal being raised. | |
92 | */ | |
93 | void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) | |
94 | { | |
95 | siginfo_t info; | |
96 | ||
97 | memset(&info, 0, sizeof(info)); | |
98 | ||
99 | info.si_signo = SIGFPE; | |
100 | info.si_code = sicode; | |
101 | info.si_addr = (void *)(instruction_pointer(regs) - 4); | |
102 | ||
103 | /* | |
104 | * This is the same as NWFPE, because it's not clear what | |
105 | * this is used for | |
106 | */ | |
107 | current->thread.error_code = 0; | |
108 | current->thread.trap_no = 6; | |
109 | ||
da41119a | 110 | send_sig_info(SIGFPE, &info, current); |
1da177e4 LT |
111 | } |
112 | ||
113 | static void vfp_panic(char *reason) | |
114 | { | |
115 | int i; | |
116 | ||
117 | printk(KERN_ERR "VFP: Error: %s\n", reason); | |
118 | printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n", | |
119 | fmrx(FPEXC), fmrx(FPSCR), fmrx(FPINST)); | |
120 | for (i = 0; i < 32; i += 2) | |
121 | printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n", | |
122 | i, vfp_get_float(i), i+1, vfp_get_float(i+1)); | |
123 | } | |
124 | ||
125 | /* | |
126 | * Process bitmask of exception conditions. | |
127 | */ | |
128 | static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) | |
129 | { | |
130 | int si_code = 0; | |
131 | ||
132 | pr_debug("VFP: raising exceptions %08x\n", exceptions); | |
133 | ||
134 | if (exceptions == (u32)-1) { | |
135 | vfp_panic("unhandled bounce"); | |
136 | vfp_raise_sigfpe(0, regs); | |
137 | return; | |
138 | } | |
139 | ||
140 | /* | |
141 | * If any of the status flags are set, update the FPSCR. | |
142 | * Comparison instructions always return at least one of | |
143 | * these flags set. | |
144 | */ | |
145 | if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V)) | |
146 | fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V); | |
147 | ||
148 | fpscr |= exceptions; | |
149 | ||
150 | fmxr(FPSCR, fpscr); | |
151 | ||
152 | #define RAISE(stat,en,sig) \ | |
153 | if (exceptions & stat && fpscr & en) \ | |
154 | si_code = sig; | |
155 | ||
156 | /* | |
157 | * These are arranged in priority order, least to highest. | |
158 | */ | |
159 | RAISE(FPSCR_IXC, FPSCR_IXE, FPE_FLTRES); | |
160 | RAISE(FPSCR_UFC, FPSCR_UFE, FPE_FLTUND); | |
161 | RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); | |
162 | RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); | |
163 | ||
164 | if (si_code) | |
165 | vfp_raise_sigfpe(si_code, regs); | |
166 | } | |
167 | ||
168 | /* | |
169 | * Emulate a VFP instruction. | |
170 | */ | |
171 | static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) | |
172 | { | |
173 | u32 exceptions = (u32)-1; | |
174 | ||
175 | pr_debug("VFP: emulate: INST=0x%08x SCR=0x%08x\n", inst, fpscr); | |
176 | ||
177 | if (INST_CPRTDO(inst)) { | |
178 | if (!INST_CPRT(inst)) { | |
179 | /* | |
180 | * CPDO | |
181 | */ | |
182 | if (vfp_single(inst)) { | |
183 | exceptions = vfp_single_cpdo(inst, fpscr); | |
184 | } else { | |
185 | exceptions = vfp_double_cpdo(inst, fpscr); | |
186 | } | |
187 | } else { | |
188 | /* | |
189 | * A CPRT instruction can not appear in FPINST2, nor | |
190 | * can it cause an exception. Therefore, we do not | |
191 | * have to emulate it. | |
192 | */ | |
193 | } | |
194 | } else { | |
195 | /* | |
196 | * A CPDT instruction can not appear in FPINST2, nor can | |
197 | * it cause an exception. Therefore, we do not have to | |
198 | * emulate it. | |
199 | */ | |
200 | } | |
928bd1b4 | 201 | return exceptions & ~VFP_NAN_FLAG; |
1da177e4 LT |
202 | } |
203 | ||
204 | /* | |
205 | * Package up a bounce condition. | |
206 | */ | |
207 | void VFP9_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) | |
208 | { | |
209 | u32 fpscr, orig_fpscr, exceptions, inst; | |
210 | ||
211 | pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); | |
212 | ||
213 | /* | |
214 | * Enable access to the VFP so we can handle the bounce. | |
215 | */ | |
216 | fmxr(FPEXC, fpexc & ~(FPEXC_EXCEPTION|FPEXC_INV|FPEXC_UFC|FPEXC_IOC)); | |
217 | ||
218 | orig_fpscr = fpscr = fmrx(FPSCR); | |
219 | ||
220 | /* | |
221 | * If we are running with inexact exceptions enabled, we need to | |
222 | * emulate the trigger instruction. Note that as we're emulating | |
223 | * the trigger instruction, we need to increment PC. | |
224 | */ | |
225 | if (fpscr & FPSCR_IXE) { | |
226 | regs->ARM_pc += 4; | |
227 | goto emulate; | |
228 | } | |
229 | ||
230 | barrier(); | |
231 | ||
232 | /* | |
233 | * Modify fpscr to indicate the number of iterations remaining | |
234 | */ | |
235 | if (fpexc & FPEXC_EXCEPTION) { | |
236 | u32 len; | |
237 | ||
238 | len = fpexc + (1 << FPEXC_LENGTH_BIT); | |
239 | ||
240 | fpscr &= ~FPSCR_LENGTH_MASK; | |
241 | fpscr |= (len & FPEXC_LENGTH_MASK) << (FPSCR_LENGTH_BIT - FPEXC_LENGTH_BIT); | |
242 | } | |
243 | ||
244 | /* | |
245 | * Handle the first FP instruction. We used to take note of the | |
246 | * FPEXC bounce reason, but this appears to be unreliable. | |
247 | * Emulate the bounced instruction instead. | |
248 | */ | |
249 | inst = fmrx(FPINST); | |
250 | exceptions = vfp_emulate_instruction(inst, fpscr, regs); | |
251 | if (exceptions) | |
252 | vfp_raise_exceptions(exceptions, inst, orig_fpscr, regs); | |
253 | ||
254 | /* | |
255 | * If there isn't a second FP instruction, exit now. | |
256 | */ | |
257 | if (!(fpexc & FPEXC_FPV2)) | |
258 | return; | |
259 | ||
260 | /* | |
261 | * The barrier() here prevents fpinst2 being read | |
262 | * before the condition above. | |
263 | */ | |
264 | barrier(); | |
265 | trigger = fmrx(FPINST2); | |
b7d7ef87 | 266 | orig_fpscr = fpscr = fmrx(FPSCR); |
1da177e4 LT |
267 | |
268 | emulate: | |
269 | exceptions = vfp_emulate_instruction(trigger, fpscr, regs); | |
270 | if (exceptions) | |
271 | vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); | |
272 | } | |
273 | ||
274 | /* | |
275 | * VFP support code initialisation. | |
276 | */ | |
277 | static int __init vfp_init(void) | |
278 | { | |
279 | unsigned int vfpsid; | |
280 | ||
281 | /* | |
282 | * First check that there is a VFP that we can use. | |
283 | * The handler is already setup to just log calls, so | |
284 | * we just need to read the VFPSID register. | |
285 | */ | |
286 | vfpsid = fmrx(FPSID); | |
287 | ||
288 | printk(KERN_INFO "VFP support v0.3: "); | |
289 | if (VFP_arch) { | |
290 | printk("not present\n"); | |
291 | } else if (vfpsid & FPSID_NODOUBLE) { | |
292 | printk("no double precision support\n"); | |
293 | } else { | |
294 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ | |
295 | printk("implementor %02x architecture %d part %02x variant %x rev %x\n", | |
296 | (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT, | |
297 | (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT, | |
298 | (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, | |
299 | (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, | |
300 | (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); | |
301 | vfp_vector = vfp_support_entry; | |
d6551e88 RK |
302 | |
303 | thread_register_notifier(&vfp_notifier_block); | |
1da177e4 LT |
304 | } |
305 | return 0; | |
306 | } | |
307 | ||
308 | late_initcall(vfp_init); |