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