[PATCH 4/4] wineboot: Initialize and calibrate user shared data Qpc frequency.
Rémi Bernon
rbernon at codeweavers.com
Mon Mar 22 04:42:52 CDT 2021
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
programs/wineboot/wineboot.c | 117 +++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)
diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
index 9e36b3c22dd..9333116e9af 100644
--- a/programs/wineboot/wineboot.c
+++ b/programs/wineboot/wineboot.c
@@ -245,10 +245,127 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data)
static void initialize_qpc_features(struct _KUSER_SHARED_DATA *data)
{
+ int regs[4], cpuid_level, denom, numer, freq, tmp;
+
+ if (data->QpcBypassEnabled) return;
+
data->QpcBypassEnabled = 0;
data->QpcFrequency = TICKSPERSEC;
data->QpcShift = 0;
data->QpcBias = 0;
+
+ if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE])
+ {
+ WARN("No RDTSC support, disabling QpcBypass\n");
+ return;
+ }
+
+ __cpuid(regs, 0x80000000);
+ if (regs[0] < 0x80000007)
+ {
+ WARN("Unable to check invariant TSC, disabling QpcBypass\n");
+ return;
+ }
+
+ /* check for invariant tsc bit */
+ __cpuid(regs, 0x80000007);
+ if (!(regs[3] & (1 << 8)))
+ {
+ WARN("No invariant TSC, disabling QpcBypass\n");
+ return;
+ }
+ data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED;
+
+ /* check for rdtscp support bit */
+ __cpuid(regs, 0x80000001);
+ if ((regs[3] & (1 << 27)))
+ data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_RDTSCP;
+ else if (data->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE])
+ data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_LFENCE;
+ else
+ data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_MFENCE;
+
+ __cpuid(regs, 0);
+ tmp = regs[2];
+ regs[2] = regs[3];
+ regs[3] = tmp;
+
+ /* only available on some intel CPUs */
+ if (memcmp(regs + 1, "GenuineIntel", 12)) data->QpcFrequency = 0;
+ else if ((cpuid_level = regs[0]) < 0x15) data->QpcFrequency = 0;
+ else
+ {
+ __cpuid(regs, 0x15);
+ if (!(denom = regs[0]) || !(numer = regs[1])) data->QpcFrequency = 0;
+ else
+ {
+ if (!(freq = regs[2]) && cpuid_level >= 0x16)
+ {
+ __cpuid(regs, 0x16); /* eax is base freq in MHz */
+ freq = regs[0] * 1000 * denom / numer;
+ }
+
+ data->QpcFrequency = freq * numer / denom;
+ }
+
+ if (!data->QpcFrequency)
+ WARN("Failed to read TSC frequency from CPUID, falling back to calibration.\n");
+ else
+ {
+ data->QpcFrequency = (data->QpcFrequency + (1 << 10) - 1) >> 10;
+ data->QpcShift = 10;
+ data->QpcBias = 0;
+
+ TRACE("TSC frequency read from CPUID, freq %I64d, shift %d, bias %I64d\n",
+ data->QpcFrequency, data->QpcShift, data->QpcBias);
+ }
+ }
+
+ if (!data->QpcFrequency)
+ {
+ LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error;
+ unsigned int aux;
+ UINT retries = 50;
+
+ data->QpcShift = 0;
+ data->QpcBias = 0;
+
+ do
+ {
+ tsc0 = __rdtscp(&aux);
+ time0 = RtlGetSystemTimePrecise();
+ tsc1 = __rdtscp(&aux);
+ Sleep(1);
+ tsc2 = __rdtscp(&aux);
+ time1 = RtlGetSystemTimePrecise();
+ tsc3 = __rdtscp(&aux);
+
+ freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0);
+ freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0);
+ error = llabs((freq1 - freq0) * 1000000 / min(freq1, freq0));
+ }
+ while (error > 100 && retries--);
+
+ if (!retries) WARN("TSC frequency calibration failed, unstable TSC?\n");
+ else
+ {
+ data->QpcFrequency = (freq0 + freq1 + (1 << 10) - 1) >> 11;
+ data->QpcShift = 10;
+ data->QpcBias = 0;
+
+ TRACE("TSC frequency calibration complete, freq %I64d, shift %d, bias %I64d\n",
+ data->QpcFrequency, data->QpcShift, data->QpcBias);
+ }
+ }
+
+ if (!data->QpcFrequency)
+ {
+ WARN("Unable to calibrate TSC frequency, disabling QpcBypass.\n");
+ data->QpcBypassEnabled = 0;
+ data->QpcFrequency = TICKSPERSEC;
+ data->QpcShift = 0;
+ data->QpcBias = 0;
+ }
}
#else
--
2.30.2
More information about the wine-devel
mailing list