/* * PROJECT: ReactOS API Tests * LICENSE: MIT (https://spdx.org/licenses/MIT) * PURPOSE: Tests for extended state * COPYRIGHT: Copyright 2025 Timo Kreuzer */ #include "precomp.h" #include #include #include // These are not officially documented #define XSTATE_PKRU 9 #define XSTATE_HDC 13 #define XSTATE_UINTR 14 #define XSTATE_LBR 15 #define XSTATE_MASK_PKRU (1LL << (XSTATE_PKRU)) #define XSTATE_MASK_HDC (1LL << (XSTATE_HDC)) #define XSTATE_MASK_UINTR (1LL << (XSTATE_UINTR)) #define XSTATE_MASK_LBR (1LL << (XSTATE_LBR)) #define XSTATE_MASK_SUPERVISOR \ (XSTATE_MASK_IPT | \ XSTATE_MASK_PASID | \ XSTATE_MASK_CET_U | \ XSTATE_MASK_CET_S | \ XSTATE_MASK_HDC | \ XSTATE_MASK_UINTR | \ XSTATE_MASK_LBR) template struct TXSTATE_CONFIGURATION; template<> struct TXSTATE_CONFIGURATION { ULONGLONG EnabledFeatures; //0x0 ULONG Size; //0x8 ULONG OptimizedSave:1; //0xc struct _XSTATE_FEATURE Features[64]; //0x10 }; template<> struct TXSTATE_CONFIGURATION { ULONGLONG EnabledFeatures; //0x0 ULONGLONG EnabledVolatileFeatures; //0x8 ULONG Size; //0x10 union { ULONG ControlFlags; //0x14 struct { ULONG OptimizedSave:1; //0x14 ULONG CompactionEnabled:1; //0x14 ULONG ExtendedFeatureDisable:1; //0x14 }; }; struct _XSTATE_FEATURE Features[64]; //0x18 }; template<> struct TXSTATE_CONFIGURATION : TXSTATE_CONFIGURATION { ULONGLONG EnabledSupervisorFeatures; //0x218 ULONGLONG AlignedFeatures; //0x220 ULONG AllFeatureSize; //0x228 ULONG AllFeatures[64]; //0x22c }; template<> struct TXSTATE_CONFIGURATION : TXSTATE_CONFIGURATION { ULONGLONG EnabledUserVisibleSupervisorFeatures; //0x330 }; template<> struct TXSTATE_CONFIGURATION : TXSTATE_CONFIGURATION { ULONGLONG ExtendedFeatureDisableFeatures; //0x338 ULONG AllNonLargeFeatureSize; //0x340 ULONG Spare; //0x344 }; ULONG GetXStateNtDdiVersion(void) { if (IsReactOS()) { return NTDDI_WIN11; } /* Get the NTDDI_VERSION from the PEB fields */ PPEB Peb = NtCurrentPeb(); ULONG WinVersion = (Peb->OSMajorVersion << 8) | Peb->OSMinorVersion; switch (WinVersion) { case _WIN32_WINNT_WIN7: return NTDDI_WIN7; case _WIN32_WINNT_WIN8: return NTDDI_WIN8; case _WIN32_WINNT_WINBLUE: return NTDDI_WIN8; // same as Win8 case _WIN32_WINNT_WIN10: { switch (Peb->OSBuildNumber) { case 10240: // 10.0.10240 / 1507 / Threshold 1 case 10586: // 10.0.10586 / 1511 / Threshold 2 case 14393: // 10.0.14393 / 1607 / Redstone 1 case 15063: // 10.0.15063 / 1703 / Redstone 2 case 16299: // 10.0.16299 / 1709 / Redstone 3 case 17134: // 10.0.17134 / 1803 / Redstone 4 return NTDDI_WIN10; case 17763: // 10.0.17763 / 1809 / Redstone 5 case 18362: // 10.0.18362 / 1903 / 19H1 "Titanium" case 18363: // 10.0.18363 / Vanadium case 19041: // 10.0.19041 / 2004 / Vibranium R1 case 19042: // 10.0.19042 / 20H2 / Vibranium R2 aka Manganese case 19043: // 10.0.19043 / 21H1 / Vibranium R3 aka Ferrum case 19044: // 10.0.19044 / 21H2 / Vibranium R4 aka Cobalt case 19045: // 10.0.19045 / 22H2 / Vibranium R5 return NTDDI_WIN10_RS5; // Win 11 case 22000: // Cobalt case 22621: // 22H2 Nickel R1 case 22631: // 23H2 Nickel R2 case 26100: // 24H2 Germanium return NTDDI_WIN11; default: trace("Unknown Windows 10 build number: %d\n", Peb->OSBuildNumber); return 0; } } default: trace("UnsuUnknown Windows version: 0x%lX\n", WinVersion); return 0; } return 0; } template SIZE_T GetXStateOffset(void) { if (IsReactOS()) { return FIELD_OFFSET(KUSER_SHARED_DATA, XState); // ReactOS } if (NtDdiVersion < NTDDI_WIN8) { return 0x3e0; // Win 7 } else { return 0x3d8; // Win 8 - Win 11 } } template TXSTATE_CONFIGURATION* GetOsXState(void) { SIZE_T Offset = GetXStateOffset(); PVOID Pointer = (PVOID)((ULONG_PTR)SharedUserData + Offset); return (TXSTATE_CONFIGURATION*)Pointer; } void GetExpectedXStateConfig(TXSTATE_CONFIGURATION* XStateConfig) { ULONG64 SupportedUserMask; ULONG64 SupportedSupervisorMask; ULONG64 SupportedComponentMask; ULONG NextUserOffset, NextSupervisorOffset, NextOffset; RtlZeroMemory(XStateConfig, sizeof(*XStateConfig)); if (!IsProcessorFeaturePresent(PF_XSAVE_ENABLED)) { trace("XSAVE not supported\n"); return; } /* Read CPUID_EXTENDED_STATE main leaf (0x0D, 0x00) */ CPUID_EXTENDED_STATE_MAIN_LEAF_REGS ExtStateMain; __cpuidex(ExtStateMain.AsInt32, CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_MAIN_LEAF); /* Get the supported XCR0 bits */ SupportedUserMask = (ULONG64)ExtStateMain.Edx << 32 | (ULONG64)ExtStateMain.Eax.Uint32; /* Mask the allowed components */ SupportedUserMask &= XSTATE_MASK_ALLOWED; XStateConfig->EnabledFeatures = SupportedUserMask; XStateConfig->EnabledVolatileFeatures = SupportedUserMask & ~XSTATE_MASK_PERSISTENT; /* Read CPUID_EXTENDED_STATE sub-leaf (0x0D, 0x01) */ CPUID_EXTENDED_STATE_SUB_LEAF_REGS ExtStateSub; __cpuidex(ExtStateSub.AsInt32, CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF); /* Save control flags */ XStateConfig->OptimizedSave = ExtStateSub.Eax.Bits.XSAVEOPT; XStateConfig->CompactionEnabled = ExtStateSub.Eax.Bits.XSAVEC; XStateConfig->ExtendedFeatureDisable = ExtStateSub.Eax.Bits.Xfd; /* Determine supported supervisor features */ SupportedSupervisorMask = 0; if (ExtStateSub.Eax.Bits.XSAVES) { SupportedSupervisorMask = (ULONG64)ExtStateSub.Edx << 32 | (ULONG64)ExtStateSub.Ecx.Uint32; SupportedSupervisorMask &= XSTATE_MASK_ALLOWED & XSTATE_MASK_SUPERVISOR; } /* Save the supervisor features */ XStateConfig->EnabledSupervisorFeatures = SupportedSupervisorMask; XStateConfig->EnabledUserVisibleSupervisorFeatures = SupportedSupervisorMask & XSTATE_MASK_USER_VISIBLE_SUPERVISOR; /* Calculate full mask */ SupportedComponentMask = SupportedUserMask | SupportedSupervisorMask; /* Basic features (always enabled) */ XStateConfig->Features[XSTATE_LEGACY_FLOATING_POINT].Offset = 0; XStateConfig->Features[XSTATE_LEGACY_FLOATING_POINT].Size = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters); XStateConfig->AllFeatures[XSTATE_LEGACY_FLOATING_POINT] = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters); XStateConfig->Features[XSTATE_LEGACY_SSE].Offset = FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters); XStateConfig->Features[XSTATE_LEGACY_SSE].Size = RTL_FIELD_SIZE(XSAVE_FORMAT, XmmRegisters); XStateConfig->AllFeatures[XSTATE_LEGACY_SSE] = RTL_FIELD_SIZE(XSAVE_FORMAT, XmmRegisters); /* Other components start after legacy state + header */ NextUserOffset = NextSupervisorOffset = sizeof(XSAVE_AREA); /* Loop all components from 2 up */ for (ULONG Component = 2; Component < MAXIMUM_XSTATE_FEATURES; Component++) { ULONG64 ComponentBit = (1ULL << Component); /* Query component features */ CPUID_EXTENDED_STATE_SIZE_OFFSET_REGS ExtStateComponent; __cpuidex(ExtStateComponent.AsInt32, CPUID_EXTENDED_STATE, Component); /* Save size for all features */ XStateConfig->AllFeatures[Component] = ExtStateComponent.Size; /* If the offset is 0, this component isn't valid */ if (ExtStateComponent.Size == 0) continue; /* Check for components that are not OS supported */ if ((ComponentBit & SupportedComponentMask) == 0) { /* This emulates weird (broken) Windows behavior */ if ((ComponentBit & XSTATE_MASK_SUPERVISOR) == 0) { XStateConfig->Features[Component].Offset = ExtStateComponent.Offset; XStateConfig->Features[Component].Size = ExtStateComponent.Size; } /* Skip the rest */ continue; } /* Check if compaction is enabled */ if (XStateConfig->CompactionEnabled) { /* Align the offsets, if needed */ if (ExtStateComponent.Ecx.Bits.Aligned) { XStateConfig->AlignedFeatures |= ComponentBit; NextSupervisorOffset = ALIGN_UP(NextSupervisorOffset, 64); if ((ComponentBit & SupportedUserMask) != 0) { NextUserOffset = ALIGN_UP(NextUserOffset, 64); } } /* Update the supervisor offset */ NextSupervisorOffset += ExtStateComponent.Size; /* For user components save and update the offset and size */ if ((ComponentBit & SupportedUserMask) != 0) { XStateConfig->Features[Component].Offset = NextUserOffset; XStateConfig->Features[Component].Size = ExtStateComponent.Size; NextUserOffset += ExtStateComponent.Size; } } else { /* Not compacted, use the offset and size specified by the CPUID */ NextOffset = ExtStateComponent.Offset + ExtStateComponent.Size; NextSupervisorOffset = max(NextSupervisorOffset, NextOffset); /* For user components save and update the offset and size */ if ((ComponentBit & SupportedUserMask) != 0) { XStateConfig->Features[Component].Offset = ExtStateComponent.Offset; XStateConfig->Features[Component].Size = ExtStateComponent.Size; NextUserOffset = max(NextUserOffset, NextOffset); } } } XStateConfig->Size = NextUserOffset; XStateConfig->AllFeatureSize = NextSupervisorOffset; } template void ValidateXState( TXSTATE_CONFIGURATION* XStateConfig, TXSTATE_CONFIGURATION* ExpectedConfig); template<> void ValidateXState( TXSTATE_CONFIGURATION* XStateConfig, TXSTATE_CONFIGURATION* ExpectedConfig) { ok_eq_hex64(XStateConfig->EnabledFeatures, ExpectedConfig->EnabledFeatures); ok_eq_ulong(XStateConfig->Size, ExpectedConfig->Size); ok_eq_ulong(XStateConfig->OptimizedSave, ExpectedConfig->OptimizedSave); for (ULONG i = 0; i < 64; i++) { ok(XStateConfig->Features[i].Offset == ExpectedConfig->Features[i].Offset, "XStateConfig->Features[%lu].Offset = 0x%lx, expected 0x%lx\n", i, XStateConfig->Features[i].Offset, ExpectedConfig->Features[i].Offset); ok(XStateConfig->Features[i].Offset == ExpectedConfig->Features[i].Offset, "XStateConfig->Features[%lu].Size = 0x%lx, expected 0x%lx\n", i, XStateConfig->Features[i].Size, ExpectedConfig->Features[i].Size); } }; template<> void ValidateXState( TXSTATE_CONFIGURATION* XStateConfig, TXSTATE_CONFIGURATION* ExpectedConfig) { ok_eq_hex64(XStateConfig->EnabledFeatures, ExpectedConfig->EnabledFeatures); ok_eq_hex64(XStateConfig->EnabledVolatileFeatures, ExpectedConfig->EnabledVolatileFeatures); ok_eq_ulong(XStateConfig->Size, ExpectedConfig->Size); ok_eq_ulong(XStateConfig->OptimizedSave, ExpectedConfig->OptimizedSave); for (ULONG i = 0; i < 64; i++) { ok(XStateConfig->Features[i].Offset == ExpectedConfig->Features[i].Offset, "XStateConfig->Features[%lu].Offset = 0x%lx, expected 0x%lx\n", i, XStateConfig->Features[i].Offset, ExpectedConfig->Features[i].Offset); ok(XStateConfig->Features[i].Size == ExpectedConfig->Features[i].Size, "XStateConfig->Features[%lu].Size = 0x%lx, expected 0x%lx\n", i, XStateConfig->Features[i].Size, ExpectedConfig->Features[i].Size); } } template<> void ValidateXState( TXSTATE_CONFIGURATION* XStateConfig, TXSTATE_CONFIGURATION* ExpectedConfig) { ValidateXState(XStateConfig, ExpectedConfig); ok_eq_hex64(XStateConfig->EnabledSupervisorFeatures, ExpectedConfig->EnabledSupervisorFeatures); ok_eq_hex64(XStateConfig->AlignedFeatures, ExpectedConfig->AlignedFeatures); ok_eq_ulong(XStateConfig->AllFeatureSize, ExpectedConfig->AllFeatureSize); for (ULONG i = 0; i < 64; i++) { ok(XStateConfig->AllFeatures[i] == ExpectedConfig->AllFeatures[i], "XStateConfig->AllFeatures[%lu] = 0x%lx, expected 0x%lx\n", i, XStateConfig->AllFeatures[i], ExpectedConfig->AllFeatures[i]); } } template<> void ValidateXState( TXSTATE_CONFIGURATION* XStateConfig, TXSTATE_CONFIGURATION* ExpectedConfig) { ValidateXState(XStateConfig, ExpectedConfig); ok_eq_hex64(XStateConfig->EnabledUserVisibleSupervisorFeatures, ExpectedConfig->EnabledUserVisibleSupervisorFeatures); } template<> void ValidateXState( TXSTATE_CONFIGURATION* XStateConfig, TXSTATE_CONFIGURATION* ExpectedConfig) { ValidateXState(XStateConfig, ExpectedConfig); ok_eq_hex64(XStateConfig->ExtendedFeatureDisableFeatures, ExpectedConfig->ExtendedFeatureDisableFeatures); ok_eq_ulong(XStateConfig->AllNonLargeFeatureSize, ExpectedConfig->AllNonLargeFeatureSize); ok_eq_ulong(XStateConfig->Spare, ExpectedConfig->Spare); } template void TestXStateConfig(void) { TXSTATE_CONFIGURATION ExpectedXState; TXSTATE_CONFIGURATION* ActualXState = GetOsXState(); GetExpectedXStateConfig(&ExpectedXState); ValidateXState(ActualXState, &ExpectedXState); if (IsProcessorFeaturePresent(PF_XSAVE_ENABLED)) { ULONG64 xcr0 = _xgetbv(0); ok_eq_hex64(ActualXState->EnabledFeatures, xcr0); } } START_TEST(XStateConfig) { ULONG NtDdiVersion = GetXStateNtDdiVersion(); switch (NtDdiVersion) { case NTDDI_WIN7: TestXStateConfig(); break; case NTDDI_WIN8: TestXStateConfig(); break; case NTDDI_WIN10: TestXStateConfig(); break; case NTDDI_WIN10_RS5: TestXStateConfig(); break; case NTDDI_WIN11: TestXStateConfig(); break; default: skip("Skipping XStateConfig test on usupported Windows version\n"); break; } }