GNU Linux-libre 4.9.337-gnu1
[releases.git] / arch / arm64 / kernel / fpsimd.c
1 /*
2  * FP/SIMD context switching and fault handling
3  *
4  * Copyright (C) 2012 ARM Ltd.
5  * Author: Catalin Marinas <catalin.marinas@arm.com>
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
20 #include <linux/cpu.h>
21 #include <linux/cpu_pm.h>
22 #include <linux/kernel.h>
23 #include <linux/init.h>
24 #include <linux/sched.h>
25 #include <linux/signal.h>
26 #include <linux/hardirq.h>
27
28 #include <asm/fpsimd.h>
29 #include <asm/cpufeature.h>
30 #include <asm/cputype.h>
31
32 #define FPEXC_IOF       (1 << 0)
33 #define FPEXC_DZF       (1 << 1)
34 #define FPEXC_OFF       (1 << 2)
35 #define FPEXC_UFF       (1 << 3)
36 #define FPEXC_IXF       (1 << 4)
37 #define FPEXC_IDF       (1 << 7)
38
39 /*
40  * In order to reduce the number of times the FPSIMD state is needlessly saved
41  * and restored, we need to keep track of two things:
42  * (a) for each task, we need to remember which CPU was the last one to have
43  *     the task's FPSIMD state loaded into its FPSIMD registers;
44  * (b) for each CPU, we need to remember which task's userland FPSIMD state has
45  *     been loaded into its FPSIMD registers most recently, or whether it has
46  *     been used to perform kernel mode NEON in the meantime.
47  *
48  * For (a), we add a 'cpu' field to struct fpsimd_state, which gets updated to
49  * the id of the current CPU every time the state is loaded onto a CPU. For (b),
50  * we add the per-cpu variable 'fpsimd_last_state' (below), which contains the
51  * address of the userland FPSIMD state of the task that was loaded onto the CPU
52  * the most recently, or NULL if kernel mode NEON has been performed after that.
53  *
54  * With this in place, we no longer have to restore the next FPSIMD state right
55  * when switching between tasks. Instead, we can defer this check to userland
56  * resume, at which time we verify whether the CPU's fpsimd_last_state and the
57  * task's fpsimd_state.cpu are still mutually in sync. If this is the case, we
58  * can omit the FPSIMD restore.
59  *
60  * As an optimization, we use the thread_info flag TIF_FOREIGN_FPSTATE to
61  * indicate whether or not the userland FPSIMD state of the current task is
62  * present in the registers. The flag is set unless the FPSIMD registers of this
63  * CPU currently contain the most recent userland FPSIMD state of the current
64  * task.
65  *
66  * For a certain task, the sequence may look something like this:
67  * - the task gets scheduled in; if both the task's fpsimd_state.cpu field
68  *   contains the id of the current CPU, and the CPU's fpsimd_last_state per-cpu
69  *   variable points to the task's fpsimd_state, the TIF_FOREIGN_FPSTATE flag is
70  *   cleared, otherwise it is set;
71  *
72  * - the task returns to userland; if TIF_FOREIGN_FPSTATE is set, the task's
73  *   userland FPSIMD state is copied from memory to the registers, the task's
74  *   fpsimd_state.cpu field is set to the id of the current CPU, the current
75  *   CPU's fpsimd_last_state pointer is set to this task's fpsimd_state and the
76  *   TIF_FOREIGN_FPSTATE flag is cleared;
77  *
78  * - the task executes an ordinary syscall; upon return to userland, the
79  *   TIF_FOREIGN_FPSTATE flag will still be cleared, so no FPSIMD state is
80  *   restored;
81  *
82  * - the task executes a syscall which executes some NEON instructions; this is
83  *   preceded by a call to kernel_neon_begin(), which copies the task's FPSIMD
84  *   register contents to memory, clears the fpsimd_last_state per-cpu variable
85  *   and sets the TIF_FOREIGN_FPSTATE flag;
86  *
87  * - the task gets preempted after kernel_neon_end() is called; as we have not
88  *   returned from the 2nd syscall yet, TIF_FOREIGN_FPSTATE is still set so
89  *   whatever is in the FPSIMD registers is not saved to memory, but discarded.
90  */
91 static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state);
92
93 /*
94  * Trapped FP/ASIMD access.
95  */
96 void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs)
97 {
98         /* TODO: implement lazy context saving/restoring */
99         WARN_ON(1);
100 }
101
102 /*
103  * Raise a SIGFPE for the current process.
104  */
105 void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs)
106 {
107         siginfo_t info;
108         unsigned int si_code = 0;
109
110         if (esr & FPEXC_IOF)
111                 si_code = FPE_FLTINV;
112         else if (esr & FPEXC_DZF)
113                 si_code = FPE_FLTDIV;
114         else if (esr & FPEXC_OFF)
115                 si_code = FPE_FLTOVF;
116         else if (esr & FPEXC_UFF)
117                 si_code = FPE_FLTUND;
118         else if (esr & FPEXC_IXF)
119                 si_code = FPE_FLTRES;
120
121         memset(&info, 0, sizeof(info));
122         info.si_signo = SIGFPE;
123         info.si_code = si_code;
124         info.si_addr = (void __user *)instruction_pointer(regs);
125
126         send_sig_info(SIGFPE, &info, current);
127 }
128
129 void fpsimd_thread_switch(struct task_struct *next)
130 {
131         /*
132          * Save the current FPSIMD state to memory, but only if whatever is in
133          * the registers is in fact the most recent userland FPSIMD state of
134          * 'current'.
135          */
136         if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE))
137                 fpsimd_save_state(&current->thread.fpsimd_state);
138
139         if (next->mm) {
140                 /*
141                  * If we are switching to a task whose most recent userland
142                  * FPSIMD state is already in the registers of *this* cpu,
143                  * we can skip loading the state from memory. Otherwise, set
144                  * the TIF_FOREIGN_FPSTATE flag so the state will be loaded
145                  * upon the next return to userland.
146                  */
147                 struct fpsimd_state *st = &next->thread.fpsimd_state;
148
149                 if (__this_cpu_read(fpsimd_last_state) == st
150                     && st->cpu == smp_processor_id())
151                         clear_ti_thread_flag(task_thread_info(next),
152                                              TIF_FOREIGN_FPSTATE);
153                 else
154                         set_ti_thread_flag(task_thread_info(next),
155                                            TIF_FOREIGN_FPSTATE);
156         }
157 }
158
159 void fpsimd_flush_thread(void)
160 {
161         preempt_disable();
162         memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
163         fpsimd_flush_task_state(current);
164         set_thread_flag(TIF_FOREIGN_FPSTATE);
165         preempt_enable();
166 }
167
168 /*
169  * Save the userland FPSIMD state of 'current' to memory, but only if the state
170  * currently held in the registers does in fact belong to 'current'
171  */
172 void fpsimd_preserve_current_state(void)
173 {
174         preempt_disable();
175         if (!test_thread_flag(TIF_FOREIGN_FPSTATE))
176                 fpsimd_save_state(&current->thread.fpsimd_state);
177         preempt_enable();
178 }
179
180 /*
181  * Load the userland FPSIMD state of 'current' from memory, but only if the
182  * FPSIMD state already held in the registers is /not/ the most recent FPSIMD
183  * state of 'current'
184  */
185 void fpsimd_restore_current_state(void)
186 {
187         preempt_disable();
188         if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) {
189                 struct fpsimd_state *st = &current->thread.fpsimd_state;
190
191                 fpsimd_load_state(st);
192                 this_cpu_write(fpsimd_last_state, st);
193                 st->cpu = smp_processor_id();
194         }
195         preempt_enable();
196 }
197
198 /*
199  * Load an updated userland FPSIMD state for 'current' from memory and set the
200  * flag that indicates that the FPSIMD register contents are the most recent
201  * FPSIMD state of 'current'
202  */
203 void fpsimd_update_current_state(struct fpsimd_state *state)
204 {
205         preempt_disable();
206         fpsimd_load_state(state);
207         if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) {
208                 struct fpsimd_state *st = &current->thread.fpsimd_state;
209
210                 this_cpu_write(fpsimd_last_state, st);
211                 st->cpu = smp_processor_id();
212         }
213         preempt_enable();
214 }
215
216 /*
217  * Invalidate live CPU copies of task t's FPSIMD state
218  */
219 void fpsimd_flush_task_state(struct task_struct *t)
220 {
221         t->thread.fpsimd_state.cpu = NR_CPUS;
222 }
223
224 #ifdef CONFIG_KERNEL_MODE_NEON
225
226 static DEFINE_PER_CPU(struct fpsimd_partial_state, hardirq_fpsimdstate);
227 static DEFINE_PER_CPU(struct fpsimd_partial_state, softirq_fpsimdstate);
228
229 /*
230  * Kernel-side NEON support functions
231  */
232 void kernel_neon_begin_partial(u32 num_regs)
233 {
234         if (in_interrupt()) {
235                 struct fpsimd_partial_state *s = this_cpu_ptr(
236                         in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate);
237
238                 BUG_ON(num_regs > 32);
239                 fpsimd_save_partial_state(s, roundup(num_regs, 2));
240         } else {
241                 /*
242                  * Save the userland FPSIMD state if we have one and if we
243                  * haven't done so already. Clear fpsimd_last_state to indicate
244                  * that there is no longer userland FPSIMD state in the
245                  * registers.
246                  */
247                 preempt_disable();
248                 if (current->mm &&
249                     !test_and_set_thread_flag(TIF_FOREIGN_FPSTATE))
250                         fpsimd_save_state(&current->thread.fpsimd_state);
251                 this_cpu_write(fpsimd_last_state, NULL);
252         }
253 }
254 EXPORT_SYMBOL(kernel_neon_begin_partial);
255
256 void kernel_neon_end(void)
257 {
258         if (in_interrupt()) {
259                 struct fpsimd_partial_state *s = this_cpu_ptr(
260                         in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate);
261                 fpsimd_load_partial_state(s);
262         } else {
263                 preempt_enable();
264         }
265 }
266 EXPORT_SYMBOL(kernel_neon_end);
267
268 #endif /* CONFIG_KERNEL_MODE_NEON */
269
270 #ifdef CONFIG_CPU_PM
271 static int fpsimd_cpu_pm_notifier(struct notifier_block *self,
272                                   unsigned long cmd, void *v)
273 {
274         switch (cmd) {
275         case CPU_PM_ENTER:
276                 if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE))
277                         fpsimd_save_state(&current->thread.fpsimd_state);
278                 this_cpu_write(fpsimd_last_state, NULL);
279                 break;
280         case CPU_PM_EXIT:
281                 if (current->mm)
282                         set_thread_flag(TIF_FOREIGN_FPSTATE);
283                 break;
284         case CPU_PM_ENTER_FAILED:
285         default:
286                 return NOTIFY_DONE;
287         }
288         return NOTIFY_OK;
289 }
290
291 static struct notifier_block fpsimd_cpu_pm_notifier_block = {
292         .notifier_call = fpsimd_cpu_pm_notifier,
293 };
294
295 static void __init fpsimd_pm_init(void)
296 {
297         cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block);
298 }
299
300 #else
301 static inline void fpsimd_pm_init(void) { }
302 #endif /* CONFIG_CPU_PM */
303
304 #ifdef CONFIG_HOTPLUG_CPU
305 static int fpsimd_cpu_dead(unsigned int cpu)
306 {
307         per_cpu(fpsimd_last_state, cpu) = NULL;
308         return 0;
309 }
310
311 static inline void fpsimd_hotplug_init(void)
312 {
313         cpuhp_setup_state_nocalls(CPUHP_ARM64_FPSIMD_DEAD, "arm64/fpsimd:dead",
314                                   NULL, fpsimd_cpu_dead);
315 }
316
317 #else
318 static inline void fpsimd_hotplug_init(void) { }
319 #endif
320
321 /*
322  * FP/SIMD support code initialisation.
323  */
324 static int __init fpsimd_init(void)
325 {
326         if (elf_hwcap & HWCAP_FP) {
327                 fpsimd_pm_init();
328                 fpsimd_hotplug_init();
329         } else {
330                 pr_notice("Floating-point is not implemented\n");
331         }
332
333         if (!(elf_hwcap & HWCAP_ASIMD))
334                 pr_notice("Advanced SIMD is not implemented\n");
335
336         return 0;
337 }
338 late_initcall(fpsimd_init);