1 /*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include <model/statedata.h>
8 #include <arch/machine/fpu.h>
9 #include <arch/machine/cpu_registers.h>
10 #include <arch/object/structures.h>
11 #include <arch/machine/fpu.h>
12
13 /*
14 * Setup the FPU register state for a new thread.
15 */
Arch_initFpuContext(user_context_t * context)16 void Arch_initFpuContext(user_context_t *context)
17 {
18 context->fpuState = x86KSnullFpuState;
19 }
20
21 /*
22 * Initialise the FPU for this machine.
23 */
Arch_initFpu(void)24 BOOT_CODE bool_t Arch_initFpu(void)
25 {
26 /* Enable FPU / SSE / SSE2 / SSE3 / SSSE3 / SSE4 Extensions. */
27 write_cr4(read_cr4() | CR4_OSFXSR);
28
29 /* Enable the FPU in general. */
30 write_cr0((read_cr0() & ~CR0_EMULATION) | CR0_MONITOR_COPROC | CR0_NUMERIC_ERROR);
31 enableFpu();
32
33 /* Initialize the fpu state */
34 finit();
35
36 if (config_set(CONFIG_XSAVE)) {
37 uint64_t xsave_features;
38 uint32_t xsave_instruction;
39 uint64_t desired_features = config_ternary(CONFIG_XSAVE, CONFIG_XSAVE_FEATURE_SET, 1);
40 xsave_state_t *nullFpuState = (xsave_state_t *) &x86KSnullFpuState;
41
42 /* create NULL state for FPU to be used by XSAVE variants */
43 memzero(&x86KSnullFpuState, sizeof(x86KSnullFpuState));
44
45 /* check for XSAVE support */
46 if (!(x86_cpuid_ecx(1, 0) & BIT(26))) {
47 printf("XSAVE not supported\n");
48 return false;
49 }
50 /* enable XSAVE support */
51 write_cr4(read_cr4() | CR4_OSXSAVE);
52 /* check feature mask */
53 xsave_features = ((uint64_t)x86_cpuid_edx(0x0d, 0x0) << 32) | x86_cpuid_eax(0x0d, 0x0);
54 if ((xsave_features & desired_features) != desired_features) {
55 printf("Requested feature mask is 0x%llx, but only 0x%llx supported\n", desired_features, (long long)xsave_features);
56 return false;
57 }
58 /* enable feature mask */
59 write_xcr0(desired_features);
60 /* validate the xsave buffer size and instruction */
61 if (x86_cpuid_ebx(0x0d, 0x0) > CONFIG_XSAVE_SIZE) {
62 printf("XSAVE buffer set set to %d, but needs to be at least %d\n", CONFIG_XSAVE_SIZE, x86_cpuid_ebx(0x0d, 0x0));
63 return false;
64 }
65 if (x86_cpuid_ebx(0x0d, 0x0) < CONFIG_XSAVE_SIZE) {
66 printf("XSAVE buffer set set to %d, but only needs to be %d.\n"
67 "Warning: Memory may be wasted with larger than needed TCBs.\n",
68 CONFIG_XSAVE_SIZE, x86_cpuid_ebx(0x0d, 0x0));
69 }
70 /* check if a specialized XSAVE instruction was requested */
71 xsave_instruction = x86_cpuid_eax(0x0d, 0x1);
72 if (config_set(CONFIG_XSAVE_XSAVEOPT)) {
73 if (!(xsave_instruction & BIT(0))) {
74 printf("XSAVEOPT requested, but not supported\n");
75 return false;
76 }
77 } else if (config_set(CONFIG_XSAVE_XSAVEC)) {
78 if (!(xsave_instruction & BIT(1))) {
79 printf("XSAVEC requested, but not supported\n");
80 return false;
81 }
82 } else if (config_set(CONFIG_XSAVE_XSAVES)) {
83 if (!(xsave_instruction & BIT(3))) {
84 printf("XSAVES requested, but not supported\n");
85 return false;
86 }
87
88 /* AVX state from extended region should be in compacted format */
89 nullFpuState->header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT;
90
91 /* initialize the XSS MSR */
92 x86_wrmsr(IA32_XSS_MSR, desired_features);
93 }
94
95 /* copy i387 FPU initial state from FPU */
96 saveFpuState(&x86KSnullFpuState);
97 nullFpuState->i387.mxcsr = MXCSR_INIT_VALUE;
98 } else {
99 /* Store the null fpu state */
100 saveFpuState(&x86KSnullFpuState);
101 }
102 /* Set the FPU to lazy switch mode */
103 disableFpu();
104 return true;
105 }
106