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