1 /*
2  * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3  *
4  * SPDX-License-Identifier: GPL-2.0-only
5  */
6 
7 #include <config.h>
8 #include <machine/fpu.h>
9 #include <api/failures.h>
10 #include <model/statedata.h>
11 #include <arch/object/structures.h>
12 
13 #ifdef CONFIG_HAVE_FPU
14 /* Switch the owner of the FPU to the given thread on local core. */
switchLocalFpuOwner(user_fpu_state_t * new_owner)15 void switchLocalFpuOwner(user_fpu_state_t *new_owner)
16 {
17     enableFpu();
18     if (NODE_STATE(ksActiveFPUState)) {
19         saveFpuState(NODE_STATE(ksActiveFPUState));
20     }
21     if (new_owner) {
22         NODE_STATE(ksFPURestoresSinceSwitch) = 0;
23         loadFpuState(new_owner);
24     } else {
25         disableFpu();
26     }
27     NODE_STATE(ksActiveFPUState) = new_owner;
28 }
29 
switchFpuOwner(user_fpu_state_t * new_owner,word_t cpu)30 void switchFpuOwner(user_fpu_state_t *new_owner, word_t cpu)
31 {
32 #ifdef ENABLE_SMP_SUPPORT
33     if (cpu != getCurrentCPUIndex()) {
34         doRemoteswitchFpuOwner(new_owner, cpu);
35     } else
36 #endif /* ENABLE_SMP_SUPPORT */
37     {
38         switchLocalFpuOwner(new_owner);
39     }
40 }
41 
42 /* Handle an FPU fault.
43  *
44  * This CPU exception is thrown when userspace attempts to use the FPU while
45  * it is disabled. We need to save the current state of the FPU, and hand
46  * it over. */
handleFPUFault(void)47 exception_t handleFPUFault(void)
48 {
49     /* If we have already given the FPU to the user, we should not reach here.
50      * This should only be able to occur on CPUs without an FPU at all, which
51      * we presumably are happy to assume will not be running seL4. */
52     assert(!nativeThreadUsingFPU(NODE_STATE(ksCurThread)));
53 
54     /* Otherwise, lazily switch over the FPU. */
55     switchLocalFpuOwner(&NODE_STATE(ksCurThread)->tcbArch.tcbContext.fpuState);
56 
57     return EXCEPTION_NONE;
58 }
59 
60 /* Prepare for the deletion of the given thread. */
fpuThreadDelete(tcb_t * thread)61 void fpuThreadDelete(tcb_t *thread)
62 {
63     /* If the thread being deleted currently owns the FPU, switch away from it
64      * so that 'ksActiveFPUState' doesn't point to invalid memory. */
65     if (nativeThreadUsingFPU(thread)) {
66         switchFpuOwner(NULL, SMP_TERNARY(thread->tcbAffinity, 0));
67     }
68 }
69 #endif /* CONFIG_HAVE_FPU */
70