/* * Returns 1 if success, 0 in case of QueryPerformanceCounter is not supported. * * Calibration is done as described above. * * To ensure that the calibration is accurate, we interleave sampling * the microsecond resolution counter via QueryPerformanceCounter with the 1 * millisecond resolution system clock via GetSystemTimeAsFileTime. * When we see the edge transition where the system clock ticks, we * compare the before and after microsecond counter values. If it is * within a calibration threshold (kMaxCalibrationDiff), then we * record the instantaneous system time (1 msresolution) and the * 64-bit counter value (~0.5 mks reslution). Since we have a before and * after counter value, we interpolate it to get the "average" counter * value to associate with the system time. */ static int NaClCalibrateWindowsClockQpc(struct NaClTimeState *ntsp) { FILETIME ft_start; FILETIME ft_prev; FILETIME ft_now; LARGE_INTEGER counter_before; LARGE_INTEGER counter_after; int64_t counter_diff; int64_t counter_diff_ms; uint64_t end_of_calibrate; int sys_time_changed; int calibration_success = 0; NaClLog(5, "Entered NaClCalibrateWindowsClockQpc\n"); GetSystemTimeAsFileTime(&ft_start); ft_prev = ft_start; end_of_calibrate = NaClFileTimeToMs(&ft_start) + kCalibrateTimeoutMs; do { if (!QueryPerformanceCounter(&counter_before)) return 0; GetSystemTimeAsFileTime(&ft_now); if (!QueryPerformanceCounter(&counter_after)) return 0; counter_diff = counter_after.QuadPart - counter_before.QuadPart; counter_diff_ms = (counter_diff * kMillisecondsPerSecond) / ntsp->qpc_frequency; sys_time_changed = (ft_now.dwHighDateTime != ft_prev.dwHighDateTime) || (ft_now.dwLowDateTime != ft_prev.dwLowDateTime); if ((counter_diff >= 0) && (counter_diff_ms <= kMaxCalibrationDiff) && sys_time_changed) { calibration_success = 1; break; } if (sys_time_changed) ft_prev = ft_now; } while (NaClFileTimeToMs(&ft_now) < end_of_calibrate); ntsp->system_time_start_ms = NaClFileTimeToMs(&ft_now); ntsp->qpc_start = counter_before.QuadPart + (counter_diff / 2); ntsp->last_qpc = counter_after.QuadPart; NaClLog(5, "Leaving NaClCalibrateWindowsClockQpc : %d\n", calibration_success); return calibration_success; }
/* * Calibration is done as described above. * * To ensure that the calibration is accurate, we interleave sampling * the millisecond resolution counter via timeGetTime with the 10-55 * millisecond resolution system clock via GetSystemTimeAsFileTime. * When we see the edge transition where the system clock ticks, we * compare the before and after millisecond counter values. If it is * within a calibration threshold (kMaxCalibrationDiff), then we * record the instantaneous system time (10-55 msresolution) and the * 32-bit counter value (1ms reslution). Since we have a before and * after counter value, we interpolate it to get the "average" counter * value to associate with the system time. */ static void NaClCalibrateWindowsClockMu(struct NaClTimeState *ntsp) { FILETIME ft_start; FILETIME ft_now; DWORD ms_counter_before; DWORD ms_counter_after; uint32_t ms_counter_diff; NaClLog(4, "Entered NaClCalibrateWindowsClockMu\n"); GetSystemTimeAsFileTime(&ft_start); ms_counter_before = timeGetTime(); for (;;) { GetSystemTimeAsFileTime(&ft_now); ms_counter_after = timeGetTime(); ms_counter_diff = ms_counter_after - (uint32_t) ms_counter_before; NaClLog(4, "ms_counter_diff %u\n", ms_counter_diff); if (ms_counter_diff <= kMaxCalibrationDiff && (ft_now.dwHighDateTime != ft_start.dwHighDateTime || ft_now.dwLowDateTime != ft_start.dwLowDateTime)) { break; } ms_counter_before = ms_counter_after; } ntsp->system_time_start_ms = NaClFileTimeToMs(&ft_now); /* * Average the counter values. Note unsigned computation of * ms_counter_diff, so that was mod 2**32 arithmetic, and the * addition of half the difference is numerically correct, whereas * (ms_counter_before + ms_counter_after)/2 is wrong due to * overflow. */ ntsp->ms_counter_start = (DWORD) (ms_counter_before + (ms_counter_diff / 2)); NaClLog(4, "Leaving NaClCalibrateWindowsClockMu\n"); }
int NaClGetTimeOfDayIntern(struct nacl_abi_timeval *tv, struct NaClTimeState *ntsp) { FILETIME ft_now; DWORD ms_counter_now; uint64_t t_ms; DWORD ms_counter_at_ft_now; uint32_t ms_counter_diff; uint64_t unix_time_ms; if (ntsp->can_use_qpc) return NaClGetTimeOfDayInternQpc(tv, ntsp, 1); GetSystemTimeAsFileTime(&ft_now); ms_counter_now = timeGetTime(); t_ms = NaClFileTimeToMs(&ft_now); NaClXMutexLock(&ntsp->mu); if (!ntsp->allow_low_resolution) { NaClLog(5, "ms_counter_now %"NACL_PRIu32"\n", (uint32_t) ms_counter_now); NaClLog(5, "t_ms %"NACL_PRId64"\n", t_ms); NaClLog(5, "system_time_start_ms %"NACL_PRIu64"\n", ntsp->system_time_start_ms); ms_counter_at_ft_now = (DWORD) (ntsp->ms_counter_start + (uint32_t) (t_ms - ntsp->system_time_start_ms)); NaClLog(5, "ms_counter_at_ft_now %"NACL_PRIu32"\n", (uint32_t) ms_counter_at_ft_now); ms_counter_diff = ms_counter_now - (uint32_t) ms_counter_at_ft_now; NaClLog(5, "ms_counter_diff %"NACL_PRIu32"\n", ms_counter_diff); if (ms_counter_diff <= kMaxMillsecondDriftBeforeRecalibration) { t_ms = t_ms + ms_counter_diff; } else { NaClCalibrateWindowsClockMu(ntsp); t_ms = ntsp->system_time_start_ms; } NaClLog(5, "adjusted t_ms = %"NACL_PRIu64"\n", t_ms); } unix_time_ms = t_ms - ntsp->epoch_start_ms; NaClXMutexUnlock(&ntsp->mu); NaClLog(5, "unix_time_ms = %"NACL_PRId64"\n", unix_time_ms); /* * Unix time is measured relative to a different epoch, Jan 1, 1970. * See the module initialization for epoch_start_ms. */ tv->nacl_abi_tv_sec = (nacl_abi_time_t) (unix_time_ms / 1000); tv->nacl_abi_tv_usec = (nacl_abi_suseconds_t) ((unix_time_ms % 1000) * 1000); return 0; }
void NaClTimeInternalInit(struct NaClTimeState *ntsp) { TIMECAPS tc; SYSTEMTIME st; FILETIME ft; /* * Maximize timer/Sleep resolution. */ timeGetDevCaps(&tc, sizeof tc); ntsp->wPeriodMin = tc.wPeriodMin; ntsp->time_resolution_ns = tc.wPeriodMin * NACL_NANOS_PER_MILLI; NaClLog(0, "NaClTimeInternalInit: timeBeginPeriod(%u)\n", tc.wPeriodMin); timeBeginPeriod(ntsp->wPeriodMin); /* * Compute Unix epoch start; calibrate high resolution clock. */ st.wYear = 1970; st.wMonth = 1; st.wDay = 1; st.wHour = 0; st.wMinute = 0; st.wSecond = 0; st.wMilliseconds = 0; SystemTimeToFileTime(&st, &ft); ntsp->epoch_start_ms = NaClFileTimeToMs(&ft); ntsp->last_reported_time_ms = 0; NaClLog(0, "Unix epoch start is %"NACL_PRIu64"ms in Windows epoch time\n", ntsp->epoch_start_ms); NaClMutexCtor(&ntsp->mu); /* * We don't actually grab the lock, since the module initializer * should be called before going threaded. */ NaClCalibrateWindowsClockMu(ntsp); }
int NaClGetTimeOfDayInternQpc(struct nacl_abi_timeval *tv, struct NaClTimeState *ntsp, int allow_calibration) { FILETIME ft_now; int64_t sys_now_mks; LARGE_INTEGER qpc; int64_t qpc_diff; int64_t qpc_diff_mks; int64_t qpc_now_mks; int64_t drift_mks; int64_t drift_ms; NaClLog(5, "Entered NaClGetTimeOfDayInternQpc\n"); NaClXMutexLock(&ntsp->mu); GetSystemTimeAsFileTime(&ft_now); QueryPerformanceCounter(&qpc); sys_now_mks = NaClFileTimeToMs(&ft_now) * kMicrosecondsPerMillisecond; NaClLog(5, " sys_now_mks = %"NACL_PRId64" (us)\n", sys_now_mks); qpc_diff = qpc.QuadPart - ntsp->qpc_start; NaClLog(5, " qpc_diff = %"NACL_PRId64" (counts)\n", qpc_diff); /* * Coarse qpc_now_mks to 10 microseconds resolution, * to match the other platforms and not make a side-channel * attack any easier than it needs to be. */ qpc_diff_mks = ((qpc_diff * (kMicrosecondsPerSecond / kCoarseMks)) / ntsp->qpc_frequency) * kCoarseMks; NaClLog(5, " qpc_diff_mks = %"NACL_PRId64" (us)\n", qpc_diff_mks); qpc_now_mks = (ntsp->system_time_start_ms * kMicrosecondsPerMillisecond) + qpc_diff_mks; NaClLog(5, " system_time_start_ms %"NACL_PRIu64"\n", ntsp->system_time_start_ms); NaClLog(5, " qpc_now_mks = %"NACL_PRId64" (us)\n", qpc_now_mks); if ((qpc_diff < 0) || (qpc.QuadPart < ntsp->last_qpc)) { NaClLog(5, " need recalibration\n"); if (allow_calibration) { NaClCalibrateWindowsClockQpc(ntsp); NaClXMutexUnlock(&ntsp->mu); return NaClGetTimeOfDayInternQpc(tv, ntsp, 0); } else { NaClLog(5, " ... but using coarse, system time instead.\n"); /* use GetSystemTimeAsFileTime(), not QPC */ qpc_now_mks = sys_now_mks; } } ntsp->last_qpc = qpc.QuadPart; drift_mks = sys_now_mks - qpc_now_mks; if (qpc_now_mks > sys_now_mks) drift_mks = qpc_now_mks - sys_now_mks; drift_ms = drift_mks / kMicrosecondsPerMillisecond; NaClLog(5, " drift_ms = %"NACL_PRId64"\n", drift_ms); if (allow_calibration && (drift_ms > kMaxMillsecondDriftBeforeRecalibration)) { NaClLog(5, "drift_ms recalibration\n"); NaClCalibrateWindowsClockQpc(ntsp); NaClXMutexUnlock(&ntsp->mu); return NaClGetTimeOfDayInternQpc(tv, ntsp, 0); } NaClXMutexUnlock(&ntsp->mu); /* translate to unix time base */ qpc_now_mks = qpc_now_mks - ntsp->epoch_start_ms * kMicrosecondsPerMillisecond; tv->nacl_abi_tv_sec = (nacl_abi_time_t)(qpc_now_mks / kMicrosecondsPerSecond); tv->nacl_abi_tv_usec = (nacl_abi_suseconds_t)(qpc_now_mks % kMicrosecondsPerSecond); return 0; }
void NaClTimeInternalInit(struct NaClTimeState *ntsp) { TIMECAPS tc; SYSTEMTIME st; FILETIME ft; LARGE_INTEGER qpc_freq; /* * Maximize timer/Sleep resolution. */ timeGetDevCaps(&tc, sizeof tc); if (ntsp->allow_low_resolution) { /* Set resolution to max so we don't over-promise. */ ntsp->wPeriodMin = tc.wPeriodMax; } else { ntsp->wPeriodMin = tc.wPeriodMin; timeBeginPeriod(ntsp->wPeriodMin); NaClLog(4, "NaClTimeInternalInit: timeBeginPeriod(%u)\n", ntsp->wPeriodMin); } ntsp->time_resolution_ns = ntsp->wPeriodMin * NACL_NANOS_PER_MILLI; /* * Compute Unix epoch start; calibrate high resolution clock. */ st.wYear = 1970; st.wMonth = 1; st.wDay = 1; st.wHour = 0; st.wMinute = 0; st.wSecond = 0; st.wMilliseconds = 0; SystemTimeToFileTime(&st, &ft); ntsp->epoch_start_ms = NaClFileTimeToMs(&ft); NaClLog(4, "Unix epoch start is %"NACL_PRIu64"ms in Windows epoch time\n", ntsp->epoch_start_ms); NaClMutexCtor(&ntsp->mu); /* * We don't actually grab the lock, since the module initializer * should be called before going threaded. */ ntsp->can_use_qpc = 0; if (!ntsp->allow_low_resolution) { ntsp->can_use_qpc = QueryPerformanceFrequency(&qpc_freq); /* * On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is * unreliable. Fallback to low-res clock. */ if (strstr(CPU_GetBrandString(), "AuthenticAMD") && (CPU_GetFamily() == 15)) ntsp->can_use_qpc = 0; NaClLog(4, "CPU_GetBrandString->[%s] ntsp->can_use_qpc=%d\n", CPU_GetBrandString(), ntsp->can_use_qpc); if (ntsp->can_use_qpc) { ntsp->qpc_frequency = qpc_freq.QuadPart; NaClLog(4, "qpc_frequency = %"NACL_PRId64" (counts/s)\n", ntsp->qpc_frequency); if (!NaClCalibrateWindowsClockQpc(ntsp)) ntsp->can_use_qpc = 0; } if (!ntsp->can_use_qpc) NaClCalibrateWindowsClockMu(ntsp); } }