diff --git a/inc/cxplat.h b/inc/cxplat.h index 80188eb..dfc5eda 100644 --- a/inc/cxplat.h +++ b/inc/cxplat.h @@ -9,6 +9,23 @@ --*/ +// +// Time unit conversion. +// +#define NS_TO_US(x) ((x) / 1000) +#define US_TO_NS(x) ((x) * 1000) +#define NS100_TO_US(x) ((x) / 10) +#define US_TO_NS100(x) ((x) * 10) +#define MS_TO_NS100(x) ((x)*10000) +#define NS100_TO_MS(x) ((x)/10000) +#define US_TO_MS(x) ((x) / 1000) +#define MS_TO_US(x) ((x) * 1000) +#define US_TO_S(x) ((x) / (1000 * 1000)) +#define S_TO_US(x) ((x) * 1000 * 1000) +#define S_TO_NS(x) ((x) * 1000 * 1000 * 1000) +#define MS_TO_S(x) ((x) / 1000) +#define S_TO_MS(x) ((x) * 1000) + #ifdef CX_PLATFORM_WINKERNEL #include "cxplat_winkernel.h" #elif CX_PLATFORM_WINUSER diff --git a/inc/cxplat_posix.h b/inc/cxplat_posix.h index 33ad472..3d51fc1 100644 --- a/inc/cxplat_posix.h +++ b/inc/cxplat_posix.h @@ -295,6 +295,111 @@ CxPlatFree( #define CxPlatMoveMemory(Destination, Source, Length) memmove((Destination), (Source), (Length)) #define CxPlatSecureZeroMemory CxPlatZeroMemory // TODO - Something better? +// +// Time Measurement Interfaces +// + +#define CXPLAT_NANOSEC_PER_MS (1000000) +#define CXPLAT_NANOSEC_PER_MICROSEC (1000) +#define CXPLAT_NANOSEC_PER_SEC (1000000000) +#define CXPLAT_MICROSEC_PER_MS (1000) +#define CXPLAT_MICROSEC_PER_SEC (1000000) +#define CXPLAT_MS_PER_SECOND (1000) + +uint64_t +CxPlatGetTimerResolution( + void + ); + +void +CxPlatGetAbsoluteTime( + _In_ unsigned long DeltaMs, + _Out_ struct timespec *Time + ); + +uint64_t +CxPlatTimeUs64( + void + ); + +#define CxPlatTimeUs32() (uint32_t)CxPlatTimeUs64() +#define CxPlatTimeMs64() (CxPlatTimeUs64() / CXPLAT_MICROSEC_PER_MS) +#define CxPlatTimeMs32() (uint32_t)CxPlatTimeMs64() +#define CxPlatTimeUs64ToPlat(x) (x) + +inline +int64_t +CxPlatTimeEpochMs64( + void + ) +{ + struct timeval tv; + CxPlatZeroMemory(&tv, sizeof(tv)); + gettimeofday(&tv, NULL); + return S_TO_MS(tv.tv_sec) + US_TO_MS(tv.tv_usec); +} + +inline +uint64_t +CxPlatTimeDiff64( + _In_ uint64_t T1, + _In_ uint64_t T2 + ) +{ + // + // Assume no wrap around. + // + + return T2 - T1; +} + +inline +uint32_t +CXPLAT_NO_SANITIZE("unsigned-integer-overflow") +CxPlatTimeDiff32( + _In_ uint32_t T1, // First time measured + _In_ uint32_t T2 // Second time measured + ) +{ + if (T2 > T1) { + return T2 - T1; + } else { // Wrap around case. + return T2 + (0xFFFFFFFF - T1) + 1; + } +} + +inline +BOOLEAN +CxPlatTimeAtOrBefore64( + _In_ uint64_t T1, + _In_ uint64_t T2 + ) +{ + // + // Assume no wrap around. + // + + return T1 <= T2; +} + +inline +BOOLEAN +CXPLAT_NO_SANITIZE("unsigned-integer-overflow") +CxPlatTimeAtOrBefore32( + _In_ uint32_t T1, + _In_ uint32_t T2 + ) +{ + return (int32_t)(T1 - T2) <= 0; +} + +void +CxPlatSleep( + _In_ uint32_t DurationMs + ); + +#define CxPlatSchedulerYield() sched_yield() + // // Crypto Interfaces // diff --git a/inc/cxplat_winkernel.h b/inc/cxplat_winkernel.h index d4ff1cb..4bc3749 100644 --- a/inc/cxplat_winkernel.h +++ b/inc/cxplat_winkernel.h @@ -138,6 +138,178 @@ CxPlatLogAssert( #define CxPlatMoveMemory RtlMoveMemory #define CxPlatSecureZeroMemory RtlSecureZeroMemory +// +// Time Measurement Interfaces +// + +// +// Returns the worst-case system timer resolution (in us). +// +inline +uint64_t +CxPlatGetTimerResolution() +{ + ULONG MaximumTime, MinimumTime, CurrentTime; + ExQueryTimerResolution(&MaximumTime, &MinimumTime, &CurrentTime); + return NS100_TO_US(MaximumTime); +} + +// +// Performance counter frequency. +// +extern uint64_t CxPlatPerfFreq; + +// +// Returns the current time in platform specific time units. +// +inline +uint64_t +CxPlatTimePlat( + void + ) +{ + return (uint64_t)KeQueryPerformanceCounter(NULL).QuadPart; +} + +// +// Converts platform time to microseconds. +// +inline +uint64_t +CxPlatTimePlatToUs64( + uint64_t Count + ) +{ + // + // Multiply by a big number (1000000, to convert seconds to microseconds) + // and divide by a big number (CxPlatPerfFreq, to convert counts to secs). + // + // Avoid overflow with separate multiplication/division of the high and low + // bits. Taken from TcpConvertPerformanceCounterToMicroseconds. + // + uint64_t High = (Count >> 32) * 1000000; + uint64_t Low = (Count & 0xFFFFFFFF) * 1000000; + return + ((High / CxPlatPerfFreq) << 32) + + ((Low + ((High % CxPlatPerfFreq) << 32)) / CxPlatPerfFreq); +} + +// +// Converts microseconds to platform time. +// +inline +uint64_t +CxPlatTimeUs64ToPlat( + uint64_t TimeUs + ) +{ + uint64_t High = (TimeUs >> 32) * CxPlatPerfFreq; + uint64_t Low = (TimeUs & 0xFFFFFFFF) * CxPlatPerfFreq; + return + ((High / 1000000) << 32) + + ((Low + ((High % 1000000) << 32)) / 1000000); +} + +#define CxPlatTimeUs64() CxPlatTimePlatToUs64(CxPlatTimePlat()) +#define CxPlatTimeUs32() (uint32_t)CxPlatTimeUs64() +#define CxPlatTimeMs64() US_TO_MS(CxPlatTimeUs64()) +#define CxPlatTimeMs32() (uint32_t)CxPlatTimeMs64() + +#define UNIX_EPOCH_AS_FILE_TIME 0x19db1ded53e8000ll + +inline +int64_t +CxPlatTimeEpochMs64( + ) +{ + LARGE_INTEGER SystemTime; + KeQuerySystemTime(&SystemTime); + return NS100_TO_MS(SystemTime.QuadPart - UNIX_EPOCH_AS_FILE_TIME); +} + +// +// Returns the difference between two timestamps. +// +inline +uint64_t +CxPlatTimeDiff64( + _In_ uint64_t T1, // First time measured + _In_ uint64_t T2 // Second time measured + ) +{ + // + // Assume no wrap around. + // + return T2 - T1; +} + +// +// Returns the difference between two timestamps. +// +inline +uint32_t +CxPlatTimeDiff32( + _In_ uint32_t T1, // First time measured + _In_ uint32_t T2 // Second time measured + ) +{ + if (T2 > T1) { + return T2 - T1; + } else { // Wrap around case. + return T2 + (0xFFFFFFFF - T1) + 1; + } +} + +// +// Returns TRUE if T1 came before T2. +// +inline +BOOLEAN +CxPlatTimeAtOrBefore64( + _In_ uint64_t T1, + _In_ uint64_t T2 + ) +{ + // + // Assume no wrap around. + // + return T1 <= T2; +} + +// +// Returns TRUE if T1 came before T2. +// +inline +BOOLEAN +CxPlatTimeAtOrBefore32( + _In_ uint32_t T1, + _In_ uint32_t T2 + ) +{ + return (int32_t)(T1 - T2) <= 0; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +inline +void +CxPlatSleep( + _In_ uint32_t DurationMs + ) +{ + CXPLAT_DBG_ASSERT(DurationMs != (uint32_t)-1); + + KTIMER SleepTimer; + LARGE_INTEGER TimerValue; + + KeInitializeTimerEx(&SleepTimer, SynchronizationTimer); + TimerValue.QuadPart = Int32x32To64(DurationMs, -10000); + KeSetTimer(&SleepTimer, TimerValue, NULL); + + KeWaitForSingleObject(&SleepTimer, Executive, KernelMode, FALSE, NULL); +} + +#define CxPlatSchedulerYield() // no-op + // // Crypto Interfaces // diff --git a/inc/cxplat_winuser.h b/inc/cxplat_winuser.h index e11c686..866bf80 100644 --- a/inc/cxplat_winuser.h +++ b/inc/cxplat_winuser.h @@ -135,6 +135,176 @@ CxPlatFree( #define CxPlatMoveMemory RtlMoveMemory #define CxPlatSecureZeroMemory RtlSecureZeroMemory +// +// Time Measurement Interfaces +// + +#ifdef CXPLAT_UWP_BUILD +WINBASEAPI +_Success_(return != FALSE) +BOOL +WINAPI +GetSystemTimeAdjustment( + _Out_ PDWORD lpTimeAdjustment, + _Out_ PDWORD lpTimeIncrement, + _Out_ PBOOL lpTimeAdjustmentDisabled + ); +#endif + +// +// Returns the worst-case system timer resolution (in us). +// +inline +uint64_t +CxPlatGetTimerResolution() +{ + DWORD Adjustment, Increment; + BOOL AdjustmentDisabled; + GetSystemTimeAdjustment(&Adjustment, &Increment, &AdjustmentDisabled); + return NS100_TO_US(Increment); +} + +// +// Performance counter frequency. +// +extern uint64_t CxPlatPerfFreq; + +// +// Returns the current time in platform specific time units. +// +inline +uint64_t +CxPlatTimePlat( + void + ) +{ + uint64_t Count; + QueryPerformanceCounter((LARGE_INTEGER*)&Count); + return Count; +} + +// +// Converts platform time to microseconds. +// +inline +uint64_t +CxPlatTimePlatToUs64( + uint64_t Count + ) +{ + // + // Multiply by a big number (1000000, to convert seconds to microseconds) + // and divide by a big number (CxPlatPerfFreq, to convert counts to secs). + // + // Avoid overflow with separate multiplication/division of the high and low + // bits. Taken from TcpConvertPerformanceCounterToMicroseconds. + // + uint64_t High = (Count >> 32) * 1000000; + uint64_t Low = (Count & 0xFFFFFFFF) * 1000000; + return + ((High / CxPlatPerfFreq) << 32) + + ((Low + ((High % CxPlatPerfFreq) << 32)) / CxPlatPerfFreq); +} + +// +// Converts microseconds to platform time. +// +inline +uint64_t +CxPlatTimeUs64ToPlat( + uint64_t TimeUs + ) +{ + uint64_t High = (TimeUs >> 32) * CxPlatPerfFreq; + uint64_t Low = (TimeUs & 0xFFFFFFFF) * CxPlatPerfFreq; + return + ((High / 1000000) << 32) + + ((Low + ((High % 1000000) << 32)) / CxPlatPerfFreq); +} + +#define CxPlatTimeUs64() CxPlatTimePlatToUs64(CxPlatTimePlat()) +#define CxPlatTimeUs32() (uint32_t)CxPlatTimeUs64() +#define CxPlatTimeMs64() US_TO_MS(CxPlatTimeUs64()) +#define CxPlatTimeMs32() (uint32_t)CxPlatTimeMs64() + +#define UNIX_EPOCH_AS_FILE_TIME 0x19db1ded53e8000ll + +inline +int64_t +CxPlatTimeEpochMs64( + ) +{ + LARGE_INTEGER FileTime; + GetSystemTimeAsFileTime((FILETIME*) &FileTime); + return NS100_TO_MS(FileTime.QuadPart - UNIX_EPOCH_AS_FILE_TIME); +} + +// +// Returns the difference between two timestamps. +// +inline +uint64_t +CxPlatTimeDiff64( + _In_ uint64_t T1, // First time measured + _In_ uint64_t T2 // Second time measured + ) +{ + // + // Assume no wrap around. + // + return T2 - T1; +} + +// +// Returns the difference between two timestamps. +// +inline +uint32_t +CxPlatTimeDiff32( + _In_ uint32_t T1, // First time measured + _In_ uint32_t T2 // Second time measured + ) +{ + if (T2 > T1) { + return T2 - T1; + } else { // Wrap around case. + return T2 + (0xFFFFFFFF - T1) + 1; + } +} + +// +// Returns TRUE if T1 came before T2. +// +inline +BOOLEAN +CxPlatTimeAtOrBefore64( + _In_ uint64_t T1, + _In_ uint64_t T2 + ) +{ + // + // Assume no wrap around. + // + return T1 <= T2; +} + +// +// Returns TRUE if T1 came before T2. +// +inline +BOOLEAN +CxPlatTimeAtOrBefore32( + _In_ uint32_t T1, + _In_ uint32_t T2 + ) +{ + return (int32_t)(T1 - T2) <= 0; +} + +#define CxPlatSleep(ms) Sleep(ms) + +#define CxPlatSchedulerYield() Sleep(0) + // // Crypto Interfaces // diff --git a/src/lib/cxplat_posix.c b/src/lib/cxplat_posix.c index 03e7e33..214ecb5 100644 --- a/src/lib/cxplat_posix.c +++ b/src/lib/cxplat_posix.c @@ -1,4 +1,4 @@ -#include "cxplat_posix.h" +#include "cxplat.h" #include "cxplat_trace.h" // For FreeBSD @@ -170,6 +170,91 @@ CxPlatFree( free(Mem); } +uint64_t +CxPlatTimespecToUs( + _In_ const struct timespec *Time + ) +{ + return (Time->tv_sec * CXPLAT_MICROSEC_PER_SEC) + (Time->tv_nsec / CXPLAT_NANOSEC_PER_MICROSEC); +} + +uint64_t +CxPlatGetTimerResolution( + void + ) +{ + struct timespec Res = {0}; + int ErrorCode = clock_getres(CLOCK_MONOTONIC, &Res); + CXPLAT_DBG_ASSERT(ErrorCode == 0); + UNREFERENCED_PARAMETER(ErrorCode); + return CxPlatTimespecToUs(&Res); +} + +uint64_t +CxPlatTimeUs64( + void + ) +{ + struct timespec CurrTime = {0}; + int ErrorCode = clock_gettime(CLOCK_MONOTONIC, &CurrTime); + CXPLAT_DBG_ASSERT(ErrorCode == 0); + UNREFERENCED_PARAMETER(ErrorCode); + return CxPlatTimespecToUs(&CurrTime); +} + +void +CxPlatGetAbsoluteTime( + _In_ unsigned long DeltaMs, + _Out_ struct timespec *Time + ) +{ + int ErrorCode = 0; + + CxPlatZeroMemory(Time, sizeof(struct timespec)); + +#if defined(CX_PLATFORM_LINUX) + ErrorCode = clock_gettime(CLOCK_MONOTONIC, Time); +#elif defined(CX_PLATFORM_DARWIN) + // + // timespec_get is used on darwin, as CLOCK_MONOTONIC isn't actually + // monotonic according to our tests. + // + timespec_get(Time, TIME_UTC); +#endif // CX_PLATFORM_DARWIN + + CXPLAT_DBG_ASSERT(ErrorCode == 0); + UNREFERENCED_PARAMETER(ErrorCode); + + Time->tv_sec += (DeltaMs / CXPLAT_MS_PER_SECOND); + Time->tv_nsec += ((DeltaMs % CXPLAT_MS_PER_SECOND) * CXPLAT_NANOSEC_PER_MS); + + if (Time->tv_nsec >= CXPLAT_NANOSEC_PER_SEC) + { + Time->tv_sec += 1; + Time->tv_nsec -= CXPLAT_NANOSEC_PER_SEC; + } + + CXPLAT_DBG_ASSERT(Time->tv_sec >= 0); + CXPLAT_DBG_ASSERT(Time->tv_nsec >= 0); + CXPLAT_DBG_ASSERT(Time->tv_nsec < CXPLAT_NANOSEC_PER_SEC); +} + +void +CxPlatSleep( + _In_ uint32_t DurationMs + ) +{ + int ErrorCode = 0; + struct timespec TS = { + .tv_sec = (DurationMs / CXPLAT_MS_PER_SECOND), + .tv_nsec = (CXPLAT_NANOSEC_PER_MS * (DurationMs % CXPLAT_MS_PER_SECOND)) + }; + + ErrorCode = nanosleep(&TS, &TS); + CXPLAT_DBG_ASSERT(ErrorCode == 0); + UNREFERENCED_PARAMETER(ErrorCode); +} + CXPLAT_STATUS CxPlatRandom( _In_ uint32_t BufferLen, diff --git a/src/lib/cxplat_winkernel.c b/src/lib/cxplat_winkernel.c index 722315b..2789221 100644 --- a/src/lib/cxplat_winkernel.c +++ b/src/lib/cxplat_winkernel.c @@ -1,4 +1,4 @@ -#include "cxplat_winkernel.h" +#include "cxplat.h" #include "cxplat_trace.h" #include @@ -23,6 +23,7 @@ typedef struct CX_PLATFORM { } CX_PLATFORM; +uint64_t CxPlatPerfFreq; CX_PLATFORM CxPlatform = { NULL }; PAGEDX @@ -34,6 +35,8 @@ CxPlatInitialize( { PAGED_CODE(); + (VOID)KeQueryPerformanceCounter((LARGE_INTEGER*)&CxPlatPerfFreq); + CXPLAT_STATUS Status = BCryptOpenAlgorithmProvider( &CxPlatform.RngAlgorithm, diff --git a/src/lib/cxplat_winuser.c b/src/lib/cxplat_winuser.c index dcb0b41..aa4102d 100644 --- a/src/lib/cxplat_winuser.c +++ b/src/lib/cxplat_winuser.c @@ -1,4 +1,4 @@ -#include "cxplat_winuser.h" +#include "cxplat.h" #include "cxplat_trace.h" #include @@ -24,6 +24,7 @@ typedef struct CX_PLATFORM { } CX_PLATFORM; +uint64_t CxPlatPerfFreq; CX_PLATFORM CxPlatform = { NULL }; _IRQL_requires_max_(PASSIVE_LEVEL) @@ -34,6 +35,8 @@ CxPlatInitialize( { CXPLAT_STATUS Status; + (void)QueryPerformanceFrequency((LARGE_INTEGER*)&CxPlatPerfFreq); + CxPlatform.Heap = HeapCreate(0, 0, 0); if (CxPlatform.Heap == NULL) { Status = CXPLAT_STATUS_OUT_OF_MEMORY; diff --git a/src/lib/inline.c b/src/lib/inline.c index 6fb2ea3..e867442 100644 --- a/src/lib/inline.c +++ b/src/lib/inline.c @@ -106,3 +106,32 @@ BOOLEAN InterlockedFetchAndSetBoolean( _Inout_ _Interlocked_operand_ BOOLEAN volatile *Target ); + +int64_t +CxPlatTimeEpochMs64( + void + ); + +uint64_t +CxPlatTimeDiff64( + _In_ uint64_t T1, + _In_ uint64_t T2 + ); + +uint32_t +CxPlatTimeDiff32( + _In_ uint32_t T1, + _In_ uint32_t T2 + ); + +BOOLEAN +CxPlatTimeAtOrBefore64( + _In_ uint64_t T1, + _In_ uint64_t T2 + ); + +BOOLEAN +CxPlatTimeAtOrBefore32( + _In_ uint32_t T1, + _In_ uint32_t T2 + ); diff --git a/src/test/CxPlatTests.h b/src/test/CxPlatTests.h index 6eaf84e..2696280 100644 --- a/src/test/CxPlatTests.h +++ b/src/test/CxPlatTests.h @@ -31,6 +31,11 @@ void CxPlatTestMemoryBasic(); void CxPlatTestMemoryFailureInjection(); #endif +// +// Time Tests +// + +void CxPlatTestTimeBasic(); // // Platform Specific Functions @@ -92,4 +97,7 @@ static const GUID CXPLAT_TEST_DEVICE_INSTANCE = #define IOCTL_CXPLAT_RUN_MEMORY_BASIC \ CXPLAT_CTL_CODE(2, METHOD_BUFFERED, FILE_WRITE_DATA) -#define CXPLAT_MAX_IOCTL_FUNC_CODE 2 +#define IOCTL_CXPLAT_RUN_TIME_BASIC \ + CXPLAT_CTL_CODE(3, METHOD_BUFFERED, FILE_WRITE_DATA) + +#define CXPLAT_MAX_IOCTL_FUNC_CODE 3 diff --git a/src/test/bin/cxplat_gtest.cpp b/src/test/bin/cxplat_gtest.cpp index 5716847..7a3bc09 100644 --- a/src/test/bin/cxplat_gtest.cpp +++ b/src/test/bin/cxplat_gtest.cpp @@ -134,6 +134,15 @@ TEST(MemorySuite, FailureInjection) { } #endif +TEST(TimeSuite, Basic) { + TestLogger Logger("CxPlatTestTimeBasic"); + if (TestingKernelMode) { + ASSERT_TRUE(DriverClient.Run(IOCTL_CXPLAT_RUN_TIME_BASIC)); + } else { + CxPlatTestTimeBasic(); + } +} + int main(int argc, char** argv) { for (int i = 0; i < argc; ++i) { if (strcmp("--kernel", argv[i]) == 0) { diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index 3ec7265..d572870 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -360,6 +360,7 @@ size_t CXPLAT_IOCTL_BUFFER_SIZES[] = 0, 0, 0, + 0, }; static_assert( @@ -478,6 +479,10 @@ CxPlatTestCtlEvtIoDeviceControl( CxPlatTestCtlRun(CxPlatTestMemoryBasic()); break; + case IOCTL_CXPLAT_RUN_TIME_BASIC: + CxPlatTestCtlRun(CxPlatTestTimeBasic()); + break; + default: Status = STATUS_NOT_IMPLEMENTED; break; diff --git a/src/test/lib/CMakeLists.txt b/src/test/lib/CMakeLists.txt index 7a25066..caf4197 100644 --- a/src/test/lib/CMakeLists.txt +++ b/src/test/lib/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES CryptTest.cpp MemoryTest.cpp + TimeTest.cpp ) add_library(testlib STATIC ${SOURCES}) diff --git a/src/test/lib/TimeTest.cpp b/src/test/lib/TimeTest.cpp new file mode 100644 index 0000000..1852310 --- /dev/null +++ b/src/test/lib/TimeTest.cpp @@ -0,0 +1,51 @@ +/*++ + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +Abstract: + + Time test. + +--*/ + +#include "precomp.h" + +void CxPlatTestTimeBasic() +{ + const uint64_t SleepTimeMs = 1000; + const uint64_t FudgeMs = 500; + + // + // On Linux, returned timer resolution is 0. Bug? + // + CxPlatGetTimerResolution(); + + TEST_TRUE(CxPlatTimeUs64() > 0); + + TEST_TRUE(CxPlatTimeEpochMs64() > 1714503647); + + uint32_t T1Us32 = CxPlatTimeUs32(); + uint64_t T1Us64 = CxPlatTimeUs64(); + uint32_t T1Ms32 = CxPlatTimeMs32(); + uint64_t T1Ms64 = CxPlatTimeMs64(); + + CxPlatSleep(SleepTimeMs); + + uint32_t T2Us32 = CxPlatTimeUs32(); + uint64_t T2Us64 = CxPlatTimeUs64(); + uint32_t T2Ms32 = CxPlatTimeMs32(); + uint64_t T2Ms64 = CxPlatTimeMs64(); + + TEST_TRUE(US_TO_MS(CxPlatTimeDiff64(T1Us64, T2Us64)) > SleepTimeMs - FudgeMs); + TEST_TRUE(US_TO_MS(CxPlatTimeDiff64(T1Us64, T2Us64)) < SleepTimeMs + FudgeMs); + TEST_TRUE(US_TO_MS(CxPlatTimeDiff32(T1Us32, T2Us32)) > SleepTimeMs - FudgeMs); + TEST_TRUE(US_TO_MS(CxPlatTimeDiff32(T1Us32, T2Us32)) < SleepTimeMs + FudgeMs); + TEST_TRUE(CxPlatTimeDiff64(T1Ms64, T2Ms64) > SleepTimeMs - FudgeMs); + TEST_TRUE(CxPlatTimeDiff64(T1Ms64, T2Ms64) < SleepTimeMs + FudgeMs); + TEST_TRUE(CxPlatTimeDiff32(T1Ms32, T2Ms32) > SleepTimeMs - FudgeMs); + TEST_TRUE(CxPlatTimeDiff32(T1Ms32, T2Ms32) < SleepTimeMs + FudgeMs); + + TEST_TRUE(CxPlatTimeAtOrBefore32(T1Ms32, T2Ms32)); + TEST_TRUE(CxPlatTimeAtOrBefore64(T1Ms64, T2Ms64)); +} diff --git a/src/test/lib/testlib.kernel.vcxproj b/src/test/lib/testlib.kernel.vcxproj index 6856e5b..5c30fca 100644 --- a/src/test/lib/testlib.kernel.vcxproj +++ b/src/test/lib/testlib.kernel.vcxproj @@ -21,6 +21,7 @@ +