Reactos
1/*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Tests for extended state
5 * COPYRIGHT: Copyright 2025 Timo Kreuzer <timo.kreuzer@reactos.org>
6 */
7
8#include "precomp.h"
9#include <windows.h>
10#include <versionhelpers.h>
11#include <x86x64/Cpuid.h>
12
13// These are not officially documented
14#define XSTATE_PKRU 9
15#define XSTATE_HDC 13
16#define XSTATE_UINTR 14
17#define XSTATE_LBR 15
18#define XSTATE_MASK_PKRU (1LL << (XSTATE_PKRU))
19#define XSTATE_MASK_HDC (1LL << (XSTATE_HDC))
20#define XSTATE_MASK_UINTR (1LL << (XSTATE_UINTR))
21#define XSTATE_MASK_LBR (1LL << (XSTATE_LBR))
22
23#define XSTATE_MASK_SUPERVISOR \
24 (XSTATE_MASK_IPT | \
25 XSTATE_MASK_PASID | \
26 XSTATE_MASK_CET_U | \
27 XSTATE_MASK_CET_S | \
28 XSTATE_MASK_HDC | \
29 XSTATE_MASK_UINTR | \
30 XSTATE_MASK_LBR)
31
32template<ULONG NtDdiVersion>
33struct TXSTATE_CONFIGURATION;
34
35template<>
36struct TXSTATE_CONFIGURATION<NTDDI_WIN7>
37{
38 ULONGLONG EnabledFeatures; //0x0
39 ULONG Size; //0x8
40 ULONG OptimizedSave:1; //0xc
41 struct _XSTATE_FEATURE Features[64]; //0x10
42};
43
44template<>
45struct TXSTATE_CONFIGURATION<NTDDI_WIN8>
46{
47 ULONGLONG EnabledFeatures; //0x0
48 ULONGLONG EnabledVolatileFeatures; //0x8
49 ULONG Size; //0x10
50 union
51 {
52 ULONG ControlFlags; //0x14
53 struct
54 {
55 ULONG OptimizedSave:1; //0x14
56 ULONG CompactionEnabled:1; //0x14
57 ULONG ExtendedFeatureDisable:1; //0x14
58 };
59 };
60 struct _XSTATE_FEATURE Features[64]; //0x18
61};
62
63template<>
64struct TXSTATE_CONFIGURATION<NTDDI_WIN10> : TXSTATE_CONFIGURATION<NTDDI_WIN8>
65{
66 ULONGLONG EnabledSupervisorFeatures; //0x218
67 ULONGLONG AlignedFeatures; //0x220
68 ULONG AllFeatureSize; //0x228
69 ULONG AllFeatures[64]; //0x22c
70};
71
72template<>
73struct TXSTATE_CONFIGURATION<NTDDI_WIN10_RS5> : TXSTATE_CONFIGURATION<NTDDI_WIN10>
74{
75 ULONGLONG EnabledUserVisibleSupervisorFeatures; //0x330
76};
77
78template<>
79struct TXSTATE_CONFIGURATION<NTDDI_WIN11> : TXSTATE_CONFIGURATION<NTDDI_WIN10_RS5>
80{
81 ULONGLONG ExtendedFeatureDisableFeatures; //0x338
82 ULONG AllNonLargeFeatureSize; //0x340
83 ULONG Spare; //0x344
84};
85
86ULONG GetXStateNtDdiVersion(void)
87{
88 if (IsReactOS())
89 {
90 return NTDDI_WIN11;
91 }
92
93 /* Get the NTDDI_VERSION from the PEB fields */
94 PPEB Peb = NtCurrentPeb();
95 ULONG WinVersion = (Peb->OSMajorVersion << 8) | Peb->OSMinorVersion;
96 switch (WinVersion)
97 {
98 case _WIN32_WINNT_WIN7:
99 return NTDDI_WIN7;
100
101 case _WIN32_WINNT_WIN8:
102 return NTDDI_WIN8;
103
104 case _WIN32_WINNT_WINBLUE:
105 return NTDDI_WIN8; // same as Win8
106
107 case _WIN32_WINNT_WIN10:
108 {
109 switch (Peb->OSBuildNumber)
110 {
111 case 10240: // 10.0.10240 / 1507 / Threshold 1
112 case 10586: // 10.0.10586 / 1511 / Threshold 2
113 case 14393: // 10.0.14393 / 1607 / Redstone 1
114 case 15063: // 10.0.15063 / 1703 / Redstone 2
115 case 16299: // 10.0.16299 / 1709 / Redstone 3
116 case 17134: // 10.0.17134 / 1803 / Redstone 4
117 return NTDDI_WIN10;
118 case 17763: // 10.0.17763 / 1809 / Redstone 5
119 case 18362: // 10.0.18362 / 1903 / 19H1 "Titanium"
120 case 18363: // 10.0.18363 / Vanadium
121 case 19041: // 10.0.19041 / 2004 / Vibranium R1
122 case 19042: // 10.0.19042 / 20H2 / Vibranium R2 aka Manganese
123 case 19043: // 10.0.19043 / 21H1 / Vibranium R3 aka Ferrum
124 case 19044: // 10.0.19044 / 21H2 / Vibranium R4 aka Cobalt
125 case 19045: // 10.0.19045 / 22H2 / Vibranium R5
126 return NTDDI_WIN10_RS5;
127
128 // Win 11
129 case 22000: // Cobalt
130 case 22621: // 22H2 Nickel R1
131 case 22631: // 23H2 Nickel R2
132 case 26100: // 24H2 Germanium
133 return NTDDI_WIN11;
134
135 default:
136 trace("Unknown Windows 10 build number: %d\n", Peb->OSBuildNumber);
137 return 0;
138 }
139 }
140
141 default:
142 trace("UnsuUnknown Windows version: 0x%lX\n", WinVersion);
143 return 0;
144 }
145
146 return 0;
147}
148
149template<ULONG NtDdiVersion>
150SIZE_T GetXStateOffset(void)
151{
152 if (IsReactOS())
153 {
154 return FIELD_OFFSET(KUSER_SHARED_DATA, XState); // ReactOS
155 }
156 if (NtDdiVersion < NTDDI_WIN8)
157 {
158 return 0x3e0; // Win 7
159 }
160 else
161 {
162 return 0x3d8; // Win 8 - Win 11
163 }
164}
165
166template<ULONG NtDdiVersion>
167TXSTATE_CONFIGURATION<NtDdiVersion>* GetOsXState(void)
168{
169 SIZE_T Offset = GetXStateOffset<NtDdiVersion>();
170 PVOID Pointer = (PVOID)((ULONG_PTR)SharedUserData + Offset);
171 return (TXSTATE_CONFIGURATION<NtDdiVersion>*)Pointer;
172}
173
174void GetExpectedXStateConfig(TXSTATE_CONFIGURATION<NTDDI_WIN11>* XStateConfig)
175{
176 ULONG64 SupportedUserMask;
177 ULONG64 SupportedSupervisorMask;
178 ULONG64 SupportedComponentMask;
179 ULONG NextUserOffset, NextSupervisorOffset, NextOffset;
180
181 RtlZeroMemory(XStateConfig, sizeof(*XStateConfig));
182
183 if (!IsProcessorFeaturePresent(PF_XSAVE_ENABLED))
184 {
185 trace("XSAVE not supported\n");
186 return;
187 }
188
189 /* Read CPUID_EXTENDED_STATE main leaf (0x0D, 0x00) */
190 CPUID_EXTENDED_STATE_MAIN_LEAF_REGS ExtStateMain;
191 __cpuidex(ExtStateMain.AsInt32,
192 CPUID_EXTENDED_STATE,
193 CPUID_EXTENDED_STATE_MAIN_LEAF);
194
195 /* Get the supported XCR0 bits */
196 SupportedUserMask = (ULONG64)ExtStateMain.Edx << 32 |
197 (ULONG64)ExtStateMain.Eax.Uint32;
198
199 /* Mask the allowed components */
200 SupportedUserMask &= XSTATE_MASK_ALLOWED;
201 XStateConfig->EnabledFeatures = SupportedUserMask;
202 XStateConfig->EnabledVolatileFeatures = SupportedUserMask & ~XSTATE_MASK_PERSISTENT;
203
204 /* Read CPUID_EXTENDED_STATE sub-leaf (0x0D, 0x01) */
205 CPUID_EXTENDED_STATE_SUB_LEAF_REGS ExtStateSub;
206 __cpuidex(ExtStateSub.AsInt32,
207 CPUID_EXTENDED_STATE,
208 CPUID_EXTENDED_STATE_SUB_LEAF);
209
210 /* Save control flags */
211 XStateConfig->OptimizedSave = ExtStateSub.Eax.Bits.XSAVEOPT;
212 XStateConfig->CompactionEnabled = ExtStateSub.Eax.Bits.XSAVEC;
213 XStateConfig->ExtendedFeatureDisable = ExtStateSub.Eax.Bits.Xfd;
214
215 /* Determine supported supervisor features */
216 SupportedSupervisorMask = 0;
217 if (ExtStateSub.Eax.Bits.XSAVES)
218 {
219 SupportedSupervisorMask = (ULONG64)ExtStateSub.Edx << 32 |
220 (ULONG64)ExtStateSub.Ecx.Uint32;
221 SupportedSupervisorMask &= XSTATE_MASK_ALLOWED & XSTATE_MASK_SUPERVISOR;
222 }
223
224 /* Save the supervisor features */
225 XStateConfig->EnabledSupervisorFeatures = SupportedSupervisorMask;
226 XStateConfig->EnabledUserVisibleSupervisorFeatures = SupportedSupervisorMask & XSTATE_MASK_USER_VISIBLE_SUPERVISOR;
227
228 /* Calculate full mask */
229 SupportedComponentMask = SupportedUserMask | SupportedSupervisorMask;
230
231 /* Basic features (always enabled) */
232 XStateConfig->Features[XSTATE_LEGACY_FLOATING_POINT].Offset = 0;
233 XStateConfig->Features[XSTATE_LEGACY_FLOATING_POINT].Size = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters);
234 XStateConfig->AllFeatures[XSTATE_LEGACY_FLOATING_POINT] = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters);
235 XStateConfig->Features[XSTATE_LEGACY_SSE].Offset = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters);
236 XStateConfig->Features[XSTATE_LEGACY_SSE].Size = RTL_FIELD_SIZE(XSAVE_FORMAT, XmmRegisters);
237 XStateConfig->AllFeatures[XSTATE_LEGACY_SSE] = RTL_FIELD_SIZE(XSAVE_FORMAT, XmmRegisters);
238
239 /* Other components start after legacy state + header */
240 NextUserOffset = NextSupervisorOffset = sizeof(XSAVE_AREA);
241
242 /* Loop all components from 2 up */
243 for (ULONG Component = 2; Component < MAXIMUM_XSTATE_FEATURES; Component++)
244 {
245 ULONG64 ComponentBit = (1ULL << Component);
246
247 /* Query component features */
248 CPUID_EXTENDED_STATE_SIZE_OFFSET_REGS ExtStateComponent;
249 __cpuidex(ExtStateComponent.AsInt32,
250 CPUID_EXTENDED_STATE,
251 Component);
252
253 /* Save size for all features */
254 XStateConfig->AllFeatures[Component] = ExtStateComponent.Size;
255
256 /* If the offset is 0, this component isn't valid */
257 if (ExtStateComponent.Size == 0) continue;
258
259 /* Check for components that are not OS supported */
260 if ((ComponentBit & SupportedComponentMask) == 0)
261 {
262 /* This emulates weird (broken) Windows behavior */
263 if ((ComponentBit & XSTATE_MASK_SUPERVISOR) == 0)
264 {
265 XStateConfig->Features[Component].Offset = ExtStateComponent.Offset;
266 XStateConfig->Features[Component].Size = ExtStateComponent.Size;
267 }
268
269 /* Skip the rest */
270 continue;
271 }
272
273 /* Check if compaction is enabled */
274 if (XStateConfig->CompactionEnabled)
275 {
276 /* Align the offsets, if needed */
277 if (ExtStateComponent.Ecx.Bits.Aligned)
278 {
279 XStateConfig->AlignedFeatures |= ComponentBit;
280 NextSupervisorOffset = ALIGN_UP(NextSupervisorOffset, 64);
281 if ((ComponentBit & SupportedUserMask) != 0)
282 {
283 NextUserOffset = ALIGN_UP(NextUserOffset, 64);
284 }
285 }
286
287 /* Update the supervisor offset */
288 NextSupervisorOffset += ExtStateComponent.Size;
289
290 /* For user components save and update the offset and size */
291 if ((ComponentBit & SupportedUserMask) != 0)
292 {
293 XStateConfig->Features[Component].Offset = NextUserOffset;
294 XStateConfig->Features[Component].Size = ExtStateComponent.Size;
295 NextUserOffset += ExtStateComponent.Size;
296 }
297 }
298 else
299 {
300 /* Not compacted, use the offset and size specified by the CPUID */
301 NextOffset = ExtStateComponent.Offset + ExtStateComponent.Size;
302 NextSupervisorOffset = max(NextSupervisorOffset, NextOffset);
303
304 /* For user components save and update the offset and size */
305 if ((ComponentBit & SupportedUserMask) != 0)
306 {
307 XStateConfig->Features[Component].Offset = ExtStateComponent.Offset;
308 XStateConfig->Features[Component].Size = ExtStateComponent.Size;
309 NextUserOffset = max(NextUserOffset, NextOffset);
310 }
311 }
312 }
313
314 XStateConfig->Size = NextUserOffset;
315 XStateConfig->AllFeatureSize = NextSupervisorOffset;
316}
317
318template<ULONG NtDdiVersion>
319void ValidateXState(
320 TXSTATE_CONFIGURATION<NtDdiVersion>* XStateConfig,
321 TXSTATE_CONFIGURATION<NTDDI_WIN11>* ExpectedConfig);
322
323template<>
324void ValidateXState<NTDDI_WIN7>(
325 TXSTATE_CONFIGURATION<NTDDI_WIN7>* XStateConfig,
326 TXSTATE_CONFIGURATION<NTDDI_WIN11>* ExpectedConfig)
327{
328 ok_eq_hex64(XStateConfig->EnabledFeatures, ExpectedConfig->EnabledFeatures);
329 ok_eq_ulong(XStateConfig->Size, ExpectedConfig->Size);
330 ok_eq_ulong(XStateConfig->OptimizedSave, ExpectedConfig->OptimizedSave);
331 for (ULONG i = 0; i < 64; i++)
332 {
333 ok(XStateConfig->Features[i].Offset == ExpectedConfig->Features[i].Offset,
334 "XStateConfig->Features[%lu].Offset = 0x%lx, expected 0x%lx\n",
335 i, XStateConfig->Features[i].Offset, ExpectedConfig->Features[i].Offset);
336 ok(XStateConfig->Features[i].Offset == ExpectedConfig->Features[i].Offset,
337 "XStateConfig->Features[%lu].Size = 0x%lx, expected 0x%lx\n",
338 i, XStateConfig->Features[i].Size, ExpectedConfig->Features[i].Size);
339 }
340};
341
342template<>
343void ValidateXState<NTDDI_WIN8>(
344 TXSTATE_CONFIGURATION<NTDDI_WIN8>* XStateConfig,
345 TXSTATE_CONFIGURATION<NTDDI_WIN11>* ExpectedConfig)
346{
347 ok_eq_hex64(XStateConfig->EnabledFeatures, ExpectedConfig->EnabledFeatures);
348 ok_eq_hex64(XStateConfig->EnabledVolatileFeatures, ExpectedConfig->EnabledVolatileFeatures);
349 ok_eq_ulong(XStateConfig->Size, ExpectedConfig->Size);
350 ok_eq_ulong(XStateConfig->OptimizedSave, ExpectedConfig->OptimizedSave);
351 for (ULONG i = 0; i < 64; i++)
352 {
353 ok(XStateConfig->Features[i].Offset == ExpectedConfig->Features[i].Offset,
354 "XStateConfig->Features[%lu].Offset = 0x%lx, expected 0x%lx\n",
355 i, XStateConfig->Features[i].Offset, ExpectedConfig->Features[i].Offset);
356 ok(XStateConfig->Features[i].Size == ExpectedConfig->Features[i].Size,
357 "XStateConfig->Features[%lu].Size = 0x%lx, expected 0x%lx\n",
358 i, XStateConfig->Features[i].Size, ExpectedConfig->Features[i].Size);
359 }
360}
361
362template<>
363void ValidateXState<NTDDI_WIN10>(
364 TXSTATE_CONFIGURATION<NTDDI_WIN10>* XStateConfig,
365 TXSTATE_CONFIGURATION<NTDDI_WIN11>* ExpectedConfig)
366{
367 ValidateXState<NTDDI_WIN8>(XStateConfig, ExpectedConfig);
368 ok_eq_hex64(XStateConfig->EnabledSupervisorFeatures, ExpectedConfig->EnabledSupervisorFeatures);
369 ok_eq_hex64(XStateConfig->AlignedFeatures, ExpectedConfig->AlignedFeatures);
370 ok_eq_ulong(XStateConfig->AllFeatureSize, ExpectedConfig->AllFeatureSize);
371 for (ULONG i = 0; i < 64; i++)
372 {
373 ok(XStateConfig->AllFeatures[i] == ExpectedConfig->AllFeatures[i],
374 "XStateConfig->AllFeatures[%lu] = 0x%lx, expected 0x%lx\n",
375 i, XStateConfig->AllFeatures[i], ExpectedConfig->AllFeatures[i]);
376 }
377}
378
379template<>
380void ValidateXState<NTDDI_WIN10_RS5>(
381 TXSTATE_CONFIGURATION<NTDDI_WIN10_RS5>* XStateConfig,
382 TXSTATE_CONFIGURATION<NTDDI_WIN11>* ExpectedConfig)
383{
384 ValidateXState<NTDDI_WIN10>(XStateConfig, ExpectedConfig);
385 ok_eq_hex64(XStateConfig->EnabledUserVisibleSupervisorFeatures, ExpectedConfig->EnabledUserVisibleSupervisorFeatures);
386}
387
388template<>
389void ValidateXState<NTDDI_WIN11>(
390 TXSTATE_CONFIGURATION<NTDDI_WIN11>* XStateConfig,
391 TXSTATE_CONFIGURATION<NTDDI_WIN11>* ExpectedConfig)
392{
393 ValidateXState<NTDDI_WIN10_RS5>(XStateConfig, ExpectedConfig);
394 ok_eq_hex64(XStateConfig->ExtendedFeatureDisableFeatures, ExpectedConfig->ExtendedFeatureDisableFeatures);
395 ok_eq_ulong(XStateConfig->AllNonLargeFeatureSize, ExpectedConfig->AllNonLargeFeatureSize);
396 ok_eq_ulong(XStateConfig->Spare, ExpectedConfig->Spare);
397}
398
399template<ULONG NtDdiVersion>
400void TestXStateConfig(void)
401{
402 TXSTATE_CONFIGURATION<NTDDI_WIN11> ExpectedXState;
403 TXSTATE_CONFIGURATION<NtDdiVersion>* ActualXState = GetOsXState<NtDdiVersion>();
404
405 GetExpectedXStateConfig(&ExpectedXState);
406
407 ValidateXState<NtDdiVersion>(ActualXState, &ExpectedXState);
408
409 if (IsProcessorFeaturePresent(PF_XSAVE_ENABLED))
410 {
411 ULONG64 xcr0 = _xgetbv(0);
412 ok_eq_hex64(ActualXState->EnabledFeatures, xcr0);
413 }
414}
415
416START_TEST(XStateConfig)
417{
418 ULONG NtDdiVersion = GetXStateNtDdiVersion();
419
420 switch (NtDdiVersion)
421 {
422 case NTDDI_WIN7:
423 TestXStateConfig<NTDDI_WIN7>();
424 break;
425 case NTDDI_WIN8:
426 TestXStateConfig<NTDDI_WIN8>();
427 break;
428 case NTDDI_WIN10:
429 TestXStateConfig<NTDDI_WIN10>();
430 break;
431 case NTDDI_WIN10_RS5:
432 TestXStateConfig<NTDDI_WIN10_RS5>();
433 break;
434 case NTDDI_WIN11:
435 TestXStateConfig<NTDDI_WIN11>();
436 break;
437
438 default:
439 skip("Skipping XStateConfig test on usupported Windows version\n");
440 break;
441 }
442}