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