Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v2: Stop trying to be smart and just use the helper everywhere instead of __umulh. Rename it too.
dlls/ntdll/tests/time.c | 106 ++++++++++++++++++++++++++++++++++++++++ include/winternl.h | 1 + 2 files changed, 107 insertions(+)
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c index 51d9115b87c..f9133f276a3 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,104 @@ 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; +}; + +/* 128-bit multiply a by b and return the high 64 bits, same as __umulh */ +static UINT64 multiply_tsc(UINT64 a, UINT64 b) +{ + UINT64 ah = a >> 32, al = (UINT32)a, bh = b >> 32, bl = (UINT32)b, m; + m = (ah * bl) + (bh * al) + ((al * bl) >> 32); + return (ah * bh) + (m >> 32); +} + +static void test_RtlQueryPerformanceCounter(void) +{ + struct hypervisor_shared_data *hsd; + KSHARED_USER_DATA *usd = (void *)0x7ffe0000; + LARGE_INTEGER frequency, counter; + NTSTATUS status; + UINT64 tsc0, tsc1; + 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 ); + + hsd = NULL; + status = pNtQuerySystemInformation( SystemHypervisorSharedPageInformation, &hsd, sizeof(void *), &len ); + ok( !status, "NtQuerySystemInformation returned %x\n", status ); + ok( len == sizeof(void *), "unexpected SystemHypervisorSharedPageInformation length %u\n", len ); + ok( !!hsd, "unexpected SystemHypervisorSharedPageInformation address %p\n", hsd ); + + tsc0 = __rdtsc(); + ret = pRtlQueryPerformanceCounter( &counter ); + tsc1 = __rdtsc(); + ok( ret, "RtlQueryPerformanceCounter failed\n" ); + + tsc0 = multiply_tsc(tsc0, hsd->QpcMultiplier) + hsd->QpcBias + usd->QpcBias; + tsc1 = multiply_tsc(tsc1, hsd->QpcMultiplier) + hsd->QpcBias + usd->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 +369,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 +379,5 @@ START_TEST(time) test_NtQueryPerformanceCounter(); test_RtlQueryTimeZoneInformation(); test_user_shared_data_time(); + test_RtlQueryPerformanceCounter(); } diff --git a/include/winternl.h b/include/winternl.h index d35d509eb41..fcdedaec8aa 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1551,6 +1551,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS { SystemFileCacheInformationEx = 81, SystemDynamicTimeZoneInformation = 102, SystemLogicalProcessorInformationEx = 107, + SystemHypervisorSharedPageInformation = 197, SystemInformationClassMax } SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
Hi,
This patch introduced two new test failures:
https://test.winehq.org/data/patterns.html#ntdll:time
* One happens on the localized TestBot VMs but is random. It was also reported against v1 of this patch but not v2 because... random! It's strange that it only hits some localized VMs but not all.
ntdll: time.c:178: Test failed: unexpected QpcSystemTimeIncrement 80057c7cf37fda44, expected 1<<63
I actually created a bug report for it a while back: https://bugs.winehq.org/show_bug.cgi?id=50989
* The other is systematic and so may be easier to debug but it only happens on the real hardware WineTest machines: cw-gtx560 and cw-rx460.
time.c:204: Test failed: unexpected QpcInterruptTimeIncrementShift 105, expected 2 time.c:206: Test failed: unexpected QpcSystemTimeIncrementShift 9, expected 2
Let me know if you need me to run tests on these.
On 4/29/21 10:56 AM, Francois Gouget wrote:
Hi,
This patch introduced two new test failures:
https://test.winehq.org/data/patterns.html#ntdll:time
One happens on the localized TestBot VMs but is random. It was also reported against v1 of this patch but not v2 because... random! It's strange that it only hits some localized VMs but not all.
ntdll: time.c:178: Test failed: unexpected QpcSystemTimeIncrement 80057c7cf37fda44, expected 1<<63
I actually created a bug report for it a while back: https://bugs.winehq.org/show_bug.cgi?id=50989
The other is systematic and so may be easier to debug but it only happens on the real hardware WineTest machines: cw-gtx560 and cw-rx460.
time.c:204: Test failed: unexpected QpcInterruptTimeIncrementShift 105, expected 2 time.c:206: Test failed: unexpected QpcSystemTimeIncrementShift 9, expected 2
Let me know if you need me to run tests on these.
I think we should actually remove these tests, I'll send a patch.
I've been doing a few more tests lately on a VM and these values are actually not fixed at all. Afaics they change with time, but not very frequently, cycling between apparently random values and often getting back to 1<<63.