/ util / msrtool / msrtool.c
msrtool.c
  1  /* SPDX-License-Identifier: GPL-2.0-only */
  2  
  3  #include <stdio.h>
  4  #include <stdlib.h>
  5  #include <unistd.h>
  6  #include <string.h>
  7  #include <sys/types.h>
  8  #include <sys/stat.h>
  9  #include <fcntl.h>
 10  #include <errno.h>
 11  #include <pci/pci.h>
 12  
 13  #include "msrtool.h"
 14  
 15  #define DEFAULT_CPU 0
 16  static uint8_t cpu = DEFAULT_CPU;
 17  
 18  uint8_t targets_found = 0;
 19  const struct targetdef **targets = NULL;
 20  const struct sysdef *sys = NULL;
 21  uint8_t reserved = 0, verbose = 0, quiet = 0;
 22  
 23  struct pci_access *pacc = NULL;
 24  
 25  static struct targetdef alltargets[] = {
 26  	{ "geodegx2", "AMD Geode(tm) GX2", geodegx2_probe, geodegx2_msrs },
 27  	{ "geodelx", "AMD Geode(tm) LX", geodelx_probe, geodelx_msrs },
 28  	{ "cs5536", "AMD Geode(tm) CS5536", cs5536_probe, cs5536_msrs },
 29  	{ "K8", "AMD K8 Family", k8_probe, k8_msrs },
 30  	{ "via_c7", "VIA Esther processor, C7", via_c7_probe, via_c7_msrs },
 31  	{ "intel_pentium3_early", "Intel Pentium III family", intel_pentium3_early_probe, intel_pentium3_early_msrs },
 32  	{ "intel_pentium3", "Intel Pentium III Xeon Processor, Intel Pentium III Processor", intel_pentium3_probe, intel_pentium3_msrs },
 33  	{ "intel_core1", "Intel Core Duo, Intel Core Solo processors", intel_core1_probe, intel_core1_msrs },
 34  	{ "intel_core2_early", "Intel Xeon Processor 3000, 3200, 5100, 5300, 7300 series, Intel Core 2 Quad processor 6000 series, Intel Core 2 Extreme 6000	series, Intel Core 2 Duo 4000, 5000, 6000, 7000 series processors, Intel Pentium dual-core processors", intel_core2_early_probe, intel_core2_early_msrs },
 35  	{ "intel_core2_later", "Intel Xeon Processor 5200, 5400 series, Intel Core 2 Quad	processors 8000, 9000 series", intel_core2_later_probe, intel_core2_later_msrs },
 36  	{ "intel_pentium4_early", "Intel Xeon Processor, Intel Xeon Processor MP, Intel Pentium 4 processors", intel_pentium4_early_probe, intel_pentium4_early_msrs },
 37  	{ "intel_pentium4_later", "Intel Xeon Processor, Intel Xeon Processor MP, Intel Pentium 4, Pentium D processors", intel_pentium4_later_probe, intel_pentium4_later_msrs },
 38  	{ "intel_pentium_d", "Intel Pentium D processors", intel_pentium_d_probe, intel_pentium_d_msrs },
 39  	{ "intel_nehalem", "Intel Core i5, i7 Processors, Intel Xeon Processor 3400, 3500, 5500, based on Nehalem architecture", intel_nehalem_probe, intel_nehalem_msrs },
 40  	{ "intel_atom", "Intel Atom Processors, N450", intel_atom_probe, intel_atom_msrs },
 41  	{ TARGET_EOT }
 42  };
 43  
 44  static struct sysdef allsystems[] = {
 45  	{ "linux", "Linux with /dev/cpu/*/msr", linux_probe, linux_open, linux_close, linux_rdmsr },
 46  	{ "darwin", "Mac OS X with DirectHW", darwin_probe, darwin_open, darwin_close, darwin_rdmsr },
 47  	{ "freebsd", "FreeBSD with /dev/cpuctl*", freebsd_probe, freebsd_open, freebsd_close, freebsd_rdmsr },
 48  	{ SYSTEM_EOT }
 49  };
 50  
 51  static void syntax(char *argv[]) {
 52  	printf("syntax: %s [-hvqrkl] [-c cpu] [-m system] [-t target ...]\n", argv[0]);
 53  	printf("\t [-i addr=hi[:]lo] | [-s file] | [-d [:]file] | addr...\n");
 54  	printf("  -h\t show this help text\n");
 55  	printf("  -v\t be verbose\n");
 56  	printf("  -q\t be quiet (overrides -v)\n");
 57  	printf("  -r\t include [Reserved] values\n");
 58  	printf("  -k\t list all known systems and targets\n");
 59  	printf("  -l\t list MSRs and bit fields for current target(s) (-kl for ALL targets!)\n");
 60  	printf("  -c\t access MSRs on the specified CPU, default=%d\n", DEFAULT_CPU);
 61  	printf("  -m\t force a system, e.g: -m linux\n");
 62  	printf("  -t\t force a target, can be used multiple times, e.g: -t geodelx -t cs5536\n");
 63  	printf("  -i\t immediate mode\n");
 64  	printf("\t decode hex addr=hi:lo for the target without reading hw value\n");
 65  	printf("\t e.g: -i 4c00000f=f2f100ff56960004\n");
 66  	printf("  -s\t stream mode\n");
 67  	printf("\t read one MSR address per line and append current hw value to the line\n");
 68  	printf("\t use the filename - for stdin/stdout\n");
 69  	printf("\t using -l -s ignores input and will output all MSRs with values\n");
 70  	printf("  -d\t diff mode\n");
 71  	printf("\t read one address and value per line and compare with current hw value,\n");
 72  	printf("\t printing differences to stdout. use the filename - to read from stdin\n");
 73  	printf("\t use :file or :- to reverse diff, normally hw values are considered new\n");
 74  	printf("  addr.. direct mode, read and decode values for the given MSR address(es)\n");
 75  }
 76  
 77  static void *add_target(const struct targetdef *t) {
 78  	void *tmp;
 79  	tmp = realloc(targets, (targets_found + 2) * sizeof(struct targetdef *));
 80  	if (NULL == tmp) {
 81  		perror("realloc");
 82  		return tmp;
 83  	}
 84  	targets = tmp;
 85  	targets[targets_found++] = t;
 86  	targets[targets_found] = NULL;
 87  	return targets;
 88  }
 89  
 90  static int found_system(void) {
 91  	if (!sys || (sys && !sys->name)) {
 92  		fprintf(stderr, "Unable to detect the current operating system!\n");
 93  		fprintf(stderr, "On Linux, please run 'modprobe msr' and try again.\n");
 94  		fprintf(stderr, "Please send a report or patch to coreboot@coreboot.org. Thanks for your help!\n");
 95  		fprintf(stderr, "\n");
 96  	}
 97  	return (sys && sys->name);
 98  }
 99  
