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. */
15void 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
30void 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. */
47exception_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. */
61void 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