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 */
16void Arch_initFpuContext(user_context_t *context)
17{
18    context->fpuState = x86KSnullFpuState;
19}
20
21/*
22 * Initialise the FPU for this machine.
23 */
24BOOT_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