Changeset 53471 in vbox for trunk/src/VBox/Runtime/r3/nt
- Timestamp:
- Dec 6, 2014 3:57:52 AM (10 years ago)
- svn:sync-xref-src-repo-rev:
- 97201
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/nt/time-nt.cpp
r53467 r53471 30 30 *******************************************************************************/ 31 31 #define LOG_GROUP RTLOGGROUP_TIME 32 #include <Windows.h>32 #include "internal-r3-nt.h" 33 33 34 34 #include <iprt/time.h> 35 #include "internal/iprt.h"36 37 35 #include <iprt/asm.h> 38 36 #include <iprt/assert.h> 39 37 #include <iprt/err.h> 38 #include <iprt/ldr.h> 39 #include <iprt/uint128.h> 40 40 #include "internal/time.h" 41 41 42 /* 43 * Note! The selected time source be the exact same one as we use in kernel land! 42 43 /******************************************************************************* 44 * Global Variables * 45 *******************************************************************************/ 46 /** Whether we've tried to resolve g_pfnRtlGetSystemTimePrecise or not. */ 47 static bool g_fInitialized = false; 48 /** Pointer to RtlGetSystemTimePrecise, added in 6.2 (windows 8). */ 49 static PFNRTLGETSYSTEMTIMEPRECISE g_pfnRtlGetSystemTimePrecise = NULL; 50 51 52 /** 53 * Initializes globals. 44 54 */ 45 //#define USE_TICK_COUNT 46 //#define USE_PERFORMANCE_COUNTER 47 //# define USE_FILE_TIME 48 //#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) 49 # define USE_INTERRUPT_TIME 50 //#else 51 //# define USE_TICK_COUNT 52 //#endif 53 54 55 #ifdef USE_INTERRUPT_TIME 56 57 typedef struct _MY_KSYSTEM_TIME 58 { 59 ULONG LowPart; 60 LONG High1Time; 61 LONG High2Time; 62 } MY_KSYSTEM_TIME; 63 64 typedef struct _MY_KUSER_SHARED_DATA 65 { 66 ULONG TickCountLowDeprecated; 67 ULONG TickCountMultiplier; 68 volatile MY_KSYSTEM_TIME InterruptTime; 69 /* The rest is not relevant. */ 70 } MY_KUSER_SHARED_DATA, *PMY_KUSER_SHARED_DATA; 71 72 #endif /* USE_INTERRUPT_TIME */ 73 74 75 DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void) 76 { 77 #if defined USE_TICK_COUNT 78 /* 79 * This would work if it didn't flip over every 49 (or so) days. 80 */ 81 return (uint64_t)GetTickCount() * RT_NS_1MS_64; 82 83 #elif defined USE_PERFORMANCE_COUNTER 84 /* 85 * Slow and not derived from InterruptTime. 86 */ 87 static LARGE_INTEGER llFreq; 88 static unsigned uMult; 89 if (!llFreq.QuadPart) 90 { 91 if (!QueryPerformanceFrequency(&llFreq)) 92 return (uint64_t)GetTickCount() * RT_NS_1MS_64; 93 llFreq.QuadPart /= 1000; 94 uMult = 1000000; /* no math genius, but this seemed to help avoiding floating point. */ 55 static void rtTimeNtInitialize(void) 56 { 57 /* 58 * Make sure we don't recurse here when calling into RTLdr. 59 */ 60 if (ASMAtomicCmpXchgBool(&g_fInitialized, true, false)) 61 { 62 void *pvFunc = RTLdrGetSystemSymbol("ntdll.dll", "RtlGetSystemTimePrecise"); 63 if (pvFunc) 64 ASMAtomicWritePtr((void * volatile *)&g_pfnRtlGetSystemTimePrecise, pvFunc); 65 ASMCompilerBarrier(); 95 66 } 96 97 LARGE_INTEGER ll; 98 if (QueryPerformanceCounter(&ll)) 99 return (ll.QuadPart * uMult) / llFreq.QuadPart; 100 return (uint64_t)GetTickCount() * RT_NS_1MS_64; 101 102 #elif defined USE_FILE_TIME 103 /* 104 * This is SystemTime not InterruptTime. 105 */ 106 uint64_t u64; /* manual say larger integer, should be safe to assume it's the same. */ 107 GetSystemTimeAsFileTime((LPFILETIME)&u64); 108 return u64 * 100; 109 110 #elif defined USE_INTERRUPT_TIME 111 # if 0 /* ASSUME 0x7ffe0000 is set in stone */ 112 /* 113 * This is exactly what we want, but we have to obtain it by non-official 114 * means. 115 */ 116 static MY_KUSER_SHARED_DATA *s_pUserSharedData = NULL; 117 if (!s_pUserSharedData) 118 { 119 /** @todo find official way of getting this or some more clever 120 * detection algorithm if necessary. The com debugger class 121 * exports this too, windbg knows it too... */ 122 s_pUserSharedData = (PMY_KUSER_SHARED_DATA)(uintptr_t)0x7ffe0000; 123 } 124 # endif 125 PMY_KUSER_SHARED_DATA pUserSharedData = (PMY_KUSER_SHARED_DATA)(uintptr_t)0x7ffe0000; 126 127 /* use interrupt time */ 128 LARGE_INTEGER Time; 129 do 130 { 131 Time.HighPart = pUserSharedData->InterruptTime.High1Time; 132 Time.LowPart = pUserSharedData->InterruptTime.LowPart; 133 } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart); 134 135 return (uint64_t)Time.QuadPart * 100; 136 137 #else 138 # error "Must select a method bright guy!" 139 #endif 140 } 141 142 143 RTDECL(uint64_t) RTTimeSystemNanoTS(void) 144 { 145 return rtTimeGetSystemNanoTS(); 146 } 147 148 149 RTDECL(uint64_t) RTTimeSystemMilliTS(void) 150 { 151 return rtTimeGetSystemNanoTS() / RT_NS_1MS; 152 } 153 154 155 RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime) 156 { 157 uint64_t u64; 158 AssertCompile(sizeof(u64) == sizeof(FILETIME)); 159 GetSystemTimeAsFileTime((LPFILETIME)&u64); 160 return RTTimeSpecSetNtTime(pTime, u64); 161 } 162 163 164 RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime) 165 { 166 FILETIME FileTime; 167 SYSTEMTIME SysTime; 168 if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTime, &FileTime), &SysTime)) 169 { 170 if (SetSystemTime(&SysTime)) 171 return VINF_SUCCESS; 172 } 173 return RTErrConvertFromWin32(GetLastError()); 174 } 175 176 177 RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime) 178 { 179 uint64_t u64; 180 AssertCompile(sizeof(u64) == sizeof(FILETIME)); 181 GetSystemTimeAsFileTime((LPFILETIME)&u64); 182 uint64_t u64Local; 183 if (!FileTimeToLocalFileTime((FILETIME const *)&u64, (LPFILETIME)&u64Local)) 184 u64Local = u64; 185 return RTTimeSpecSetNtTime(pTime, u64Local); 186 } 187 188 189 RTDECL(int64_t) RTTimeLocalDeltaNano(void) 190 { 191 /* 192 * UTC = local + Tzi.Bias; 193 * The bias is given in minutes. 194 */ 195 TIME_ZONE_INFORMATION Tzi; 196 Tzi.Bias = 0; 197 if (GetTimeZoneInformation(&Tzi) != TIME_ZONE_ID_INVALID) 198 return -(int64_t)Tzi.Bias * 60 * RT_NS_1SEC_64; 199 return 0; 200 } 201 202 203 RTDECL(PRTTIME) RTTimeLocalExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec) 204 { 205 /* 206 * FileTimeToLocalFileTime does not do the right thing, so we'll have 207 * to convert to system time and SystemTimeToTzSpecificLocalTime instead. 208 */ 209 RTTIMESPEC LocalTime; 210 SYSTEMTIME SystemTimeIn; 211 FILETIME FileTime; 212 if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn)) 213 { 214 SYSTEMTIME SystemTimeOut; 215 if (SystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, 216 &SystemTimeIn, 217 &SystemTimeOut)) 67 } 68 69 70 static uint64_t rtTimeGetSystemNanoTS(void) 71 { 72 if (RT_UNLIKELY(!g_fInitialized)) 73 rtTimeNtInitialize(); 74 75 KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA; 76 77 #if 1 78 /* 79 * If there is precise time, get the precise system time and calculate the 80 * interrupt time from it. (Microsoft doesn't expose interrupt time to user 81 * application, which is very unfortunate as there are a lot place where 82 * monotonic time is applicable but developer is "forced" to use wall clock.) 83 */ 84 if (g_pfnRtlGetSystemTimePrecise) 85 { 86 for (;;) 218 87 { 219 if (SystemTimeToFileTime(&SystemTimeOut, &FileTime)) 88 uint64_t uUpdateLockBefore; 89 while ((uUpdateLockBefore = pUserSharedData->TimeUpdateLock) & 1) 90 ASMNopPause(); 91 92 uint64_t uInterruptTime = *(uint64_t volatile *)&pUserSharedData->InterruptTime; 93 uint64_t uBaselineInterruptTimeQpc = pUserSharedData->BaselineInterruptTimeQpc; 94 uint64_t uQpcInterruptTimeIncrement = pUserSharedData->QpcInterruptTimeIncrement; 95 uint8_t uQpcInterruptTimeIncrementShift = pUserSharedData->QpcInterruptTimeIncrementShift; 96 LARGE_INTEGER QpcValue; 97 RtlQueryPerformanceCounter(&QpcValue); 98 99 if (pUserSharedData->TimeUpdateLock == uUpdateLockBefore) 220 100 { 221 RTTimeSpecSetNtFileTime(&LocalTime, &FileTime); 222 pTime = RTTimeExplode(pTime, &LocalTime); 223 if (pTime) 224 pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL; 225 return pTime; 101 uint64_t uQpcValue = QpcValue.QuadPart; 102 if (uQpcValue <= uBaselineInterruptTimeQpc) 103 return uInterruptTime * 100; 104 105 /* Calc QPC delta since base line. */ 106 uQpcValue -= uBaselineInterruptTimeQpc; 107 uQpcValue--; 108 109 /* Multiply by 10 million. */ 110 uQpcValue *= UINT32_C(10000000); 111 112 /* Multiply by QPC interrupt time increment value. */ 113 RTUINT128U Tmp128; 114 RTUInt128MulU64ByU64(&Tmp128, uQpcValue, uQpcInterruptTimeIncrement); 115 116 /* Shift the upper 64 bits by the increment shift factor. */ 117 uint64_t uResult = Tmp128.s.Hi >> uQpcInterruptTimeIncrementShift; 118 119 /* Add to base interrupt time value. */ 120 uResult += uInterruptTime; 121 122 /* Convert from NT unit to nano seconds. */ 123 return uResult * 100; 226 124 } 125 126 ASMNopPause(); 227 127 } 228 128 } 229 230 /* 231 * The fallback is to use the current offset. 232 * (A better fallback would be to use the offset of the same time of the year.) 233 */ 234 LocalTime = *pTimeSpec; 235 RTTimeSpecAddNano(&LocalTime, RTTimeLocalDeltaNano()); 236 pTime = RTTimeExplode(pTime, &LocalTime); 237 if (pTime) 238 pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL; 239 return pTime; 240 } 241 129 #endif 130 131 /* 132 * Just read interrupt time. 133 */ 134 #if ARCH_BITS >= 64 135 uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->InterruptTime; /* This is what KeQueryInterruptTime does. */ 136 uRet *= 100; 137 return uRet; 138 #else 139 140 LARGE_INTEGER NtTime; 141 do 142 { 143 NtTime.HighPart = pUserSharedData->InterruptTime.High1Time; 144 NtTime.LowPart = pUserSharedData->InterruptTime.LowPart; 145 } while (pUserSharedData->InterruptTime.High2Time != NtTime.HighPart); 146 147 return (uint64_t)NtTime.QuadPart * 100; 148 #endif 149 } 150 151 152 RTDECL(uint64_t) RTTimeSystemNanoTS(void) 153 { 154 return rtTimeGetSystemNanoTS(); 155 } 156 157 158 RTDECL(uint64_t) RTTimeSystemMilliTS(void) 159 { 160 return rtTimeGetSystemNanoTS() / RT_NS_1MS; 161 } 162 163 164 RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime) 165 { 166 /* 167 * Get the precise time if possible. 168 */ 169 if (RT_UNLIKELY(!g_fInitialized)) 170 rtTimeNtInitialize(); 171 if (g_pfnRtlGetSystemTimePrecise != NULL) 172 return RTTimeSpecSetNtTime(pTime, g_pfnRtlGetSystemTimePrecise()); 173 174 /* 175 * Just read system time. 176 */ 177 KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA; 178 #ifdef RT_ARCH_AMD64 179 uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->SystemTime; /* This is what KeQuerySystemTime does. */ 180 return RTTimeSpecSetNtTime(pTime, uRet); 181 #else 182 183 LARGE_INTEGER NtTime; 184 do 185 { 186 NtTime.HighPart = pUserSharedData->SystemTime.High1Time; 187 NtTime.LowPart = pUserSharedData->SystemTime.LowPart; 188 } while (pUserSharedData->SystemTime.High2Time != NtTime.HighPart); 189 return RTTimeSpecSetNtTime(pTime, NtTime.QuadPart); 190 #endif 191 } 192 193 194 RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime) 195 { 196 return RTTimeSpecAddNano(RTTimeNow(pTime), RTTimeLocalDeltaNano()); 197 } 198 199 200 RTDECL(int64_t) RTTimeLocalDeltaNano(void) 201 { 202 /* 203 * UTC = local + TimeZoneBias; The bias is given in NT units. 204 */ 205 KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA; 206 LARGE_INTEGER Delta; 207 #if ARCH_BITS == 64 208 Delta.QuadPart = *(int64_t volatile *)&pUserSharedData->TimeZoneBias; 209 #else 210 do 211 { 212 Delta.HighPart = pUserSharedData->TimeZoneBias.High1Time; 213 Delta.LowPart = pUserSharedData->TimeZoneBias.LowPart; 214 } while (pUserSharedData->TimeZoneBias.High2Time != Delta.HighPart); 215 #endif 216 return Delta.QuadPart * -100; 217 } 218
Note:
See TracChangeset
for help on using the changeset viewer.