You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if (!QueryPerformanceFrequency(&freq)) // NtPowerInformation and NtQueryPerformanceCounter are avoided as some hypervisors downscale tsc only if we triggered a context switch from userspace
4698
-
return false;
4699
-
4700
-
// on modern Intel/AMD hardware with an invariant/constant TSC we can measure once (pin to a single core like we did before)
4701
-
// and treat that value as the system TSC rate, we do not need to iterate every logical CPU
4702
-
// if the CPU is old and doesn't have invariant TSC, they will not have a hybrid architecture either (cores with different frequencies)
4703
-
// this was verified in both AMD and Intel, for example Intel since Nehalem
4704
-
// the idea is to detect the clock speed of the fastest core, corroborate with our db if its downscaled (sign of rdtsc patch) and detect the kernel patch
4705
-
// we do not use the slowest (E-Core) even if it would be more idle and probably have less kernel noise, because someone could just trap on the fast cores
4706
-
// and downscale their TSC until it matches the slowest cores, defeating our detection
4707
-
// this could've been prevented if theres a possibility to always ask the Windows kernel for the type of core we're running under,
4708
-
// but this proved to not be reliable always, specially on AMD
4709
-
4710
-
// calculates the invariant TSC base rate (on modern CPUs), not the dynamic core frequency, similar to what CallNtPowerInformation would give you
4711
-
LARGE_INTEGER t1q, t2q;
4712
-
thread_local u32 aux = 0;
4713
-
const u64 t1 = __rdtsc();
4714
-
QueryPerformanceCounter(&t1q); // uses RDTSCP under the hood unless platformclock (a bcdedit setting) is set, which then would use HPET or ACPI PM via NtQueryPerformanceCounter
4715
-
SleepEx(50, 0); // 50ms under more than 100000 tests was enough to get stable results on modern Windows systems, even under heavy load
4716
-
QueryPerformanceCounter(&t2q);
4717
-
const u64 t2 = __rdtscp(&aux);
4718
-
4719
-
// this thread is pinned to the first CPU core due to the previous SetThreadAffinityMask call, meaning this calculation and cpu::get_cpu_base_speed() will report the same speed
4720
-
// (normally) P-cores are in lower indexes, althought we don't really care about which type of vCPU VMAware will be pinned under
4721
-
// pinning to index 0 is also good to keep the check compatible with dinosaur (single-core) systems
4722
-
const double elapsedSec = double(t2q.QuadPart - t1q.QuadPart) / double(freq.QuadPart); // the performance counter frequency is always 10MHz when running under Hyper-V
// even if it sounds unbelievable, this will NOT be affected even if in the BIOS the "by core usage" frequency scaling or SpeedStep (or equivalent) is enabled, and even under heavy loads
4727
-
debug("TIMER: Current CPU base speed -> ", tscMHz, " MHz"); // it wont also be affected if we tell our OS to use the HPET timer instead of TSC
4728
-
4729
-
if (tscMHz < 800.0 || tscMHz >= 7000) { // i9-14900KS has 6.2 GHz; 9 9950X3D has 5.7 GHz
4730
-
debug("TIMER: TSC is spoofed");
4731
-
return true;
4732
-
}
4733
-
4734
-
const auto& info = VM::cpu::analyze_cpu();
4735
-
if (info.found) {
4736
-
if (info.base_clock_mhz == 0) {
4737
-
debug("TIMER: CPU not found in the database");
4738
-
}
4739
-
else if (info.base_clock_mhz < 800.0) {
4740
-
debug("TIMER: RDTSC seems to be intercepted by an hypervisor");
4741
-
return true;
4742
-
}
4743
-
else {
4744
-
debug("TIMER: CPU's true base speed -> ", static_cast<double>(info.base_clock_mhz), " MHz");
if (tscMHz <= info.base_clock_mhz - INVARIANT_TSC_DELTA) return true;
4758
-
}
4759
-
else {
4760
-
debug("TIMER: CPU does not support invariant TSC");
4761
-
if (tscMHz <= info.base_clock_mhz - LEGACY_DELTA) return true;
4762
-
}
4763
-
}
4764
-
4765
-
constexpr double delta = 250.0;
4766
-
if (tscMHz <= info.base_clock_mhz - delta)
4767
-
return true;
4768
-
}
4769
-
}
4770
-
4771
4692
/* TSC offseting detection */
4772
4693
// This detection uses two clocks and two loops, a loop and a timer that the hypervisor can spoof and a second loop/timer that the hypervisor cannot
4773
4694
// When the TSC is "hooked", the hypervisor usually downscales the result to hide the time passed or doesnt let TSC advance for the time it was vm-exiting
@@ -4988,6 +4909,7 @@ struct VM {
4988
4909
// we used a rng before running the traditional rdtsc-cpuid-rdtsc trick
4989
4910
4990
4911
// sometimes not intercepted in some hvs (like VirtualBox) under compat mode
4912
+
thread_localu32 aux = 0;
4991
4913
auto cpuid = [&](unsignedint leaf) noexcept -> u64 {
4992
4914
#if (MSVC)
4993
4915
// make regs volatile so writes cannot be optimized out, if this isn't added and the code is compiled in release mode, cycles would be around 40 even under Hyper-V
0 commit comments