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 <mode/machine.h>
14#include <arch/machine/fpu.h>
15#include <mode/model/statedata.h>
16#include <config.h>
17#include <util.h>
18
19/* We cache the following value to avoid reading the coprocessor when isFpuEnable()
20 * is called. enableFpu() and disableFpu(), the value is set to cache/reflect the
21 * actual HW FPU enable/disable state.
22 */
23bool_t isFPUEnabledCached[CONFIG_MAX_NUM_NODES];
24
25/*
26 * The following function checks if the subarchitecture support asynchronous exceptions
27 */
28BOOT_CODE static inline bool_t supportsAsyncExceptions(void)
29{
30    word_t fpexc;
31
32    if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
33        enableFpuInstInHyp();
34    }
35    /* Set FPEXC.EX=1 */
36    MRC(FPEXC, fpexc);
37    fpexc |= BIT(FPEXC_EX_BIT);
38    MCR(FPEXC, fpexc);
39
40    /* Read back the FPEXC register*/
41    MRC(FPEXC, fpexc);
42
43    return !!(fpexc & BIT(FPEXC_EX_BIT));
44}
45
46#ifdef CONFIG_HAVE_FPU
47/* This variable is set at boot/init time to true if the FPU supports 32 registers (d0-d31).
48 * otherwise it only supports 16 registers (d0-d15).
49 * We cache this value in the following variable to avoid reading the coprocessor
50 * on every FPU context switch, since it shouldn't change for one platform on run-time.
51 */
52bool_t isFPUD32SupportedCached;
53
54BOOT_CODE static inline bool_t isFPUD32Supported(void)
55{
56    word_t mvfr0;
57    asm volatile (".word 0xeef73a10 \n"  /* vmrs    r3, mvfr0 */
58                  "mov %0, r3       \n"
59                  : "=r" (mvfr0)
60                  :
61                  : "r3");
62    return ((mvfr0 & 0xf) == 2);
63}
64
65/* Initialise the FP/SIMD for this machine. */
66BOOT_CODE bool_t
67fpsimd_init(void)
68{
69    word_t cpacr;
70
71    MRC(CPACR, cpacr);
72    cpacr |= (CPACR_CP_ACCESS_PLX << CPACR_CP_10_SHIFT_POS |
73              CPACR_CP_ACCESS_PLX << CPACR_CP_11_SHIFT_POS);
74    MCR(CPACR, cpacr);
75
76    isb();
77
78    if (supportsAsyncExceptions()) {
79        /* In the future, when we've targets that support asynchronous FPU exceptions, we've to support them */
80        printf("Error: seL4 doesn't support FPU subarchitectures that support asynchronous exceptions\n");
81        return false;
82    }
83
84    isFPUD32SupportedCached = isFPUD32Supported();
85    /* Set the FPU to lazy switch mode */
86    disableFpu();
87
88    return true;
89}
90#endif /* CONFIG_HAVE_FPU */
91
92BOOT_CODE bool_t
93fpsimd_HWCapTest(void)
94{
95    word_t cpacr, fpsid;
96
97    /* Change permissions of CP10 and CP11 to read control/status registers */
98    MRC(CPACR, cpacr);
99    cpacr |= (CPACR_CP_ACCESS_PLX << CPACR_CP_10_SHIFT_POS |
100              CPACR_CP_ACCESS_PLX << CPACR_CP_11_SHIFT_POS);
101    MCR(CPACR, cpacr);
102
103    isb();
104
105    if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
106        enableFpuInstInHyp();
107    }
108
109    /* Check of this platform supports HW FP instructions */
110    asm volatile (".word 0xeef00a10  \n" /* vmrs    r0, fpsid */
111                  "mov %0, r0        \n"
112                  : "=r" (fpsid) :
113                  : "r0");
114    if (fpsid & BIT(FPSID_SW_BIT)) {
115        return false;
116    }
117
118    word_t fpsid_subarch;
119
120    if (supportsAsyncExceptions()) {
121        /* In the future, when we've targets that support asynchronous FPU exceptions, we've to support them */
122        if (config_set(CONFIG_HAVE_FPU)) {
123            printf("Error: seL4 doesn't support FPU subarchitectures that support asynchronous exceptions\n");
124            return false;
125        } else {
126            // if we aren't using the fpu then we have detected an fpu that we cannot use, but that is fine
127            return true;
128        }
129    }
130    /* Check for subarchitectures we support */
131    fpsid_subarch = (fpsid >> FPSID_SUBARCH_SHIFT_POS) & 0x7f;
132
133    switch (fpsid_subarch) {
134    /* We only support the following subarch values */
135    case 0x2:
136    case 0x3:
137    case 0x4:
138        break;
139    default: {
140        if (config_set(CONFIG_HAVE_FPU)) {
141            printf("Error: seL4 doesn't support this VFP subarchitecture\n");
142            return false;
143        } else {
144            // if we aren't using the fpu then we have detected an fpu that we cannot use, but that is fine
145            return true;
146        }
147    }
148
149    }
150
151    if (!config_set(CONFIG_HAVE_FPU)) {
152        printf("Info: Not using supported FPU as FPU is disabled in the build configuration\n");
153    }
154    return true;
155}
156