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. */
20 typedef 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. */
34 typedef 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. */
44 bool_t Arch_initFpu(void);
45 
46 /* Initialise the FPU state of the given user context. */
47 void Arch_initFpuContext(user_context_t *context);
48 
xsave_features_high(void)49 static 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 
xsave_features_low(void)55 static 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. */
saveFpuState(user_fpu_state_t * dest)62 static 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. */
loadFpuState(user_fpu_state_t * src)78 static 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. */
finit(void)92 static 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  */
enableFpu(void)101 static 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  */
disableFpu(void)109 static inline void disableFpu(void)
110 {
111     write_cr0(read_cr0() | CR0_TASK_SWITCH);
112 }
113 
114 #ifdef CONFIG_VTX
vcpuThreadUsingFPU(tcb_t * thread)115 static 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