Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/time.c | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c index 51d9115b87c..d028f12e97c 100644 --- a/dlls/ntdll/tests/time.c +++ b/dlls/ntdll/tests/time.c @@ -19,8 +19,10 @@ */
#define NONAMELESSUNION +#define NONAMELESSSTRUCT #include "ntdll_test.h" #include "ddk/wdm.h" +#include "intrin.h"
#define TICKSPERSEC 10000000 #define TICKSPERMSEC 10000 @@ -35,6 +37,9 @@ static NTSTATUS (WINAPI *pRtlQueryTimeZoneInformation)( RTL_TIME_ZONE_INFORMATIO static NTSTATUS (WINAPI *pRtlQueryDynamicTimeZoneInformation)( RTL_DYNAMIC_TIME_ZONE_INFORMATION *); static BOOL (WINAPI *pRtlQueryUnbiasedInterruptTime)( ULONGLONG *time );
+static BOOL (WINAPI *pRtlQueryPerformanceCounter)(LARGE_INTEGER*); +static BOOL (WINAPI *pRtlQueryPerformanceFrequency)(LARGE_INTEGER*); + static const int MonthLengths[2][12] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, @@ -122,6 +127,108 @@ static void test_NtQueryPerformanceCounter(void) ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); }
+struct hypervisor_shared_data +{ + UINT64 unknown; + UINT64 QpcMultiplier; + UINT64 QpcBias; +}; + +static void test_RtlQueryPerformanceCounter(void) +{ + SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION si; + struct hypervisor_shared_data *hsd; + KSHARED_USER_DATA *usd = (void *)0x7ffe0000; + LARGE_INTEGER frequency, counter; + unsigned int aux; + NTSTATUS status; + UINT64 tsc0, tsc1, mul_lo, mul_hi, tsc_lo, tsc_hi; + ULONG len; + BOOL ret; + + if (!pRtlQueryPerformanceCounter || !pRtlQueryPerformanceFrequency) + { + win_skip( "RtlQueryPerformanceCounter/Frequency not available, skipping tests\n" ); + return; + } + + if (!(usd->u3.s.QpcBypassEnabled & SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED)) + { + todo_wine win_skip("QpcBypassEnabled is not set, skipping tests\n"); + return; + } + + if ((usd->u3.s.QpcBypassEnabled & SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_HV_PAGE)) + { + ok( usd->u3.s.QpcBypassEnabled == (SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED|SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_HV_PAGE|SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_RDTSCP), + "unexpected QpcBypassEnabled %x, expected 0x83\n", usd->u3.s.QpcBypassEnabled ); + ok( usd->QpcFrequency == 10000000, "unexpected QpcFrequency %I64d, expected 10000000\n", usd->QpcFrequency ); + ok( !usd->u3.s.QpcShift, "unexpected QpcShift %d, expected 0\n", usd->u3.s.QpcShift ); + ok( usd->QpcInterruptTimeIncrement == ((ULONGLONG)1 << 63), + "unexpected QpcInterruptTimeIncrement %I64x, expected 1<<63\n", usd->QpcInterruptTimeIncrement ); + ok( usd->QpcInterruptTimeIncrementShift == 1, + "unexpected QpcInterruptTimeIncrementShift %d, expected 1\n", usd->QpcInterruptTimeIncrementShift ); + ok( usd->QpcSystemTimeIncrement == ((ULONGLONG)1 << 63), + "unexpected QpcSystemTimeIncrement %I64x, expected 1<<63\n", usd->QpcSystemTimeIncrement ); + ok( usd->QpcSystemTimeIncrementShift == 1, + "unexpected QpcSystemTimeIncrementShift %d, expected 1\n", usd->QpcSystemTimeIncrementShift ); + + memset( &si, 0xcc, sizeof(si) ); + status = pNtQuerySystemInformation( SystemHypervisorSharedPageInformation, &si, sizeof(si), &len ); + ok( !status, "NtQuerySystemInformation returned %x\n", status ); + ok( len == sizeof(si), "unexpected SystemHypervisorSharedPageInformation length %u\n", len ); + trace( "SystemHypervisorSharedPageInformation: %p\n", si.HypervisorSharedUserVa ); + hsd = (struct hypervisor_shared_data *)si.HypervisorSharedUserVa; + mul_hi = (hsd->QpcMultiplier >> 32); + mul_lo = (UINT32)hsd->QpcMultiplier; + + tsc0 = __rdtscp(&aux); + ret = pRtlQueryPerformanceCounter( &counter ); + tsc1 = __rdtscp(&aux); + ok( ret, "RtlQueryPerformanceCounter failed\n" ); + + tsc_hi = (tsc0 >> 32); + tsc_lo = (UINT32)tsc0; + tsc0 = (tsc_hi * mul_hi) + (tsc_hi * mul_lo >> 32) + (mul_hi * tsc_lo >> 32); + tsc0 += usd->QpcBias + hsd->QpcBias; + + tsc_hi = (tsc1 >> 32); + tsc_lo = (UINT32)tsc1; + tsc1 = (tsc_hi * mul_hi) + (tsc_hi * mul_lo >> 32) + (mul_hi * tsc_lo >> 32); + tsc1 += usd->QpcBias + hsd->QpcBias; + + ok( tsc0 <= counter.QuadPart, "rdtscp %I64d and RtlQueryPerformanceCounter %I64d are out of order\n", tsc0, counter.QuadPart ); + ok( counter.QuadPart <= tsc1, "RtlQueryPerformanceCounter %I64d and rdtscp %I64d are out of order\n", counter.QuadPart, tsc1 ); + } + else + { + ok( usd->u3.s.QpcShift == 10, "unexpected QpcShift %d, expected 10\n", usd->u3.s.QpcShift ); + ok( usd->QpcInterruptTimeIncrementShift == 2, + "unexpected QpcInterruptTimeIncrementShift %d, expected 2\n", usd->QpcInterruptTimeIncrementShift ); + ok( usd->QpcSystemTimeIncrementShift == 2, + "unexpected QpcSystemTimeIncrementShift %d, expected 2\n", usd->QpcSystemTimeIncrementShift ); + + tsc0 = __rdtsc(); + ret = pRtlQueryPerformanceCounter( &counter ); + tsc1 = __rdtsc(); + ok( ret, "RtlQueryPerformanceCounter failed\n" ); + + tsc0 += usd->QpcBias; + tsc0 >>= usd->u3.s.QpcShift; + tsc1 += usd->QpcBias; + tsc1 >>= usd->u3.s.QpcShift; + + ok( tsc0 <= counter.QuadPart, "rdtscp %I64d and RtlQueryPerformanceCounter %I64d are out of order\n", tsc0, counter.QuadPart ); + ok( counter.QuadPart <= tsc1, "RtlQueryPerformanceCounter %I64d and rdtscp %I64d are out of order\n", counter.QuadPart, tsc1 ); + } + + ret = pRtlQueryPerformanceFrequency( &frequency ); + ok( ret, "RtlQueryPerformanceFrequency failed\n" ); + ok( frequency.QuadPart == usd->QpcFrequency, + "RtlQueryPerformanceFrequency returned %I64d, expected USD QpcFrequency %I64d\n", + frequency.QuadPart, usd->QpcFrequency ); +} + static void test_RtlQueryTimeZoneInformation(void) { RTL_DYNAMIC_TIME_ZONE_INFORMATION tzinfo, tzinfo2; @@ -266,6 +373,8 @@ START_TEST(time) pRtlQueryDynamicTimeZoneInformation = (void *)GetProcAddress(mod, "RtlQueryDynamicTimeZoneInformation"); pRtlQueryUnbiasedInterruptTime = (void *)GetProcAddress(mod, "RtlQueryUnbiasedInterruptTime"); + pRtlQueryPerformanceCounter = (void *)GetProcAddress(mod, "RtlQueryPerformanceCounter"); + pRtlQueryPerformanceFrequency = (void *)GetProcAddress(mod, "RtlQueryPerformanceFrequency");
if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime) test_pRtlTimeToTimeFields(); @@ -274,4 +383,5 @@ START_TEST(time) test_NtQueryPerformanceCounter(); test_RtlQueryTimeZoneInformation(); test_user_shared_data_time(); + test_RtlQueryPerformanceCounter(); }