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