]>
Commit | Line | Data |
---|---|---|
53631b54 CM |
1 | /* |
2 | * FP/SIMD context switching and fault handling | |
3 | * | |
4 | * Copyright (C) 2012 ARM Ltd. | |
5 | * Author: Catalin Marinas <[email protected]> | |
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 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
cb84d11e | 20 | #include <linux/bottom_half.h> |
32365e64 | 21 | #include <linux/cpu.h> |
fb1ab1ab | 22 | #include <linux/cpu_pm.h> |
53631b54 CM |
23 | #include <linux/kernel.h> |
24 | #include <linux/init.h> | |
cb84d11e | 25 | #include <linux/percpu.h> |
4328825d | 26 | #include <linux/preempt.h> |
3f07c014 | 27 | #include <linux/sched/signal.h> |
53631b54 CM |
28 | #include <linux/signal.h> |
29 | ||
30 | #include <asm/fpsimd.h> | |
31 | #include <asm/cputype.h> | |
4328825d | 32 | #include <asm/simd.h> |
53631b54 CM |
33 | |
34 | #define FPEXC_IOF (1 << 0) | |
35 | #define FPEXC_DZF (1 << 1) | |
36 | #define FPEXC_OFF (1 << 2) | |
37 | #define FPEXC_UFF (1 << 3) | |
38 | #define FPEXC_IXF (1 << 4) | |
39 | #define FPEXC_IDF (1 << 7) | |
40 | ||
005f78cd AB |
41 | /* |
42 | * In order to reduce the number of times the FPSIMD state is needlessly saved | |
43 | * and restored, we need to keep track of two things: | |
44 | * (a) for each task, we need to remember which CPU was the last one to have | |
45 | * the task's FPSIMD state loaded into its FPSIMD registers; | |
46 | * (b) for each CPU, we need to remember which task's userland FPSIMD state has | |
47 | * been loaded into its FPSIMD registers most recently, or whether it has | |
48 | * been used to perform kernel mode NEON in the meantime. | |
49 | * | |
50 | * For (a), we add a 'cpu' field to struct fpsimd_state, which gets updated to | |
ef769e32 | 51 | * the id of the current CPU every time the state is loaded onto a CPU. For (b), |
005f78cd AB |
52 | * we add the per-cpu variable 'fpsimd_last_state' (below), which contains the |
53 | * address of the userland FPSIMD state of the task that was loaded onto the CPU | |
54 | * the most recently, or NULL if kernel mode NEON has been performed after that. | |
55 | * | |
56 | * With this in place, we no longer have to restore the next FPSIMD state right | |
57 | * when switching between tasks. Instead, we can defer this check to userland | |
58 | * resume, at which time we verify whether the CPU's fpsimd_last_state and the | |
59 | * task's fpsimd_state.cpu are still mutually in sync. If this is the case, we | |
60 | * can omit the FPSIMD restore. | |
61 | * | |
62 | * As an optimization, we use the thread_info flag TIF_FOREIGN_FPSTATE to | |
63 | * indicate whether or not the userland FPSIMD state of the current task is | |
64 | * present in the registers. The flag is set unless the FPSIMD registers of this | |
65 | * CPU currently contain the most recent userland FPSIMD state of the current | |
66 | * task. | |
67 | * | |
cb84d11e DM |
68 | * In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may |
69 | * save the task's FPSIMD context back to task_struct from softirq context. | |
70 | * To prevent this from racing with the manipulation of the task's FPSIMD state | |
71 | * from task context and thereby corrupting the state, it is necessary to | |
72 | * protect any manipulation of a task's fpsimd_state or TIF_FOREIGN_FPSTATE | |
73 | * flag with local_bh_disable() unless softirqs are already masked. | |
74 | * | |
005f78cd AB |
75 | * For a certain task, the sequence may look something like this: |
76 | * - the task gets scheduled in; if both the task's fpsimd_state.cpu field | |
77 | * contains the id of the current CPU, and the CPU's fpsimd_last_state per-cpu | |
78 | * variable points to the task's fpsimd_state, the TIF_FOREIGN_FPSTATE flag is | |
79 | * cleared, otherwise it is set; | |
80 | * | |
81 | * - the task returns to userland; if TIF_FOREIGN_FPSTATE is set, the task's | |
82 | * userland FPSIMD state is copied from memory to the registers, the task's | |
83 | * fpsimd_state.cpu field is set to the id of the current CPU, the current | |
84 | * CPU's fpsimd_last_state pointer is set to this task's fpsimd_state and the | |
85 | * TIF_FOREIGN_FPSTATE flag is cleared; | |
86 | * | |
87 | * - the task executes an ordinary syscall; upon return to userland, the | |
88 | * TIF_FOREIGN_FPSTATE flag will still be cleared, so no FPSIMD state is | |
89 | * restored; | |
90 | * | |
91 | * - the task executes a syscall which executes some NEON instructions; this is | |
92 | * preceded by a call to kernel_neon_begin(), which copies the task's FPSIMD | |
93 | * register contents to memory, clears the fpsimd_last_state per-cpu variable | |
94 | * and sets the TIF_FOREIGN_FPSTATE flag; | |
95 | * | |
96 | * - the task gets preempted after kernel_neon_end() is called; as we have not | |
97 | * returned from the 2nd syscall yet, TIF_FOREIGN_FPSTATE is still set so | |
98 | * whatever is in the FPSIMD registers is not saved to memory, but discarded. | |
99 | */ | |
100 | static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state); | |
101 | ||
53631b54 CM |
102 | /* |
103 | * Trapped FP/ASIMD access. | |
104 | */ | |
105 | void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs) | |
106 | { | |
107 | /* TODO: implement lazy context saving/restoring */ | |
108 | WARN_ON(1); | |
109 | } | |
110 | ||
111 | /* | |
112 | * Raise a SIGFPE for the current process. | |
113 | */ | |
114 | void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs) | |
115 | { | |
116 | siginfo_t info; | |
117 | unsigned int si_code = 0; | |
118 | ||
119 | if (esr & FPEXC_IOF) | |
120 | si_code = FPE_FLTINV; | |
121 | else if (esr & FPEXC_DZF) | |
122 | si_code = FPE_FLTDIV; | |
123 | else if (esr & FPEXC_OFF) | |
124 | si_code = FPE_FLTOVF; | |
125 | else if (esr & FPEXC_UFF) | |
126 | si_code = FPE_FLTUND; | |
127 | else if (esr & FPEXC_IXF) | |
128 | si_code = FPE_FLTRES; | |
129 | ||
130 | memset(&info, 0, sizeof(info)); | |
131 | info.si_signo = SIGFPE; | |
132 | info.si_code = si_code; | |
133 | info.si_addr = (void __user *)instruction_pointer(regs); | |
134 | ||
135 | send_sig_info(SIGFPE, &info, current); | |
136 | } | |
137 | ||
138 | void fpsimd_thread_switch(struct task_struct *next) | |
139 | { | |
82e0191a SP |
140 | if (!system_supports_fpsimd()) |
141 | return; | |
005f78cd AB |
142 | /* |
143 | * Save the current FPSIMD state to memory, but only if whatever is in | |
144 | * the registers is in fact the most recent userland FPSIMD state of | |
145 | * 'current'. | |
146 | */ | |
147 | if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE)) | |
53631b54 | 148 | fpsimd_save_state(¤t->thread.fpsimd_state); |
005f78cd AB |
149 | |
150 | if (next->mm) { | |
151 | /* | |
152 | * If we are switching to a task whose most recent userland | |
153 | * FPSIMD state is already in the registers of *this* cpu, | |
154 | * we can skip loading the state from memory. Otherwise, set | |
155 | * the TIF_FOREIGN_FPSTATE flag so the state will be loaded | |
156 | * upon the next return to userland. | |
157 | */ | |
158 | struct fpsimd_state *st = &next->thread.fpsimd_state; | |
159 | ||
160 | if (__this_cpu_read(fpsimd_last_state) == st | |
161 | && st->cpu == smp_processor_id()) | |
162 | clear_ti_thread_flag(task_thread_info(next), | |
163 | TIF_FOREIGN_FPSTATE); | |
164 | else | |
165 | set_ti_thread_flag(task_thread_info(next), | |
166 | TIF_FOREIGN_FPSTATE); | |
167 | } | |
53631b54 CM |
168 | } |
169 | ||
170 | void fpsimd_flush_thread(void) | |
171 | { | |
82e0191a SP |
172 | if (!system_supports_fpsimd()) |
173 | return; | |
cb84d11e DM |
174 | |
175 | local_bh_disable(); | |
176 | ||
53631b54 | 177 | memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); |
674c242c | 178 | fpsimd_flush_task_state(current); |
005f78cd | 179 | set_thread_flag(TIF_FOREIGN_FPSTATE); |
cb84d11e DM |
180 | |
181 | local_bh_enable(); | |
53631b54 CM |
182 | } |
183 | ||
c51f9269 | 184 | /* |
005f78cd AB |
185 | * Save the userland FPSIMD state of 'current' to memory, but only if the state |
186 | * currently held in the registers does in fact belong to 'current' | |
c51f9269 AB |
187 | */ |
188 | void fpsimd_preserve_current_state(void) | |
189 | { | |
82e0191a SP |
190 | if (!system_supports_fpsimd()) |
191 | return; | |
cb84d11e DM |
192 | |
193 | local_bh_disable(); | |
194 | ||
005f78cd AB |
195 | if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) |
196 | fpsimd_save_state(¤t->thread.fpsimd_state); | |
cb84d11e DM |
197 | |
198 | local_bh_enable(); | |
c51f9269 AB |
199 | } |
200 | ||
201 | /* | |
005f78cd AB |
202 | * Load the userland FPSIMD state of 'current' from memory, but only if the |
203 | * FPSIMD state already held in the registers is /not/ the most recent FPSIMD | |
204 | * state of 'current' | |
205 | */ | |
206 | void fpsimd_restore_current_state(void) | |
207 | { | |
82e0191a SP |
208 | if (!system_supports_fpsimd()) |
209 | return; | |
cb84d11e DM |
210 | |
211 | local_bh_disable(); | |
212 | ||
005f78cd AB |
213 | if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) { |
214 | struct fpsimd_state *st = ¤t->thread.fpsimd_state; | |
215 | ||
216 | fpsimd_load_state(st); | |
50464185 | 217 | __this_cpu_write(fpsimd_last_state, st); |
005f78cd AB |
218 | st->cpu = smp_processor_id(); |
219 | } | |
cb84d11e DM |
220 | |
221 | local_bh_enable(); | |
005f78cd AB |
222 | } |
223 | ||
224 | /* | |
225 | * Load an updated userland FPSIMD state for 'current' from memory and set the | |
226 | * flag that indicates that the FPSIMD register contents are the most recent | |
227 | * FPSIMD state of 'current' | |
c51f9269 AB |
228 | */ |
229 | void fpsimd_update_current_state(struct fpsimd_state *state) | |
230 | { | |
82e0191a SP |
231 | if (!system_supports_fpsimd()) |
232 | return; | |
cb84d11e DM |
233 | |
234 | local_bh_disable(); | |
235 | ||
c51f9269 | 236 | fpsimd_load_state(state); |
005f78cd AB |
237 | if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) { |
238 | struct fpsimd_state *st = ¤t->thread.fpsimd_state; | |
239 | ||
50464185 | 240 | __this_cpu_write(fpsimd_last_state, st); |
005f78cd AB |
241 | st->cpu = smp_processor_id(); |
242 | } | |
cb84d11e DM |
243 | |
244 | local_bh_enable(); | |
c51f9269 AB |
245 | } |
246 | ||
005f78cd AB |
247 | /* |
248 | * Invalidate live CPU copies of task t's FPSIMD state | |
249 | */ | |
250 | void fpsimd_flush_task_state(struct task_struct *t) | |
251 | { | |
252 | t->thread.fpsimd_state.cpu = NR_CPUS; | |
253 | } | |
254 | ||
4cfb3613 AB |
255 | #ifdef CONFIG_KERNEL_MODE_NEON |
256 | ||
cb84d11e | 257 | DEFINE_PER_CPU(bool, kernel_neon_busy); |
11cefd5a | 258 | EXPORT_PER_CPU_SYMBOL(kernel_neon_busy); |
190f1ca8 | 259 | |
4cfb3613 AB |
260 | /* |
261 | * Kernel-side NEON support functions | |
262 | */ | |
cb84d11e DM |
263 | |
264 | /* | |
265 | * kernel_neon_begin(): obtain the CPU FPSIMD registers for use by the calling | |
266 | * context | |
267 | * | |
268 | * Must not be called unless may_use_simd() returns true. | |
269 | * Task context in the FPSIMD registers is saved back to memory as necessary. | |
270 | * | |
271 | * A matching call to kernel_neon_end() must be made before returning from the | |
272 | * calling context. | |
273 | * | |
274 | * The caller may freely use the FPSIMD registers until kernel_neon_end() is | |
275 | * called. | |
276 | */ | |
277 | void kernel_neon_begin(void) | |
4cfb3613 | 278 | { |
82e0191a SP |
279 | if (WARN_ON(!system_supports_fpsimd())) |
280 | return; | |
4cfb3613 | 281 | |
cb84d11e DM |
282 | BUG_ON(!may_use_simd()); |
283 | ||
284 | local_bh_disable(); | |
285 | ||
286 | __this_cpu_write(kernel_neon_busy, true); | |
287 | ||
288 | /* Save unsaved task fpsimd state, if any: */ | |
289 | if (current->mm && !test_and_set_thread_flag(TIF_FOREIGN_FPSTATE)) | |
290 | fpsimd_save_state(¤t->thread.fpsimd_state); | |
291 | ||
292 | /* Invalidate any task state remaining in the fpsimd regs: */ | |
293 | __this_cpu_write(fpsimd_last_state, NULL); | |
294 | ||
295 | preempt_disable(); | |
296 | ||
297 | local_bh_enable(); | |
4cfb3613 | 298 | } |
cb84d11e | 299 | EXPORT_SYMBOL(kernel_neon_begin); |
4cfb3613 | 300 | |
cb84d11e DM |
301 | /* |
302 | * kernel_neon_end(): give the CPU FPSIMD registers back to the current task | |
303 | * | |
304 | * Must be called from a context in which kernel_neon_begin() was previously | |
305 | * called, with no call to kernel_neon_end() in the meantime. | |
306 | * | |
307 | * The caller must not use the FPSIMD registers after this function is called, | |
308 | * unless kernel_neon_begin() is called again in the meantime. | |
309 | */ | |
4cfb3613 AB |
310 | void kernel_neon_end(void) |
311 | { | |
cb84d11e DM |
312 | bool busy; |
313 | ||
82e0191a SP |
314 | if (!system_supports_fpsimd()) |
315 | return; | |
cb84d11e DM |
316 | |
317 | busy = __this_cpu_xchg(kernel_neon_busy, false); | |
318 | WARN_ON(!busy); /* No matching kernel_neon_begin()? */ | |
319 | ||
320 | preempt_enable(); | |
4cfb3613 AB |
321 | } |
322 | EXPORT_SYMBOL(kernel_neon_end); | |
323 | ||
e580b8bc DM |
324 | #ifdef CONFIG_EFI |
325 | ||
3b66023d DM |
326 | static DEFINE_PER_CPU(struct fpsimd_state, efi_fpsimd_state); |
327 | static DEFINE_PER_CPU(bool, efi_fpsimd_state_used); | |
4328825d DM |
328 | |
329 | /* | |
330 | * EFI runtime services support functions | |
331 | * | |
332 | * The ABI for EFI runtime services allows EFI to use FPSIMD during the call. | |
333 | * This means that for EFI (and only for EFI), we have to assume that FPSIMD | |
334 | * is always used rather than being an optional accelerator. | |
335 | * | |
336 | * These functions provide the necessary support for ensuring FPSIMD | |
337 | * save/restore in the contexts from which EFI is used. | |
338 | * | |
339 | * Do not use them for any other purpose -- if tempted to do so, you are | |
340 | * either doing something wrong or you need to propose some refactoring. | |
341 | */ | |
342 | ||
343 | /* | |
344 | * __efi_fpsimd_begin(): prepare FPSIMD for making an EFI runtime services call | |
345 | */ | |
346 | void __efi_fpsimd_begin(void) | |
347 | { | |
348 | if (!system_supports_fpsimd()) | |
349 | return; | |
350 | ||
351 | WARN_ON(preemptible()); | |
352 | ||
353 | if (may_use_simd()) | |
354 | kernel_neon_begin(); | |
355 | else { | |
356 | fpsimd_save_state(this_cpu_ptr(&efi_fpsimd_state)); | |
357 | __this_cpu_write(efi_fpsimd_state_used, true); | |
358 | } | |
359 | } | |
360 | ||
361 | /* | |
362 | * __efi_fpsimd_end(): clean up FPSIMD after an EFI runtime services call | |
363 | */ | |
364 | void __efi_fpsimd_end(void) | |
365 | { | |
366 | if (!system_supports_fpsimd()) | |
367 | return; | |
368 | ||
369 | if (__this_cpu_xchg(efi_fpsimd_state_used, false)) | |
370 | fpsimd_load_state(this_cpu_ptr(&efi_fpsimd_state)); | |
371 | else | |
372 | kernel_neon_end(); | |
373 | } | |
374 | ||
e580b8bc DM |
375 | #endif /* CONFIG_EFI */ |
376 | ||
4cfb3613 AB |
377 | #endif /* CONFIG_KERNEL_MODE_NEON */ |
378 | ||
fb1ab1ab LP |
379 | #ifdef CONFIG_CPU_PM |
380 | static int fpsimd_cpu_pm_notifier(struct notifier_block *self, | |
381 | unsigned long cmd, void *v) | |
382 | { | |
383 | switch (cmd) { | |
384 | case CPU_PM_ENTER: | |
005f78cd | 385 | if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE)) |
fb1ab1ab | 386 | fpsimd_save_state(¤t->thread.fpsimd_state); |
7c68a9cc | 387 | this_cpu_write(fpsimd_last_state, NULL); |
fb1ab1ab LP |
388 | break; |
389 | case CPU_PM_EXIT: | |
390 | if (current->mm) | |
005f78cd | 391 | set_thread_flag(TIF_FOREIGN_FPSTATE); |
fb1ab1ab LP |
392 | break; |
393 | case CPU_PM_ENTER_FAILED: | |
394 | default: | |
395 | return NOTIFY_DONE; | |
396 | } | |
397 | return NOTIFY_OK; | |
398 | } | |
399 | ||
400 | static struct notifier_block fpsimd_cpu_pm_notifier_block = { | |
401 | .notifier_call = fpsimd_cpu_pm_notifier, | |
402 | }; | |
403 | ||
a7c61a34 | 404 | static void __init fpsimd_pm_init(void) |
fb1ab1ab LP |
405 | { |
406 | cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block); | |
407 | } | |
408 | ||
409 | #else | |
410 | static inline void fpsimd_pm_init(void) { } | |
411 | #endif /* CONFIG_CPU_PM */ | |
412 | ||
32365e64 | 413 | #ifdef CONFIG_HOTPLUG_CPU |
c23a7266 | 414 | static int fpsimd_cpu_dead(unsigned int cpu) |
32365e64 | 415 | { |
c23a7266 SAS |
416 | per_cpu(fpsimd_last_state, cpu) = NULL; |
417 | return 0; | |
32365e64 JL |
418 | } |
419 | ||
32365e64 JL |
420 | static inline void fpsimd_hotplug_init(void) |
421 | { | |
c23a7266 SAS |
422 | cpuhp_setup_state_nocalls(CPUHP_ARM64_FPSIMD_DEAD, "arm64/fpsimd:dead", |
423 | NULL, fpsimd_cpu_dead); | |
32365e64 JL |
424 | } |
425 | ||
426 | #else | |
427 | static inline void fpsimd_hotplug_init(void) { } | |
428 | #endif | |
429 | ||
53631b54 CM |
430 | /* |
431 | * FP/SIMD support code initialisation. | |
432 | */ | |
433 | static int __init fpsimd_init(void) | |
434 | { | |
fe80f9f2 SP |
435 | if (elf_hwcap & HWCAP_FP) { |
436 | fpsimd_pm_init(); | |
437 | fpsimd_hotplug_init(); | |
438 | } else { | |
53631b54 | 439 | pr_notice("Floating-point is not implemented\n"); |
53631b54 | 440 | } |
53631b54 | 441 | |
fe80f9f2 | 442 | if (!(elf_hwcap & HWCAP_ASIMD)) |
53631b54 | 443 | pr_notice("Advanced SIMD is not implemented\n"); |
fb1ab1ab | 444 | |
53631b54 CM |
445 | return 0; |
446 | } | |
ae2e972d | 447 | core_initcall(fpsimd_init); |