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