1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#pragma once
8
9#include <config.h>
10#include <types.h>
11#include <arch/object/vcpu.h>
12#include <object/structures.h>
13#include <model/statedata.h>
14#include <arch/machine/cpu_registers.h>
15
16#define MXCSR_INIT_VALUE               0x1f80
17#define XCOMP_BV_COMPACTED_FORMAT      (1ull << 63)
18
19/* The state format, as saved by FXSAVE and restored by FXRSTOR instructions. */
20typedef struct i387_state {
21    uint16_t cwd;               /* control word */
22    uint16_t swd;               /* status word */
23    uint16_t twd;               /* tag word */
24    uint16_t fop;               /* last instruction opcode */
25    uint32_t reserved[4];       /* instruction and data pointers */
26    uint32_t mxcsr;             /* MXCSR register state */
27    uint32_t mxcsr_mask;        /* MXCSR mask */
28    uint32_t st_space[32];      /* FPU registers */
29    uint32_t xmm_space[64];     /* XMM registers */
30    uint32_t padding[13];
31} PACKED i387_state_t;
32
33/* The state format, as saved by XSAVE and restored by XRSTOR instructions. */
34typedef struct xsave_state {
35    i387_state_t i387;
36    struct {
37        uint64_t xfeatures;
38        uint64_t xcomp_bv;      /* state-component bitmap */
39        uint64_t reserved[6];
40    } header;
41} PACKED xsave_state_t;
42
43/* Initialise the FPU. */
44bool_t Arch_initFpu(void);
45
46/* Initialise the FPU state of the given user context. */
47void Arch_initFpuContext(user_context_t *context);
48
49static inline uint32_t xsave_features_high(void)
50{
51    uint64_t features = config_ternary(CONFIG_XSAVE, CONFIG_XSAVE_FEATURE_SET, 1);
52    return (uint32_t)(features >> 32);
53}
54
55static inline uint32_t xsave_features_low(void)
56{
57    uint64_t features = config_ternary(CONFIG_XSAVE, CONFIG_XSAVE_FEATURE_SET, 1);
58    return (uint32_t)(features & 0xffffffff);
59}
60
61/* Store state in the FPU registers into memory. */
62static inline void saveFpuState(user_fpu_state_t *dest)
63{
64    if (config_set(CONFIG_FXSAVE)) {
65        asm volatile("fxsave %[dest]" : [dest] "=m"(*dest));
66    } else if (config_set(CONFIG_XSAVE_XSAVEOPT)) {
67        asm volatile("xsaveopt %[dest]" : [dest] "=m"(*dest) : "d"(xsave_features_high()), "a"(xsave_features_low()));
68    } else if (config_set(CONFIG_XSAVE_XSAVE)) {
69        asm volatile("xsave %[dest]" : [dest] "=m"(*dest) : "d"(xsave_features_high()), "a"(xsave_features_low()));
70    } else if (config_set(CONFIG_XSAVE_XSAVEC)) {
71        asm volatile("xsavec %[dest]" : [dest] "=m"(*dest) : "d"(xsave_features_high()), "a"(xsave_features_low()));
72    } else if (config_set(CONFIG_XSAVE_XSAVES)) {
73        asm volatile("xsaves %[dest]" : [dest] "=m"(*dest) : "d"(xsave_features_high()), "a"(xsave_features_low()));
74    }
75}
76
77/* Load FPU state from memory into the FPU registers. */
78static inline void loadFpuState(user_fpu_state_t *src)
79{
80    if (config_set(CONFIG_FXSAVE)) {
81        asm volatile("fxrstor %[src]" :: [src] "m"(*src));
82    } else if (config_set(CONFIG_XSAVE)) {
83        if (config_set(CONFIG_XSAVE_XSAVES)) {
84            asm volatile("xrstors %[src]" :: [src] "m"(*src), "d"(xsave_features_high()), "a"(xsave_features_low()));
85        } else {
86            asm volatile("xrstor %[src]" :: [src] "m"(*src), "d"(xsave_features_high()), "a"(xsave_features_low()));
87        }
88    }
89}
90
91/* Reset the FPU registers into their initial blank state. */
92static inline void finit(void)
93{
94    asm volatile("finit" :: "m"(control_reg_order));
95}
96
97/*
98 * Enable the FPU to be used without faulting.
99 * Required even if the kernel attempts to use the FPU.
100 */
101static inline void enableFpu(void)
102{
103    asm volatile("clts" :: "m"(control_reg_order));
104}
105
106/*
107 * Disable the FPU so that usage of it causes a fault
108 */
109static inline void disableFpu(void)
110{
111    write_cr0(read_cr0() | CR0_TASK_SWITCH);
112}
113
114#ifdef CONFIG_VTX
115static inline bool_t vcpuThreadUsingFPU(tcb_t *thread)
116{
117    return thread->tcbArch.tcbVCPU && &thread->tcbArch.tcbVCPU->fpuState == NODE_STATE(ksActiveFPUState);
118}
119#endif /* CONFIG_VTX */
120
121