Reactos
at master 442 lines 17 kB view raw
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}