ReactOS 0.4.16-dev-1537-g4e425b5
xstate.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Extended processor state management
5 * COPYRIGHT: Copyright 2025 Timo Kreuzer <timo.kreuzer@reactos.org>
6 */
7
8#include <ntoskrnl.h>
9#include <x86x64/Cpuid.h>
10#include <x86x64/Msr.h>
11#define NDEBUG
12#include <debug.h>
13
14// These are not officially documented
15#define XSTATE_PKRU 9
16#define XSTATE_HDC 13
17#define XSTATE_UINTR 14
18#define XSTATE_LBR 15
19#define XSTATE_MASK_PKRU (1LL << (XSTATE_PKRU))
20#define XSTATE_MASK_HDC (1LL << (XSTATE_HDC))
21#define XSTATE_MASK_UINTR (1LL << (XSTATE_UINTR))
22#define XSTATE_MASK_LBR (1LL << (XSTATE_LBR))
23
24#define XSTATE_MASK_SUPERVISOR \
25 (XSTATE_MASK_IPT | \
26 XSTATE_MASK_PASID | \
27 XSTATE_MASK_CET_U | \
28 XSTATE_MASK_CET_S | \
29 XSTATE_MASK_HDC | \
30 XSTATE_MASK_UINTR | \
31 XSTATE_MASK_LBR)
32
40CODE_SEG("INIT")
41static
42VOID
44 _Out_ PXSTATE_CONFIGURATION XStateConfig)
45{
46 ULONG64 SupportedUserMask;
47 ULONG64 SupportedSupervisorMask;
48 ULONG64 SupportedComponentMask;
49 ULONG NextUserOffset, NextSupervisorOffset, NextOffset;
50
51 RtlZeroMemory(XStateConfig, sizeof(*XStateConfig));
52
53 /* Read CPUID_EXTENDED_STATE main leaf (0x0D, 0x00) */
55 __cpuidex(ExtStateMain.AsInt32,
58
59 /* Get the supported XCR0 bits */
60 SupportedUserMask = (ULONG64)ExtStateMain.Edx << 32 |
61 (ULONG64)ExtStateMain.Eax.Uint32;
62
63 /* FIXME: Temporary workaround until we have dynamic kernel stack size */
64 SupportedUserMask &= ~XSTATE_MASK_LARGE_FEATURES;
65
66 /* Mask the allowed components */
67 SupportedUserMask &= XSTATE_MASK_ALLOWED;
68
69 /* Read CPUID_EXTENDED_STATE sub-leaf (0x0D, 0x01) */
71 __cpuidex(ExtStateSub.AsInt32,
74
75 /* Save control flags */
76 XStateConfig->OptimizedSave = ExtStateSub.Eax.Bits.XSAVEOPT;
77 XStateConfig->CompactionEnabled = ExtStateSub.Eax.Bits.XSAVEC;
78 XStateConfig->ExtendedFeatureDisable = ExtStateSub.Eax.Bits.Xfd;
79
80 /* Determine supported supervisor features */
81 SupportedSupervisorMask = 0;
82 if (ExtStateSub.Eax.Bits.XSAVES)
83 {
84 SupportedSupervisorMask = (ULONG64)ExtStateSub.Edx << 32 |
85 (ULONG64)ExtStateSub.Ecx.Uint32;
86 SupportedSupervisorMask &= XSTATE_MASK_ALLOWED & XSTATE_MASK_SUPERVISOR;
87 }
88
89 /* Calculate full mask */
90 SupportedComponentMask = SupportedUserMask | SupportedSupervisorMask;
91
92 /* Basic features (always enabled) */
93 XStateConfig->Features[XSTATE_LEGACY_FLOATING_POINT].Offset = 0;
94 XStateConfig->Features[XSTATE_LEGACY_FLOATING_POINT].Size = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters);
95 XStateConfig->AllFeatures[XSTATE_LEGACY_FLOATING_POINT] = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters);
96 XStateConfig->Features[XSTATE_LEGACY_SSE].Offset = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters);
97 XStateConfig->Features[XSTATE_LEGACY_SSE].Size = FIELD_SIZE(XSAVE_FORMAT, XmmRegisters);
98 XStateConfig->AllFeatures[XSTATE_LEGACY_SSE] = FIELD_SIZE(XSAVE_FORMAT, XmmRegisters);
99
100 /* Other components start after legacy state + header */
101 NextUserOffset = NextSupervisorOffset = sizeof(XSAVE_AREA);
102
103 /* Loop all components from 2 up */
105 {
106 ULONG64 ComponentBit = (1ULL << Component);
107
108 /* Query component features */
110 __cpuidex(ExtStateComponent.AsInt32,
112 Component);
113
114 /* Save size for all features */
115 XStateConfig->AllFeatures[Component] = ExtStateComponent.Size;
116
117 /* If the offset is 0, this component isn't valid */
118 if (ExtStateComponent.Size == 0) continue;
119
120 /* Check for components that are not OS supported */
121 if ((ComponentBit & SupportedComponentMask) == 0)
122 {
123 /* This emulates weird (broken) Windows behavior */
124 if ((ComponentBit & XSTATE_MASK_SUPERVISOR) == 0)
125 {
126 XStateConfig->Features[Component].Offset = ExtStateComponent.Offset;
127 XStateConfig->Features[Component].Size = ExtStateComponent.Size;
128 }
129
130 /* Skip the rest */
131 continue;
132 }
133
134 /* Check if compaction is enabled */
135 if (XStateConfig->CompactionEnabled)
136 {
137 /* Align the offsets, if needed */
138 if (ExtStateComponent.Ecx.Bits.Aligned)
139 {
140 XStateConfig->AlignedFeatures |= ComponentBit;
141 NextSupervisorOffset = ALIGN_UP(NextSupervisorOffset, 64);
142 if ((ComponentBit & SupportedUserMask) != 0)
143 {
144 NextUserOffset = ALIGN_UP(NextUserOffset, 64);
145 }
146 }
147
148 /* Update the supervisor offset */
149 NextSupervisorOffset += ExtStateComponent.Size;
150
151 /* For user components save and update the offset and size */
152 if ((ComponentBit & SupportedUserMask) != 0)
153 {
154 XStateConfig->Features[Component].Offset = NextUserOffset;
155 XStateConfig->Features[Component].Size = ExtStateComponent.Size;
156 NextUserOffset += ExtStateComponent.Size;
157 }
158 }
159 else
160 {
161 /* Not compacted, use the offset and size specified by the CPUID */
162 NextOffset = ExtStateComponent.Offset + ExtStateComponent.Size;
163 NextSupervisorOffset = max(NextSupervisorOffset, NextOffset);
164
165 /* For user components save and update the offset and size */
166 if ((ComponentBit & SupportedUserMask) != 0)
167 {
168 XStateConfig->Features[Component].Offset = ExtStateComponent.Offset;
169 XStateConfig->Features[Component].Size = ExtStateComponent.Size;
170 NextUserOffset = max(NextUserOffset, NextOffset);
171 }
172 }
173 }
174
175 /* Save the features to be enabled */
176 XStateConfig->EnabledFeatures = SupportedUserMask;
177 XStateConfig->EnabledVolatileFeatures =
178 SupportedUserMask & ~XSTATE_MASK_PERSISTENT;
179 XStateConfig->EnabledSupervisorFeatures = SupportedSupervisorMask;
180 XStateConfig->EnabledUserVisibleSupervisorFeatures =
181 SupportedSupervisorMask & XSTATE_MASK_USER_VISIBLE_SUPERVISOR;
182
183 /* Save the calculated sizes */
184 XStateConfig->Size = NextUserOffset;
185 XStateConfig->AllFeatureSize = NextSupervisorOffset;
186 ASSERT(XStateConfig->AllFeatureSize >= XStateConfig->Size);
187}
188
194CODE_SEG("INIT")
195static
196VOID
199{
200 PXSTATE_CONFIGURATION GlobalXState = &SharedUserData->XState;
201
202 if ((XState->EnabledFeatures != GlobalXState->EnabledFeatures) ||
203 (XState->EnabledSupervisorFeatures != GlobalXState->EnabledSupervisorFeatures) ||
204 (XState->Size != GlobalXState->Size) ||
205 (XState->AllFeatureSize != GlobalXState->AllFeatureSize))
206 {
207 /* Invalid features */
208 KeBugCheck(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED);
209 }
210
211 for (ULONG i = 0; i < MAXIMUM_XSTATE_FEATURES; i++)
212 {
213 if ((XState->Features[i].Size != GlobalXState->Features[i].Size) ||
214 (XState->Features[i].Offset != GlobalXState->Features[i].Offset) ||
215 (XState->AllFeatures[i] != GlobalXState->AllFeatures[i]))
216 {
217 /* Invalid features */
218 KeBugCheck(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED);
219 }
220 }
221}
222
228CODE_SEG("INIT")
229VOID
230NTAPI
232 _In_ ULONG ProcessorNumber)
233{
234 /* Check if XSAVE is supported */
235 if ((KeFeatureBits & KF_XSTATE) == 0)
236 {
237 /* XSAVE is not supported */
238 return;
239 }
240
241 if (ProcessorNumber == 0)
242 {
243 /* Processor 0: Retrieve the global configuration */
245
246 if (SharedUserData->XState.AllFeatureSize == 0)
247 {
248 KeFeatureBits &= ~KF_XSTATE;
249 return;
250 }
251
252 KeXStateLength = SharedUserData->XState.AllFeatureSize;
253 }
254 else
255 {
256 /* Processor 1+: validate the configuration against the global one */
259 ValidateXStateConfig(&XState);
260 }
261
262 /* Enable the user mode components in XCR0 */
263 _xsetbv(0, SharedUserData->XState.EnabledFeatures);
264
265 /* Now that we have set everything up, query CPUID again to get the required
266 size based on components enabled in XCR0 */
268 __cpuidex(ExtStateMain.AsInt32,
271
272 /* CPUID 0xD, leaf 0, EBX should return the size required by all components
273 enabled in XCR0 and thus match our calculation. But VBox doesn't handle
274 this correctly and simply returns the full size of all *supported*
275 features, independent of XCR0. We check and warn. */
276 if (ExtStateMain.Ebx > SharedUserData->XState.Size)
277 {
278 DPRINT1("Processor %lu, CPUID 0xD, leaf 0, EBX returns 0x%x, but we calculated 0x%lx\n",
279 ProcessorNumber,
280 ExtStateMain.Ebx,
281 SharedUserData->XState.Size);
282 }
283
284 /* Check if we have any supervisor components enabled */
285 if (SharedUserData->XState.EnabledSupervisorFeatures != 0)
286 {
287 /* Enable the supervisor components in IA32_XSS */
288 __writemsr(MSR_IA32_XSS, SharedUserData->XState.EnabledSupervisorFeatures);
289
290 /* Get the required size for features enabled in both XCR0 and IA32_XSS */
292 __cpuidex(ExtStateSubLeaf.AsInt32,
295
296 /* Check if all components fit into what we calculated. Same VBox issue
297 here as described above. */
298 if (ExtStateSubLeaf.Ebx.XSaveAreaSize > SharedUserData->XState.AllFeatureSize)
299 {
300 DPRINT1("Processor %lu, CPUID 0xD, leaf 1, EBX returns 0x%x, but we calculated 0x%lx\n",
301 ProcessorNumber,
302 ExtStateMain.Ebx,
303 SharedUserData->XState.Size);
304
305 /* The problem is likely the VM, but to be safe, we adjust the size */
306 SharedUserData->XState.AllFeatureSize = ExtStateSubLeaf.Ebx.XSaveAreaSize;
307 }
308 }
309}
#define MSR_IA32_XSS
#define CODE_SEG(...)
#define CPUID_EXTENDED_STATE
Definition: Cpuid.h:1918
#define CPUID_EXTENDED_STATE_SUB_LEAF
Definition: Cpuid.h:2033
#define CPUID_EXTENDED_STATE_MAIN_LEAF
Definition: Cpuid.h:1953
ULONG64 KeFeatureBits
Definition: krnlinit.c:22
#define DPRINT1
Definition: precomp.h:8
DECLSPEC_NORETURN VOID NTAPI KeBugCheck(ULONG BugCheckCode)
Definition: bug.c:1434
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
void __cdecl _xsetbv(unsigned int, unsigned __int64)
Definition: immintrin.h:348
PPC_QUAL void __writemsr(const unsigned long Value)
Definition: intrin_ppc.h:748
__INTRIN_INLINE void __cpuidex(int CPUInfo[4], int InfoType, int ECXValue)
Definition: intrin_x86.h:1662
#define ASSERT(a)
Definition: mode.c:44
unsigned __int64 ULONG64
Definition: imports.h:198
#define KF_XSTATE
Definition: ketypes.h:55
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
SIZE_T KeXStateLength
Definition: stubs.c:18
#define SharedUserData
XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES]
Definition: ketypes.h:1248
ULONG64 EnabledSupervisorFeatures
Definition: ketypes.h:1250
ULONG64 EnabledFeatures
Definition: ketypes.h:1233
ULONG AllFeatures[MAXIMUM_XSTATE_FEATURES]
Definition: ketypes.h:1253
#define max(a, b)
Definition: svc.c:63
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
#define NTAPI
Definition: typedefs.h:36
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint32_t ULONG
Definition: typedefs.h:59
#define ALIGN_UP(size, type)
Definition: umtypes.h:91
CPUID_EXTENDED_STATE_MAIN_LEAF_EAX Eax
Definition: Cpuid.h:93
struct CPUID_EXTENDED_STATE_SIZE_OFFSET_ECX::@4047 Bits
CPUID_EXTENDED_STATE_SIZE_OFFSET_ECX Ecx
Definition: Cpuid.h:126
struct CPUID_EXTENDED_STATE_SUB_LEAF_EAX::@4045 Bits
CPUID_EXTENDED_STATE_SUB_LEAF_EAX Eax
Definition: Cpuid.h:107
CPUID_EXTENDED_STATE_SUB_LEAF_ECX Ecx
Definition: Cpuid.h:112
struct CPUID_EXTENDED_STATE_SUB_LEAF_REGS::@3942::@3944 Ebx
XSAVE_AREA
Definition: ketypes.h:977
XSAVE_FORMAT
Definition: ketypes.h:966
#define MAXIMUM_XSTATE_FEATURES
#define XSTATE_LEGACY_SSE
#define XSTATE_LEGACY_FLOATING_POINT
#define XSTATE_MASK_USER_VISIBLE_SUPERVISOR
#define FIELD_SIZE(type, field)
_In_ ULONG Component
Definition: potypes.h:499
VOID NTAPI KiInitializeXStateConfiguration(_In_ ULONG ProcessorNumber)
Initializes the extended state configuration for the current processor.
Definition: xstate.c:231
#define XSTATE_MASK_SUPERVISOR
Definition: xstate.c:24
static VOID KiGetXStateConfiguration(_Out_ PXSTATE_CONFIGURATION XStateConfig)
Determines the extended state configuration for the current processor.
Definition: xstate.c:43
static VOID ValidateXStateConfig(_In_ PXSTATE_CONFIGURATION XState)
Validates the provided extended state configuration against the global one.
Definition: xstate.c:197