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