Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/time.c | 107 ++++++++++++++++++++++++++++++++++++++++ include/winternl.h | 1 + 2 files changed, 108 insertions(+)
diff --git a/dlls/ntdll/tests/time.c b/dlls/ntdll/tests/time.c index 51d9115b87c..3e3127ccf29 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,105 @@ 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; +}; + +#ifndef _MSVC_VER +static UINT64 __umulh(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); +} +#endif + +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 = __umulh(tsc0, hsd->QpcMultiplier) + hsd->QpcBias + usd->QpcBias; + tsc1 = __umulh(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 +370,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 +380,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,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=87102
Your paranoid android.
=== w10pro64_he (64 bit report) ===
ntdll: time.c:178: Test failed: unexpected QpcSystemTimeIncrement 80057c7cf37fda44, expected 1<<63
Hi Rémi,
On 17.03.2021 10:31, Rémi Bernon wrote:
+#ifndef _MSVC_VER +static UINT64 __umulh(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);
+} +#endif
I think you meant _MSC_VER here and it's not available for 32-bit targets anyway. You could just rename the helper to avoid the collision instead.
Jacek
On 3/18/21 4:07 PM, Jacek Caban wrote:
Hi Rémi,
On 17.03.2021 10:31, Rémi Bernon wrote:
+#ifndef _MSVC_VER +static UINT64 __umulh(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); +} +#endif
I think you meant _MSC_VER here and it's not available for 32-bit targets anyway. You could just rename the helper to avoid the collision instead.
Jacek
Indeed thanks, I was happy to use the new intrinsic when available. I guess we can very well do without it ;)