@@ -978,3 +978,229 @@ index 2253591f6fa..07fb6decb62 100644
978
978
{
979
979
struct object *root;
980
980
struct mapping *mapping;
981
+ From 3b066b60d21e0fa03ee713a5c11ecfc5252de53c Mon Sep 17 00:00:00 2001
982
+ From: Arkadiusz Hiler <ahiler@codeweavers.com>
983
+ Date: Thu, 3 Jun 2021 22:56:08 +0300
984
+ Subject: [PATCH] wineboot: Check if the kernel trusts TSC before using it for
985
+ Qpc.
986
+
987
+ Even if the bits are claiming that TSC meets our requirements the
988
+ hardware implementation may still be broken.
989
+
990
+ The Linux kernel does a lot of quality testing before deciding to use as
991
+ the clock source. If it (or the user, through an override) does not trust
992
+ the TSC we should not trust it either.
993
+
994
+ CW-Bug-Id: #18918
995
+ CW-Bug-Id: #18958
996
+ ---
997
+ programs/wineboot/wineboot.c | 31 +++++++++++++++++++++++++++++++
998
+ 1 file changed, 31 insertions(+)
999
+
1000
+ diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
1001
+ index 9bebededa4f..79a4bda4d67 100644
1002
+ --- a/programs/wineboot/wineboot.c
1003
+ +++ b/programs/wineboot/wineboot.c
1004
+ @@ -315,6 +315,30 @@ static UINT64 read_tsc_frequency(void)
1005
+ return freq;
1006
+ }
1007
+
1008
+ +static BOOL is_tsc_trusted_by_the_kernel(void)
1009
+ +{
1010
+ + char buf[4] = {};
1011
+ + DWORD num_read;
1012
+ + HANDLE handle;
1013
+ + BOOL ret = TRUE;
1014
+ +
1015
+ + handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource",
1016
+ + GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1017
+ +
1018
+ + if (handle == INVALID_HANDLE_VALUE)
1019
+ + return TRUE;
1020
+ +
1021
+ + if (ReadFile( handle, buf, sizeof(buf)-1, &num_read, NULL ))
1022
+ + {
1023
+ + if (!!strcmp( "tsc", buf ))
1024
+ + ret = FALSE;
1025
+ + }
1026
+ +
1027
+ + CloseHandle( handle );
1028
+ +
1029
+ + return ret;
1030
+ +}
1031
+ +
1032
+ static void initialize_qpc_features(struct _KUSER_SHARED_DATA *data)
1033
+ {
1034
+ int regs[4];
1035
+ @@ -346,6 +370,13 @@ static void initialize_qpc_features(struct _KUSER_SHARED_DATA *data)
1036
+ WARN("No invariant TSC, disabling QpcBypass\n");
1037
+ return;
1038
+ }
1039
+ +
1040
+ + if (!is_tsc_trusted_by_the_kernel())
1041
+ + {
1042
+ + WARN("TSC is not trusted by the kernel, disabling QpcBypass.\n");
1043
+ + return;
1044
+ + }
1045
+ +
1046
+ data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED;
1047
+
1048
+ /* check for rdtscp support bit */
1049
+ From 69f2470f937f46ae362d0ba5c144f170f5850a8e Mon Sep 17 00:00:00 2001
1050
+ From: Joshua Ashton <joshua@froggi.es>
1051
+ Date: Thu, 3 Jun 2021 20:27:49 +0100
1052
+ Subject: [PATCH] wineboot: Return TSC frequency in ~Mhz
1053
+
1054
+ Some games such as Horizon Zero Dawn use this registry value to correlate values from rtdsc -> real time.
1055
+
1056
+ In my testing across a few devices, Windows always returns the tsc frequency in this entry, not the current/maximum frequency of the processor.
1057
+
1058
+ Returning the nominal/maximum cpu frequency here causes the game to run in slow motion as it may not match the tsc frequency of the processor.
1059
+
1060
+ Ideally we'd not have to measure this and the kernel would return tsc_khz to userspace, but this is a good enough stop-gap until https://lkml.org/lkml/2020/12/31/72 or something similar is merged.
1061
+
1062
+ Fixes: #4125 (Slow motion bug)
1063
+
1064
+ Signed-off-by: Joshua Ashton <joshua@froggi.es>
1065
+ ---
1066
+ programs/wineboot/wineboot.c | 5 ++++-
1067
+ 1 file changed, 4 insertions(+), 1 deletion(-)
1068
+
1069
+ diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
1070
+ index 79a4bda4d67..0662feac9ce 100644
1071
+ --- a/programs/wineboot/wineboot.c
1072
+ +++ b/programs/wineboot/wineboot.c
1073
+ @@ -969,12 +969,15 @@ static void create_hardware_registry_keys(void)
1074
+ if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE,
1075
+ KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1076
+ {
1077
+ + UINT64 tsc_freq = read_tsc_frequency(); /* Hz */
1078
+ + DWORD tsc_freq_mhz = (DWORD)(tsc_freq / 1000000ull);
1079
+ +
1080
+ RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) );
1081
+ set_reg_value( hkey, L"Identifier", id );
1082
+ /* TODO: report ARM properly */
1083
+ set_reg_value( hkey, L"ProcessorNameString", namestr );
1084
+ set_reg_value( hkey, L"VendorIdentifier", vendorid );
1085
+ - RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) );
1086
+ + RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) );
1087
+ RegCloseKey( hkey );
1088
+ }
1089
+ if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM &&
1090
+ From 2bde7d53b3874dfc6f3066819e00da142443a48f Mon Sep 17 00:00:00 2001
1091
+ From: Joshua Ashton <joshua@froggi.es>
1092
+ Date: Fri, 4 Jun 2021 10:20:51 +0200
1093
+ Subject: [PATCH] wineboot: Calculate TSC frequency once at the start
1094
+
1095
+ This calculates the TSC frequency once at the very start of wineboot.
1096
+
1097
+ This avoids needing to calculate this multiple times which can lead to stalls.
1098
+
1099
+ Signed-off-by: Joshua Ashton <joshua@froggi.es>
1100
+ ---
1101
+ programs/wineboot/wineboot.c | 26 ++++++++++++++------------
1102
+ 1 file changed, 14 insertions(+), 12 deletions(-)
1103
+
1104
+ diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
1105
+ index 0662feac9ce..dc0e645dd09 100644
1106
+ --- a/programs/wineboot/wineboot.c
1107
+ +++ b/programs/wineboot/wineboot.c
1108
+ @@ -339,7 +339,7 @@ static BOOL is_tsc_trusted_by_the_kernel(void)
1109
+ return ret;
1110
+ }
1111
+
1112
+ -static void initialize_qpc_features(struct _KUSER_SHARED_DATA *data)
1113
+ +static void initialize_qpc_features(struct _KUSER_SHARED_DATA *data, UINT64 tsc_frequency)
1114
+ {
1115
+ int regs[4];
1116
+
1117
+ @@ -388,7 +388,7 @@ static void initialize_qpc_features(struct _KUSER_SHARED_DATA *data)
1118
+ else
1119
+ data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_MFENCE;
1120
+
1121
+ - if ((data->QpcFrequency = (read_tsc_frequency() >> 10)))
1122
+ + if ((data->QpcFrequency = (tsc_frequency >> 10)))
1123
+ {
1124
+ data->QpcShift = 10;
1125
+ data->QpcBias = 0;
1126
+ @@ -433,7 +433,7 @@ static UINT64 muldiv_tsc(UINT64 a, UINT64 b, UINT64 c)
1127
+ return ka * kb * c + kb * ra + ka * rb + (ra * rb + c / 2) / c;
1128
+ }
1129
+
1130
+ -static void create_hypervisor_shared_data(void)
1131
+ +static void create_hypervisor_shared_data(UINT64 tsc_frequency)
1132
+ {
1133
+ struct _KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000;
1134
+ struct hypervisor_shared_data *hypervisor_shared_data;
1135
+ @@ -480,7 +480,7 @@ static void create_hypervisor_shared_data(void)
1136
+
1137
+ if (user_shared_data->QpcBypassEnabled & SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED)
1138
+ {
1139
+ - hypervisor_shared_data->QpcMultiplier = muldiv_tsc((UINT64)5000 << 32, (UINT64)2000 << 32, read_tsc_frequency());
1140
+ + hypervisor_shared_data->QpcMultiplier = muldiv_tsc((UINT64)5000 << 32, (UINT64)2000 << 32, tsc_frequency);
1141
+ user_shared_data->QpcBypassEnabled |= SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_HV_PAGE;
1142
+ user_shared_data->QpcInterruptTimeIncrement = (ULONGLONG)1 << 63;
1143
+ user_shared_data->QpcInterruptTimeIncrementShift = 1;
1144
+ @@ -495,7 +495,7 @@ static void create_hypervisor_shared_data(void)
1145
+ UnmapViewOfFile( hypervisor_shared_data );
1146
+ }
1147
+
1148
+ -static void create_user_shared_data(void)
1149
+ +static void create_user_shared_data(UINT64 tsc_frequency)
1150
+ {
1151
+ struct _KUSER_SHARED_DATA *data;
1152
+ RTL_OSVERSIONINFOEXW version;
1153
+ @@ -582,7 +582,7 @@ static void create_user_shared_data(void)
1154
+ data->ActiveGroupCount = 1;
1155
+
1156
+ initialize_xstate_features( data );
1157
+ - initialize_qpc_features( data );
1158
+ + initialize_qpc_features( data, tsc_frequency );
1159
+
1160
+ UnmapViewOfFile( data );
1161
+ }
1162
+ @@ -894,7 +894,7 @@ static void create_bios_key( HKEY system_key )
1163
+ }
1164
+
1165
+ /* create the volatile hardware registry keys */
1166
+ -static void create_hardware_registry_keys(void)
1167
+ +static void create_hardware_registry_keys(UINT64 tsc_frequency)
1168
+ {
1169
+ unsigned int i;
1170
+ HKEY hkey, system_key, cpu_key, fpu_key;
1171
+ @@ -969,8 +969,7 @@ static void create_hardware_registry_keys(void)
1172
+ if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE,
1173
+ KEY_ALL_ACCESS, NULL, &hkey, NULL ))
1174
+ {
1175
+ - UINT64 tsc_freq = read_tsc_frequency(); /* Hz */
1176
+ - DWORD tsc_freq_mhz = (DWORD)(tsc_freq / 1000000ull);
1177
+ + DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */
1178
+
1179
+ RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.FeatureSet, sizeof(DWORD) );
1180
+ set_reg_value( hkey, L"Identifier", id );
1181
+ @@ -1896,9 +1895,12 @@ int __cdecl main( int argc, char *argv[] )
1182
+ BOOL end_session, force, init, kill, restart, shutdown, update;
1183
+ HANDLE event;
1184
+ OBJECT_ATTRIBUTES attr;
1185
+ + UINT64 tsc_frequency;
1186
+ UNICODE_STRING nameW;
1187
+ BOOL is_wow64;
1188
+
1189
+ + tsc_frequency = read_tsc_frequency();
1190
+ +
1191
+ end_session = force = init = kill = restart = shutdown = update = FALSE;
1192
+ GetWindowsDirectoryW( windowsdir, MAX_PATH );
1193
+ if( !SetCurrentDirectoryW( windowsdir ) )
1194
+ @@ -1981,9 +1983,9 @@ int __cdecl main( int argc, char *argv[] )
1195
+
1196
+ ResetEvent( event ); /* in case this is a restart */
1197
+
1198
+ - create_user_shared_data();
1199
+ - create_hypervisor_shared_data();
1200
+ - create_hardware_registry_keys();
1201
+ + create_user_shared_data(tsc_frequency);
1202
+ + create_hypervisor_shared_data(tsc_frequency);
1203
+ + create_hardware_registry_keys(tsc_frequency);
1204
+ create_dynamic_registry_keys();
1205
+ create_environment_registry_keys();
1206
+ create_computer_name_keys();
0 commit comments