-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.cpp
372 lines (295 loc) · 13.4 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*
* Copyright (c) Martin Kinkelin
*
* See the "License.txt" file in the root directory for infos
* about permitted and prohibited uses of this code.
*/
#include <cstdio> //for stdout
#include <iostream> //for cout
#include "mumu.h"
#include <unistd.h> //for pread, pwrite, close
#include <stdlib.h> //for exit
#include <string.h> //strncmp
#include <assert.h> //assert
#include <fcntl.h> //for O_RDONLY
using std::cout;
using std::endl;
void showAndCheckCurrentPStateInfo();//forward declaration
void PrintParams();
void applyUnderclocking();
/*const int count=1+8;
const char* params[count]={
"self"
};*/
/// <summary>Entry point for the program.</summary>
int main(int argc, const char* argv[]) {
cout << endl;
cout << "AmdMsrTweaker v1.1 modified for my own Lenovo Z575 ONLY!!! (voltages are fixed, params ignored!)" << endl;
cout << "argv[0] is: " << argv[0] << endl;
try {
if ((argc > 1)and(0 == strncmp("I wanna brick my system!", argv[1],25))) {//we make sure, because we're about to apply preset voltages!(hardcoded in source code)
PrintParams();
fprintf(stdout,"Before:\n");
showAndCheckCurrentPStateInfo();
applyUnderclocking();
fprintf(stdout,"After:\n");
showAndCheckCurrentPStateInfo();
} else {
showAndCheckCurrentPStateInfo();
}
} catch (const std::exception& e) {
std::cerr << "ERROR: " << e.what() << endl;
return 10;
}
return 0;
}
uint64_t Rdmsr(const uint32_t regIndex) {
uint64_t result[NUMCPUCORES]={0,0,0,0};
char path[255]= "\0";
for (int i = 0; i < NUMCPUCORES; i++) {
int ret=sprintf(path, "/dev/cpu/%d/msr", i);
if (ret<0) {
pERR("snprintf failed");
fprintf(stderr,"!! snprintf ret=%d\n",ret);
exit(-1);
}
fprintf(stdout, startYELLOWcolortext " !! Rdmsr: %s idx:%x ... %lu bytes ... ", path, regIndex, sizeof(result[i]));
int msrdev = open(path, O_RDONLY);
if (msrdev == -1) {
pERR("Failed to open msr device for reading. You need: # modprobe msr");
exit(-1);
}
if (sizeof(result[i]) != pread(msrdev, &(result[i]), sizeof(result[i]), regIndex)) {//read 8 bytes
pERR("Failed to read from msr device");
}
close(msrdev);
fprintf(stdout," done. (result==%"PRIu64" hex:%08x%08x)" endcolor "\n", result[i], (unsigned int)(result[i] >> 32), (unsigned int)(result[i] & 0xFFFFFFFF));//just in case unsigned int gets more than 32 bits for wtw reason in the future! leave the & there.
if (i>0) {
if (result[i-1] != result[i]) {
pERR("Rdmsr: different results for cores(this is expected to be so depending on load)");
fprintf(stderr,"!! core[%d]==%"PRIu64" != core[%d]==%"PRIu64"\n", i-1, result[i-1], i, result[i]);
}
}
}
return result[0];//return only the core0 result
}
void Wrmsr(const uint32_t regIndex, const uint64_t& value) {
char path[255]= "\0";
for (int i = 0; i < NUMCPUCORES; i++) {
int ret=sprintf(path, "/dev/cpu/%d/msr", i);
if (ret<0) {
pERR("snprintf failed");
exit(-1);
}
//fprintf(stdout,"!! Wrmsr: %s idx:%"PRIu32" val:%"PRIu64"\n", path, index, value);
fprintf(stdout, startPURPLEcolortext " !! Wrmsr: %s idx:%x val:%"PRIu64" valx:%08x%08x... ", path, regIndex, value, (unsigned int)(value >> 32), (unsigned int)(value & 0xFFFFFFFF));
int msrdev = open(path, O_WRONLY);
if (msrdev == -1) {
pERR("Failed to open msr device for writing");
exit(-1);
}
if(pwrite(msrdev, &value, sizeof(value), regIndex) != sizeof(value)) {
pERR("Failed to write to msr device");
}
close(msrdev);
fprintf(stdout," done." endcolor "\n");
}
}
void FindFraction(double value, const double* divisors,
int& numerator, int& divisorIndex,
const int minNumerator, const int maxNumerator) {
// limitations: non-negative value and divisors
// count the null-terminated and ascendingly ordered divisors
int numDivisors = 0;
for (; divisors[numDivisors] > 0; numDivisors++) { }
// make sure the value is in a valid range
value = std::max(minNumerator / divisors[numDivisors-1], std::min(maxNumerator / divisors[0], value));
// search the best-matching combo
double bestValue = -1.0; // numerator / divisors[divisorIndex]
for (int i = 0; i < numDivisors; i++) {
const double d = divisors[i];
const int n = std::max(minNumerator, std::min(maxNumerator, (int)(value * d)));
const double myValue = n / d;
if (myValue <= value && myValue > bestValue) {
numerator = n;
divisorIndex = i;
bestValue = myValue;
if (bestValue == value)
break;
}
}
}
inline double multifromfidndid(const int fid, const int did) {
double multi= (fid + 16) / DIVISORS_12[did];
if ((multi < CPUMINMULTI) || (multi > CPUMAXMULTI)) {
std::cerr << startREDcolortext << "!! unexpected multiplier, you're probably running inside virtualbox fid:" << fid << " did:" << did << " multi:" << multi << endcolor << endl;
}
assert(multi>=CPUMINMULTI);
assert(multi<=CPUMAXMULTI);
return multi;
}
inline void multi2fidndid(const double multi, int& fid, int& did) {
assert(multi>=CPUMINMULTI);
assert(multi<CPUMAXMULTI);
const int minNumerator = 16; // numerator: 0x10 = 16 as fixed offset
const int maxNumerator = 31 + minNumerator; // 5 bits => max 2^5-1 = 31
int numerator, divisorIndex;
FindFraction(multi, DIVISORS_12, numerator, divisorIndex, minNumerator, maxNumerator);
fid = numerator - minNumerator;
did = divisorIndex;
}
PStateInfo ReadPState(const uint32_t numpstate) {
assert(numpstate >=0);
assert(numpstate < NUMPSTATES);
const uint64_t msr = Rdmsr(0xc0010064 + numpstate);
PStateInfo result;
int fid, did;
fid = GetBits(msr, 4, 5);
did = GetBits(msr, 0, 4);
result.multi = multifromfidndid(fid, did);
result.VID = GetBits(msr, 9, 7);
fprintf(stdout,"!! ReadPState P%d fid:%d did:%d multi:%02.2f vid:%d\n",
numpstate, fid, did, result.multi, result.VID);
return result;
}
bool WritePState(const uint32_t numpstate, const struct PStateInfo& info) {
assert(numpstate >=0);
assert(numpstate < NUMPSTATES);
const uint32_t regIndex = 0xc0010064 + numpstate;
uint64_t msr = Rdmsr(regIndex);
const int fidbefore = GetBits(msr, 4, 5);
const int didbefore = GetBits(msr, 0, 4);
const double Multi = multifromfidndid(fidbefore, didbefore);
const int VID = GetBits(msr, 9, 7);
fprintf(stdout,"!! Write PState(1of3) read : fid:%d did:%d vid:%d Multi:%f\n", fidbefore, didbefore, VID, Multi);
assert(info.multi >= CPUMINMULTIunderclocked);
assert(info.multi <= CPUMAXMULTIunderclocked);
int fid, did;
multi2fidndid(info.multi, fid, did);
if ((fid != fidbefore) || (did != didbefore)) {
SetBits(msr, fid, 4, 5);
SetBits(msr, did, 0, 4);
assert(info.VID >= CPUMAXVIDunderclocked);
assert(info.VID <= CPUMINVIDunderclocked);
SetBits(msr, info.VID, 9, 7);
fprintf(stdout,"!! Write PState(2of3) write:%d did:%d vid:%d (multi:%02.2f) ...\n", fid, did, info.VID, info.multi);
Wrmsr(regIndex, msr);
fprintf(stdout,"!! Write PState(3of3) write: done.\n");
return true;
} else {
fprintf(stdout,"!! Write PState(2of3 3of3) no write needed: same values. Done.\n");
return false;
}
}
int GetCurrentPState() {
const uint64_t msr = Rdmsr(0xc0010071);
const int i = GetBits(msr, 16, 3);//0..7
return i;
}
void SetCurrentPState(int numpstate) {
if (numpstate < 0 || numpstate >= NUMPSTATES)
throw ExceptionWithMessage("P-state index out of range");
// //so excluding the turbo state, however! isn't P0 the turbo state? unless this means that pstates here start from 0 to 7 and represent P7 to P0 in this order! (need to FIXME: verify this!) nope, P0 is turbo state here too, confirmed by http://review.coreboot.org/gitweb?p=coreboot.git;a=commitdiff;h=363010694dba5b5c9132e78be357a1098bdc0aba which says "/* All cores: set pstate 0 (1600 MHz) early to save a few ms of boot time */"
//ok so I got it: https://github.com/johkra/amdmsrtweaker-lnx/commit/11a4fe2f486a6686bd5e64bc0e6859145a890ef2#commitcomment-13245640
//decrease the turbo state(s) because index=0 is P1 ... and there is no way to select turbo state! (I may still be wrong, but this explaination makes sense why this was originally coded this way)
numpstate -= 1;//NumBoostStates;//XXX: no idea why decrease by 1 here then.
if (numpstate < 0)
numpstate = 0;
uint32_t regIndex = 0xc0010062;
uint64_t msr = Rdmsr(regIndex);
SetBits(msr, numpstate, 0, 3);
Wrmsr(regIndex, msr);
//Next, wait for the new pstate to be set, code from: https://chromium.googlesource.com/chromiumos/third_party/coreboot/+/c02b4fc9db3c3c1e263027382697b566127f66bb/src/cpu/amd/model_10xxx/fidvid.c line 367
regIndex=0xC0010063;
int i=-1;
int j=-1;
do {
msr = Rdmsr(regIndex);
i = GetBits(msr, 0, 16);
j = GetBits(msr, 0, 64);
cout << "i=" << i << " j=" << j << " wanted:" << numpstate << endl;//only printed once, because it's already set apparently.
} while (i != numpstate);
}
inline double vid2voltage(const int vid) {
return V155 - vid * CPUVIDSTEP;
}
inline int voltage2vid(double voltage) {
assert(CPUVIDSTEP > 0);
assert(voltage > 0.0);
assert(voltage < V155);//XXX: actual max for my CPU is probably 1.40V though!(need to verify this).
//^ wanna catch the mistake rather than just round to the limits
//XXX: here, just making sure input voltage doesn't exceed 1.325V ! (my CPU)
voltage = std::max(0.0, std::min(V1325, voltage));//done: use a less than 1.55 max voltage there, depending on reported one which is 1.325V for my cpu eg. 1.45 shouldn't be allowed!; OK, maybe that 1.55 is something else... in which case ignore all this.
assert(voltage<=V1325);
assert(voltage >= CPUMINVOLTAGEunderclocked); //that's the lowest (pstate7) stable voltage for my CPU, multi:8x
// assert(vid<=1.0875); //that's highest (pstate0) stable voltage for my CPU, multi:22x; but initially it's 1.325V at 22x pstate0, before the downclocking!
assert(voltage <= CPUMAXVOLTAGE);//when not underclocked, this is tops
// round to nearest step
int r = (int)(voltage / CPUVIDSTEP + 0.5);
//1.55 / VIDStep = highest VID (124)
int vid= (int)(V155 / CPUVIDSTEP) - r;//VIDStep is 0.0125; so, 124 - 87(for 1.0875 aka 22x multi) = 37
assert(vid >= CPUMAXVID);//multi 23x, fid 30, did 2, vid 18, pstate0 (highest) normal clocked
assert(vid <= CPUMINVIDunderclocked);//multi 8x, fid 0, did 2 vid 67, pstate7(lowest) underclocked
return vid;
}
void PrintParams() {
assert(V1325 == bootdefaults_psi[0].strvid);//ensuring; but not using V1325 because of genericity of that def. (could be changed but this use here should not!)
fprintf(stdout,"Hardcoded values to apply:\n");
for (int i = 0; i < NUMPSTATES; i++) {
assert( allpsi[i].VID/*eg. 37*/ == voltage2vid(allpsi[i].strvid /*eg. 1.0875*/));
fprintf(stdout,"pstate:%d multi:%02.2f vid:%d\n",// voltage:%d\n",
i,
allpsi[i].multi,
allpsi[i].VID
);
}
}
void applyUnderclocking() {
//pstates stuff:
bool modded=false;
for (size_t i = 0; i < NUMPSTATES; i++) {
modded=WritePState(i, allpsi[i]) | modded;
}
if (modded) {
fprintf(stdout, "Switching to another p-state temporarily so to ensure current one uses newly applied values\n");
const int currentPState = GetCurrentPState();
//we switch to another pstate temporarily, then back again so that it takes effect (apparently that's why, unsure, it's not my coding)
const int lastpstate= NUMPSTATES - 1;//aka the lowest speed one
const int tempPState = (currentPState == lastpstate ? 0 : lastpstate);
// const int tempPState = ((currentPState + 1) % NUMPSTATES);//some cores may already be at current+1 pstate; so don't use this variant
fprintf(stdout,"!! currentpstate:%d temppstate:%d\n", currentPState, tempPState);
SetCurrentPState(tempPState);
fprintf(stdout,"!! currentpstate:%d\n", GetCurrentPState());
SetCurrentPState(currentPState);
fprintf(stdout,"!! currentpstate:%d\n", GetCurrentPState());
}
}
void showAndCheckCurrentPStateInfo() {
bool unexpected=false;
for (int i = 0; i < NUMPSTATES; i++) {
const PStateInfo pi = ReadPState(i);
const double voltage=vid2voltage(pi.VID);
cout << " P" << i << ": " << pi.multi << "x at " << voltage << "V vid:"<< pi.VID << endl;
if ((pi.multi != bootdefaults_psi[i].multi) && (pi.multi != allpsi[i].multi)) {
unexpected=true;
std::cerr << startREDcolortext << "Unexpected PState multi " << "P"<<i<<": "<<pi.multi<<"x (expected "<< allpsi[i].multi<<"x or "<<bootdefaults_psi[i].multi<<"x)" << endcolor << endl;
}
if ((voltage != bootdefaults_psi[i].strvid) && (voltage != allpsi[i].strvid)) {
std::cerr << startREDcolortext << "Unexpected PState voltage " << "P"<<i<<": "<<voltage<<"V (expected "<< allpsi[i].strvid<<"V or "<<bootdefaults_psi[i].strvid<<"V)" << endcolor << endl;
}
if ((pi.VID != bootdefaults_psi[i].VID) && (pi.VID != allpsi[i].VID)) {
std::cerr << startREDcolortext << "Unexpected PState vid " << "P"<<i<<": "<<pi.VID<<" (expected "<< allpsi[i].VID<<" or "<<bootdefaults_psi[i].VID<<")" << endcolor << endl;
}
}
if (unexpected) {
throw ExceptionWithMessage("P-state values unexpected(if inside virtualbox avoid running this program!)");
}
}