100  int do_stream(const char *streamfn, uint8_t ignoreinput) {
101  	char tmpfn[20], line[256];
102  	uint8_t tn;
103  	size_t start, len;
104  	int ret = 1;
105  	int fdout = -1;
106  	FILE *fin = NULL, *fout = NULL;
107  	uint32_t addr, linenum;
108  	struct msr m = MSR1(0);
109  
110  	if (0 == strcmp(streamfn, "-")) {
111  		fin = stdin;
112  		fout = stdout;
113  	} else {
114  		if (!ignoreinput) {
115  			if (NULL == (fin = fopen(streamfn, "r"))) {
116  				perror("fopen()");
117  				return 1;
118  			}
119  			if (snprintf(tmpfn, sizeof(tmpfn), "msrtoolXXXXXX") >= sizeof(tmpfn)) {
120  				perror("snprintf");
121  				return 1;
122  			}
123  			if (-1 == (fdout = mkstemp(tmpfn))) {
124  				perror("mkstemp");
125  				return 1;
126  			}
127  			if (NULL == (fout = fdopen(fdout, "w"))) {
128  				perror("fdopen");
129  				return 1;
130  			}
131  		} else {
132  			if (NULL == (fout = fopen(streamfn, "w"))) {
133  				perror("fopen");
134  				return 1;
135  			}
136  		}
137  	}
138  
139  	if (!found_system())
140  		goto done;
141  	if (!sys->open(cpu, SYS_RDONLY))
142  		goto done;
143  	if (ignoreinput) {
144  		for (tn = 0; tn < targets_found; tn++)
145  			if (dumpmsrdefsvals(fout, targets[tn], cpu)) {
146  				ret = 1;
147  				break;
148  			}
149  	} else {
150  		for (linenum = 1; NULL != fgets(line, sizeof(line), fin); ++linenum) {
151  			start = (0 == strncmp("0x", line, 2)) ? 2 : 0;
152  			if (1 == sscanf(line + start, "%8x", &addr)) {
153  				if (!sys->rdmsr(cpu, addr, &m))
154  					goto done;
155  				fprintf(fout, "0x%08x 0x%08x%08x\n", addr, m.hi, m.lo);
156  				continue;
157  			}
158  			while (1) {
159  				fprintf(fout, "%s", line);
160  				len = strlen(line);
161  				if (NULL != strchr("\r\n", line[len - 1]))
162  					break;
163  				if (NULL == fgets(line, sizeof(line), fin))
164  					goto read_done;
165  			}
166  		}
167  read_done:
168  		if (!feof(fin)) {
169  			fprintf(stderr, "%s:%d: fgets: %s\n", streamfn, linenum, strerror(errno));
170  			goto done;
171  		}
172  	}
173  	ret = 0;
174  done:
175  	sys->close(cpu);
176  	if (strcmp(streamfn, "-")) {
177  		if (ret)
178  			unlink(tmpfn);
179  		else if (!ignoreinput)
180  			rename(tmpfn, streamfn);
181  	}
182  	if (!ignoreinput)
183  		fclose(fin);
184  	fclose(fout);
185  	return ret;
186  }
187  
188  int do_diff(const char *difffn) {
189  	char tmpfn[20], line[512], *m1start, *m2start;
190  	size_t len;
191  	int ret = 1, tmp, m1pos, sys_opened = 0;
192  	FILE *fin = NULL, *fout = stdout;
193  	uint8_t rev = 0;
194  	uint32_t addr, linenum;
195  	struct msr m1 = MSR1(0), m2 = MSR1(0);
196  
197  	if (':' == difffn[0]) {
198  		rev = 1;
199  		++difffn;
200  	}
201  	if (0 == strcmp(difffn, "-"))
202  		fin = stdin;
203  	else if (NULL == (fin = fopen(difffn, "r"))) {
204  		perror("fopen()");
205  		return 1;
206  	}
207  
208  	for (linenum = 1; NULL != fgets(line, sizeof(line), fin); ++linenum) {
209  		tmp = strncmp("0x", line, 2) ? 0 : 2;
210  		if (sscanf(line + tmp, "%8x %n%*x", &addr, &m1pos) < 1)
211  			continue;
212  		m1start = line + tmp + m1pos;
213  		for (len = strlen(m1start) - 1; NULL != strchr("\r\n", m1start[len]); --len)
214  			m1start[len] = 0;
215  		if (!str2msr(m1start, &m1, &m2start)) {
216  			fprintf(stderr, "%s:%d: invalid MSR1 value '%s'\n", difffn, linenum, m1start);
217  			continue;
218  		}
219  		while (' ' == *m2start)
220  			++m2start;
221  		if (!str2msr(m2start, &m2, NULL)) {
222  			fprintf(stderr, "%s:%d: invalid MSR2 value '%s' - reading from hardware!\n", difffn, linenum, m2start);
223  			if (!sys_opened) {
224  				if (!found_system())
225  					goto done;
226  				sys_opened = sys->open(cpu, SYS_RDONLY);
227  				if (!sys_opened)
228  					goto done;
229  			}
230  			if (!sys->rdmsr(cpu, addr, &m2))
231  				goto done;
232  		}
233  		if (diff_msr(fout, addr, rev ? m2 : m1, rev ? m1 : m2))
234  			fprintf(fout, "\n");
235  	}
236  	if (!feof(fin))
237  		fprintf(stderr, "%s:%d: fgets: %s\n", difffn, linenum, strerror(errno));
238  	else
239  		ret = 0;
240  done:
241  	if (sys_opened)
242  		sys->close(cpu);
243  	if (strcmp(difffn, "-")) {
244  		if (ret)
245  			unlink(tmpfn);
246  		else
247  			rename(tmpfn, difffn);
248  		fclose(fin);
249  		fclose(fout);
250  	}
251  	return ret;
252  }
253  
254  int main(int argc, char *argv[]) {
255  	char c;
256  	int ret = 1;
257  	const struct sysdef *s;
258  	const struct targetdef *t;
259  	uint8_t tn, listmsrs = 0, listknown = 0, input = 0;
260  	uint32_t addr = 0;
261  	const char *streamfn = NULL, *difffn = NULL;
262  	struct msr msrval = MSR2(-1, -1);
263  	while ((c = getopt(argc, argv, "hqvrklc:m:t:a:i:s:d:")) != -1)
264  		switch (c) {
265  		case 'h':
266  			syntax(argv);
267  			return 0;
268  		case 'q':
269  			quiet = 1;
270  			break;
271  		case 'v':
272  			++verbose;
273  			break;
274  		case 'r':
275  			reserved = 1;
276  			break;
277  		case 'k':
278  			listknown = 1;
279  			break;
280  		case 'l':
281  			listmsrs = 1;
282  			break;
283  		case 'c':
284  			cpu = atoi(optarg);
285  			break;
286  		case 'm':
287  			for (s = allsystems; !SYSTEM_ISEOT(*s); s++)
288  				if (!strcmp(s->name, optarg)) {
289  					sys = s;
290  					break;
291  				}
292  			break;
293  		case 't':
294  			for (t = alltargets; !TARGET_ISEOT(*t); t++)
295  				if (!strcmp(t->name, optarg)) {
296  					add_target(t);
297  					break;
298  				}
299  			break;
300  		case 'i':
301  			input = 1;
302  			addr = msraddrbyname(optarg);
303  			optarg = strchr(optarg, '=');
304  			if (NULL == optarg) {
305  				fprintf(stderr, "missing value in -i argument!\n");
306  				break;
307  			}
308  			if (!str2msr(++optarg, &msrval, NULL))
309  				fprintf(stderr, "invalid value in -i argument!\n");
310  			break;
311  		case 's':
312  			streamfn = optarg;
313  			break;
314  		case 'd':
315  			difffn = optarg;
316  			break;
317  		default:
318  			break;
319  		}
320  
321  	/** cpuid is called after reading argv so that verbose is set */
322  	const struct cpuid_t *id = cpuid();
323  
324  	printf_quiet("msrtool %s\n", VERSION);
325  
326  	pacc = pci_alloc();
327  	if (NULL == pacc) {
328  		fprintf(stderr, "Could not initialize PCI library! pci_alloc() failed.\n");
329  		return 1;
330  	}
331  	pci_init(pacc);
332  	pci_scan_bus(pacc);
333  
334  	if (!sys && !input && !listknown)
335  		for (sys = allsystems; !SYSTEM_ISEOT(*sys); sys++) {
336  			printf_verbose("Probing for system %s: %s\n", sys->name, sys->prettyname);
337  			if (!sys->probe(sys))
338  				continue;
339  			printf_quiet("Detected system %s: %s\n", sys->name, sys->prettyname);
340  			break;
341  		}
342  
343  	if (targets)
344  		for (tn = 0; tn < targets_found; tn++)
345  			printf_quiet("Forced target %s: %s\n", targets[tn]->name, targets[tn]->prettyname);
346  	else
347  		for (t = alltargets; !TARGET_ISEOT(*t); t++) {
348  			printf_verbose("Probing for target %s: %s\n", t->name, t->prettyname);
349  			if (!t->probe(t, id))
350  				continue;
351  			printf_quiet("Detected target %s: %s\n", t->name, t->prettyname);
352  			add_target(t);
353  		}
354  
355  	printf_quiet("\n");
356  	fflush(stdout);
357  
358  	if (listknown) {
359  		printf("Known systems:\n");
360  		for (s = allsystems; s->name; s++)
361  			printf("%s: %s\n", s->name, s->prettyname);
362  		printf("\nKnown targets:\n");
363  		for (t = alltargets; t->name; t++) {
364  			if (listmsrs && alltargets != t)
365  				printf("\n");
366  			printf("%s: %s\n", t->name, t->prettyname);
367  			if (listmsrs)
368  				dumpmsrdefs(t);
369  		}
370  		printf("\n");
371  		return 0;
372  	}
373  
374  	if (!targets_found || !targets) {
375  		fprintf(stderr, "Unable to detect a known target; can not decode any MSRs! (Use -t to force)\n");
376  		fprintf(stderr, "Please send a report or patch to coreboot@coreboot.org. Thanks for your help!\n");
377  		fprintf(stderr, "\n");
378  		return 1;
379  	}
380  
381  	if (input) {
382  		decodemsr(cpu, addr, msrval);
383  		return 0;
384  	}
385  
386  	if (listmsrs) {
387  		if (streamfn)
388  			return do_stream(streamfn, 1);
389  		for (tn = 0; tn < targets_found; tn++) {
390  			if (tn)
391  				printf("\n");
392  			dumpmsrdefs(targets[tn]);
393  		}
394  		printf("\n");
395  		return 0;
396  	}
397  
398  	if (streamfn)
399  		return do_stream(streamfn, 0);
400  
401  	if (difffn) {
402  		ret = do_diff(difffn);
403  		goto done;
404  	}
405  
406  	if (optind == argc) {
407  		syntax(argv);
408  		printf("\nNo mode or address(es) specified!\n");
409  		return 1;
410  	}
411  
412  	if (!found_system())
413  		return 1;
414  	if (!sys->open(cpu, SYS_RDONLY))
415  		return 1;
416  
417  	for (; optind < argc; optind++) {
418  		addr = msraddrbyname(argv[optind]);
419  		if (!sys->rdmsr(cpu, addr, &msrval))
420  			break;
421  		decodemsr(cpu, addr, msrval);
422  	}
423  	ret = 0;
424  done:
425  	sys->close(cpu);
426  	return ret;
427  }