xref: /freebsd/usr.sbin/cpucontrol/cpucontrol.c (revision e085f869d5c6ce0f8ea4ada858eac4b7ec2176ea)
1e085f869SStanislav Sedov /*-
2e085f869SStanislav Sedov  * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
3e085f869SStanislav Sedov  * All rights reserved.
4e085f869SStanislav Sedov  *
5e085f869SStanislav Sedov  * Redistribution and use in source and binary forms, with or without
6e085f869SStanislav Sedov  * modification, are permitted provided that the following conditions
7e085f869SStanislav Sedov  * are met:
8e085f869SStanislav Sedov  * 1. Redistributions of source code must retain the above copyright
9e085f869SStanislav Sedov  *    notice, this list of conditions and the following disclaimer.
10e085f869SStanislav Sedov  * 2. Redistributions in binary form must reproduce the above copyright
11e085f869SStanislav Sedov  *    notice, this list of conditions and the following disclaimer in the
12e085f869SStanislav Sedov  *    documentation and/or other materials provided with the distribution.
13e085f869SStanislav Sedov  *
14e085f869SStanislav Sedov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15e085f869SStanislav Sedov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16e085f869SStanislav Sedov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17e085f869SStanislav Sedov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18e085f869SStanislav Sedov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19e085f869SStanislav Sedov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20e085f869SStanislav Sedov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21e085f869SStanislav Sedov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22e085f869SStanislav Sedov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23e085f869SStanislav Sedov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24e085f869SStanislav Sedov  */
25e085f869SStanislav Sedov 
26e085f869SStanislav Sedov /*
27e085f869SStanislav Sedov  * This utility provides userland access to the cpuctl(4) pseudo-device
28e085f869SStanislav Sedov  * features.
29e085f869SStanislav Sedov  */
30e085f869SStanislav Sedov 
31e085f869SStanislav Sedov #include <sys/cdefs.h>
32e085f869SStanislav Sedov __FBSDID("$FreeBSD$");
33e085f869SStanislav Sedov 
34e085f869SStanislav Sedov #include <assert.h>
35e085f869SStanislav Sedov #include <stdio.h>
36e085f869SStanislav Sedov #include <stdlib.h>
37e085f869SStanislav Sedov #include <string.h>
38e085f869SStanislav Sedov #include <unistd.h>
39e085f869SStanislav Sedov #include <fcntl.h>
40e085f869SStanislav Sedov #include <err.h>
41e085f869SStanislav Sedov #include <sysexits.h>
42e085f869SStanislav Sedov #include <dirent.h>
43e085f869SStanislav Sedov 
44e085f869SStanislav Sedov #include <sys/queue.h>
45e085f869SStanislav Sedov #include <sys/param.h>
46e085f869SStanislav Sedov #include <sys/types.h>
47e085f869SStanislav Sedov #include <sys/stat.h>
48e085f869SStanislav Sedov #include <sys/ioctl.h>
49e085f869SStanislav Sedov #include <sys/cpuctl.h>
50e085f869SStanislav Sedov 
51e085f869SStanislav Sedov #include "cpucontrol.h"
52e085f869SStanislav Sedov #include "amd.h"
53e085f869SStanislav Sedov #include "intel.h"
54e085f869SStanislav Sedov 
55e085f869SStanislav Sedov int	verbosity_level = 0;
56e085f869SStanislav Sedov 
57e085f869SStanislav Sedov #define	DEFAULT_DATADIR	"/usr/local/share/cpucontrol"
58e085f869SStanislav Sedov 
59e085f869SStanislav Sedov #define	FLAG_I	0x01
60e085f869SStanislav Sedov #define	FLAG_M	0x02
61e085f869SStanislav Sedov #define	FLAG_U	0x04
62e085f869SStanislav Sedov 
63e085f869SStanislav Sedov #define	HIGH(val)	(uint32_t)(((val) >> 32) & 0xffffffff)
64e085f869SStanislav Sedov #define	LOW(val)	(uint32_t)((val) & 0xffffffff)
65e085f869SStanislav Sedov 
66e085f869SStanislav Sedov /*
67e085f869SStanislav Sedov  * Macros for freeing SLISTs, probably must be in /sys/queue.h
68e085f869SStanislav Sedov  */
69e085f869SStanislav Sedov #define	SLIST_FREE(head, field, freef) do {				\
70e085f869SStanislav Sedov 		typeof(SLIST_FIRST(head)) __elm0;			\
71e085f869SStanislav Sedov 		typeof(SLIST_FIRST(head)) __elm;			\
72e085f869SStanislav Sedov 		SLIST_FOREACH_SAFE(__elm, (head), field, __elm0)	\
73e085f869SStanislav Sedov 			(void)(freef)(__elm);				\
74e085f869SStanislav Sedov } while(0);
75e085f869SStanislav Sedov 
76e085f869SStanislav Sedov struct datadir {
77e085f869SStanislav Sedov 	const char		*path;
78e085f869SStanislav Sedov 	SLIST_ENTRY(datadir)	next;
79e085f869SStanislav Sedov };
80e085f869SStanislav Sedov static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(&datadirs);
81e085f869SStanislav Sedov 
82e085f869SStanislav Sedov struct ucode_handler {
83e085f869SStanislav Sedov 	ucode_probe_t *probe;
84e085f869SStanislav Sedov 	ucode_update_t *update;
85e085f869SStanislav Sedov } handlers[] = {
86e085f869SStanislav Sedov 	{ intel_probe, intel_update },
87e085f869SStanislav Sedov 	{ amd_probe, amd_update },
88e085f869SStanislav Sedov };
89e085f869SStanislav Sedov #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
90e085f869SStanislav Sedov 
91e085f869SStanislav Sedov static void	usage(void);
92e085f869SStanislav Sedov static int	isdir(const char *path);
93e085f869SStanislav Sedov static int	do_cpuid(const char *cmdarg, const char *dev);
94e085f869SStanislav Sedov static int	do_msr(const char *cmdarg, const char *dev);
95e085f869SStanislav Sedov static int	do_update(const char *dev);
96e085f869SStanislav Sedov static void	datadir_add(const char *path);
97e085f869SStanislav Sedov 
98e085f869SStanislav Sedov static void __dead2
99e085f869SStanislav Sedov usage()
100e085f869SStanislav Sedov {
101e085f869SStanislav Sedov 	const char *name;
102e085f869SStanislav Sedov 
103e085f869SStanislav Sedov 	name = getprogname();
104e085f869SStanislav Sedov 	if (name == NULL)
105e085f869SStanislav Sedov 		name = "cpuctl";
106e085f869SStanislav Sedov 	fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
107e085f869SStanislav Sedov 	    "-i level | -u] device\n", name);
108e085f869SStanislav Sedov 	exit(EX_USAGE);
109e085f869SStanislav Sedov }
110e085f869SStanislav Sedov 
111e085f869SStanislav Sedov static int
112e085f869SStanislav Sedov isdir(const char *path)
113e085f869SStanislav Sedov {
114e085f869SStanislav Sedov 	int error;
115e085f869SStanislav Sedov 	struct stat st;
116e085f869SStanislav Sedov 
117e085f869SStanislav Sedov 	error = stat(path, &st);
118e085f869SStanislav Sedov 	if (error < 0) {
119e085f869SStanislav Sedov 		WARN(0, "stat(%s)", path);
120e085f869SStanislav Sedov 		return (error);
121e085f869SStanislav Sedov 	}
122e085f869SStanislav Sedov 	return (st.st_mode & S_IFDIR);
123e085f869SStanislav Sedov }
124e085f869SStanislav Sedov 
125e085f869SStanislav Sedov static int
126e085f869SStanislav Sedov do_cpuid(const char *cmdarg, const char *dev)
127e085f869SStanislav Sedov {
128e085f869SStanislav Sedov 	unsigned int level;
129e085f869SStanislav Sedov 	cpuctl_cpuid_args_t args;
130e085f869SStanislav Sedov 	int fd, error;
131e085f869SStanislav Sedov 	char *endptr;
132e085f869SStanislav Sedov 
133e085f869SStanislav Sedov 	assert(cmdarg != NULL);
134e085f869SStanislav Sedov 	assert(dev != NULL);
135e085f869SStanislav Sedov 
136e085f869SStanislav Sedov 	level = strtoul(cmdarg, &endptr, 16);
137e085f869SStanislav Sedov 	if (*cmdarg == '\0' || *endptr != '\0') {
138e085f869SStanislav Sedov 		WARNX(0, "incorrect operand: %s", cmdarg);
139e085f869SStanislav Sedov 		usage();
140e085f869SStanislav Sedov 		/* NOTREACHED */
141e085f869SStanislav Sedov 	}
142e085f869SStanislav Sedov 
143e085f869SStanislav Sedov 	/*
144e085f869SStanislav Sedov 	 * Fill ioctl argument structure.
145e085f869SStanislav Sedov 	 */
146e085f869SStanislav Sedov 	args.level = level;
147e085f869SStanislav Sedov 	fd = open(dev, O_RDONLY);
148e085f869SStanislav Sedov 	if (fd < 0) {
149e085f869SStanislav Sedov 		WARNX(0, "error opening %s for reading", dev);
150e085f869SStanislav Sedov 		return (1);
151e085f869SStanislav Sedov 	}
152e085f869SStanislav Sedov 	error = ioctl(fd, CPUCTL_CPUID, &args);
153e085f869SStanislav Sedov 	if (error < 0) {
154e085f869SStanislav Sedov 		WARNX(0, "ioctl(%s, CPUCTL_CPUID)", dev);
155e085f869SStanislav Sedov 		close(fd);
156e085f869SStanislav Sedov 		return (error);
157e085f869SStanislav Sedov 	}
158e085f869SStanislav Sedov 	fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
159e085f869SStanislav Sedov 	    level, args.data[0], args.data[1], args.data[2], args.data[3]);
160e085f869SStanislav Sedov 	close(fd);
161e085f869SStanislav Sedov 	return (0);
162e085f869SStanislav Sedov }
163e085f869SStanislav Sedov 
164e085f869SStanislav Sedov static int
165e085f869SStanislav Sedov do_msr(const char *cmdarg, const char *dev)
166e085f869SStanislav Sedov {
167e085f869SStanislav Sedov 	unsigned int msr;
168e085f869SStanislav Sedov 	cpuctl_msr_args_t args;
169e085f869SStanislav Sedov 	int fd, error;
170e085f869SStanislav Sedov 	int wr = 0;
171e085f869SStanislav Sedov 	char *p;
172e085f869SStanislav Sedov 	char *endptr;
173e085f869SStanislav Sedov 
174e085f869SStanislav Sedov 	assert(cmdarg != NULL);
175e085f869SStanislav Sedov 	assert(dev != NULL);
176e085f869SStanislav Sedov 
177e085f869SStanislav Sedov 	p = strchr(cmdarg, '=');
178e085f869SStanislav Sedov 	if (p != NULL) {
179e085f869SStanislav Sedov 		wr = 1;
180e085f869SStanislav Sedov 		*p++ = '\0';
181e085f869SStanislav Sedov 		args.data = strtoull(p, &endptr, 16);
182e085f869SStanislav Sedov 		if (*p == '\0' || *endptr != '\0') {
183e085f869SStanislav Sedov 			WARNX(0, "incorrect MSR value: %s", p);
184e085f869SStanislav Sedov 			usage();
185e085f869SStanislav Sedov 			/* NOTREACHED */
186e085f869SStanislav Sedov 		}
187e085f869SStanislav Sedov 	}
188e085f869SStanislav Sedov 	msr = strtoul(cmdarg, &endptr, 16);
189e085f869SStanislav Sedov 	if (*cmdarg == '\0' || *endptr != '\0') {
190e085f869SStanislav Sedov 		WARNX(0, "incorrect MSR register: %s", cmdarg);
191e085f869SStanislav Sedov 		usage();
192e085f869SStanislav Sedov 		/* NOTREACHED */
193e085f869SStanislav Sedov 	}
194e085f869SStanislav Sedov 
195e085f869SStanislav Sedov 	/*
196e085f869SStanislav Sedov 	 * Fill ioctl argument structure.
197e085f869SStanislav Sedov 	 */
198e085f869SStanislav Sedov 	args.msr = msr;
199e085f869SStanislav Sedov 	fd = open(dev, wr == 0 ? O_RDONLY : O_WRONLY);
200e085f869SStanislav Sedov 	if (fd < 0) {
201e085f869SStanislav Sedov 		WARNX(0, "error opening %s for %s", dev,
202e085f869SStanislav Sedov 		    wr == 0 ? "reading" : "writing");
203e085f869SStanislav Sedov 		return (1);
204e085f869SStanislav Sedov 	}
205e085f869SStanislav Sedov 	error = ioctl(fd, wr == 0 ? CPUCTL_RDMSR : CPUCTL_WRMSR, &args);
206e085f869SStanislav Sedov 	if (error < 0) {
207e085f869SStanislav Sedov 		WARNX(0, "ioctl(%s, %s)", dev,
208e085f869SStanislav Sedov 		    wr == 0 ? "CPUCTL_RDMSR" : "CPUCTL_WRMSR");
209e085f869SStanislav Sedov 		close(fd);
210e085f869SStanislav Sedov 		return (1);
211e085f869SStanislav Sedov 	}
212e085f869SStanislav Sedov 	if (wr == 0)
213e085f869SStanislav Sedov 		fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
214e085f869SStanislav Sedov 		    HIGH(args.data), LOW(args.data));
215e085f869SStanislav Sedov 	close(fd);
216e085f869SStanislav Sedov 	return (0);
217e085f869SStanislav Sedov }
218e085f869SStanislav Sedov 
219e085f869SStanislav Sedov static int
220e085f869SStanislav Sedov do_update(const char *dev)
221e085f869SStanislav Sedov {
222e085f869SStanislav Sedov 	int fd;
223e085f869SStanislav Sedov 	unsigned int i;
224e085f869SStanislav Sedov 	int error;
225e085f869SStanislav Sedov 	struct ucode_handler *handler;
226e085f869SStanislav Sedov 	struct datadir *dir;
227e085f869SStanislav Sedov 	DIR *dirfd;
228e085f869SStanislav Sedov 	struct dirent *direntry;
229e085f869SStanislav Sedov 	char buf[MAXPATHLEN];
230e085f869SStanislav Sedov 
231e085f869SStanislav Sedov 	fd = open(dev, O_RDONLY);
232e085f869SStanislav Sedov 	if (fd < 0) {
233e085f869SStanislav Sedov 		WARNX(0, "error opening %s for reading", dev);
234e085f869SStanislav Sedov 		return (1);
235e085f869SStanislav Sedov 	}
236e085f869SStanislav Sedov 
237e085f869SStanislav Sedov 	/*
238e085f869SStanislav Sedov 	 * Find the appropriate handler for device.
239e085f869SStanislav Sedov 	 */
240e085f869SStanislav Sedov 	for (i = 0; i < NHANDLERS; i++)
241e085f869SStanislav Sedov 		if (handlers[i].probe(fd) == 0)
242e085f869SStanislav Sedov 			break;
243e085f869SStanislav Sedov 	if (i < NHANDLERS)
244e085f869SStanislav Sedov 		handler = &handlers[i];
245e085f869SStanislav Sedov 	else {
246e085f869SStanislav Sedov 		WARNX(0, "cannot find the appropriate handler for device");
247e085f869SStanislav Sedov 		close(fd);
248e085f869SStanislav Sedov 		return (1);
249e085f869SStanislav Sedov 	}
250e085f869SStanislav Sedov 	close(fd);
251e085f869SStanislav Sedov 
252e085f869SStanislav Sedov 	/*
253e085f869SStanislav Sedov 	 * Process every image in specified data directories.
254e085f869SStanislav Sedov 	 */
255e085f869SStanislav Sedov 	SLIST_FOREACH(dir, &datadirs, next) {
256e085f869SStanislav Sedov 		dirfd  = opendir(dir->path);
257e085f869SStanislav Sedov 		if (dirfd == NULL) {
258e085f869SStanislav Sedov 			WARNX(1, "skipping directory %s: not accessible", dir->path);
259e085f869SStanislav Sedov 			continue;
260e085f869SStanislav Sedov 		}
261e085f869SStanislav Sedov 		while ((direntry = readdir(dirfd)) != NULL) {
262e085f869SStanislav Sedov 			if (direntry->d_namlen == 0)
263e085f869SStanislav Sedov 				continue;
264e085f869SStanislav Sedov 			error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
265e085f869SStanislav Sedov 			    direntry->d_name);
266e085f869SStanislav Sedov 			if ((unsigned)error >= sizeof(buf))
267e085f869SStanislav Sedov 				WARNX(0, "skipping %s, buffer too short",
268e085f869SStanislav Sedov 				    direntry->d_name);
269e085f869SStanislav Sedov 			if (isdir(buf) != 0) {
270e085f869SStanislav Sedov 				WARNX(2, "skipping %s: is a directory", buf);
271e085f869SStanislav Sedov 				continue;
272e085f869SStanislav Sedov 			}
273e085f869SStanislav Sedov 			handler->update(dev, buf);
274e085f869SStanislav Sedov 		}
275e085f869SStanislav Sedov 		error = closedir(dirfd);
276e085f869SStanislav Sedov 		if (error != 0)
277e085f869SStanislav Sedov 			WARN(0, "closedir(%s)", dir->path);
278e085f869SStanislav Sedov 	}
279e085f869SStanislav Sedov 	return (0);
280e085f869SStanislav Sedov }
281e085f869SStanislav Sedov 
282e085f869SStanislav Sedov /*
283e085f869SStanislav Sedov  * Add new data directory to the search list.
284e085f869SStanislav Sedov  */
285e085f869SStanislav Sedov static void
286e085f869SStanislav Sedov datadir_add(const char *path)
287e085f869SStanislav Sedov {
288e085f869SStanislav Sedov 	struct datadir *newdir;
289e085f869SStanislav Sedov 
290e085f869SStanislav Sedov 	newdir = (struct datadir *)malloc(sizeof(*newdir));
291e085f869SStanislav Sedov 	if (newdir == NULL)
292e085f869SStanislav Sedov 		err(EX_OSERR, "cannot allocate memory");
293e085f869SStanislav Sedov 	newdir->path = path;
294e085f869SStanislav Sedov 	SLIST_INSERT_HEAD(&datadirs, newdir, next);
295e085f869SStanislav Sedov }
296e085f869SStanislav Sedov 
297e085f869SStanislav Sedov int
298e085f869SStanislav Sedov main(int argc, char *argv[])
299e085f869SStanislav Sedov {
300e085f869SStanislav Sedov 	int c, flags;
301e085f869SStanislav Sedov 	const char *cmdarg;
302e085f869SStanislav Sedov 	const char *dev;
303e085f869SStanislav Sedov 	int error;
304e085f869SStanislav Sedov 
305e085f869SStanislav Sedov 	flags = 0;
306e085f869SStanislav Sedov 	error = 0;
307e085f869SStanislav Sedov 	cmdarg = "";	/* To keep gcc3 happy. */
308e085f869SStanislav Sedov 
309e085f869SStanislav Sedov 	/*
310e085f869SStanislav Sedov 	 * Add all default data dirs to the list first.
311e085f869SStanislav Sedov 	 */
312e085f869SStanislav Sedov 	datadir_add(DEFAULT_DATADIR);
313e085f869SStanislav Sedov 	while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
314e085f869SStanislav Sedov 		switch (c) {
315e085f869SStanislav Sedov 		case 'd':
316e085f869SStanislav Sedov 			datadir_add(optarg);
317e085f869SStanislav Sedov 			break;
318e085f869SStanislav Sedov 		case 'i':
319e085f869SStanislav Sedov 			flags |= FLAG_I;
320e085f869SStanislav Sedov 			cmdarg = optarg;
321e085f869SStanislav Sedov 			break;
322e085f869SStanislav Sedov 		case 'm':
323e085f869SStanislav Sedov 			flags |= FLAG_M;
324e085f869SStanislav Sedov 			cmdarg = optarg;
325e085f869SStanislav Sedov 			break;
326e085f869SStanislav Sedov 		case 'u':
327e085f869SStanislav Sedov 			flags |= FLAG_U;
328e085f869SStanislav Sedov 			break;
329e085f869SStanislav Sedov 		case 'v':
330e085f869SStanislav Sedov 			verbosity_level++;
331e085f869SStanislav Sedov 			break;
332e085f869SStanislav Sedov 		case 'h':
333e085f869SStanislav Sedov 			/* FALLTHROUGH */
334e085f869SStanislav Sedov 		default:
335e085f869SStanislav Sedov 			usage();
336e085f869SStanislav Sedov 			/* NOTREACHED */
337e085f869SStanislav Sedov 		}
338e085f869SStanislav Sedov 	}
339e085f869SStanislav Sedov 	argc -= optind;
340e085f869SStanislav Sedov 	argv += optind;
341e085f869SStanislav Sedov 	if (argc < 1) {
342e085f869SStanislav Sedov 		usage();
343e085f869SStanislav Sedov 		/* NOTREACHED */
344e085f869SStanislav Sedov 	}
345e085f869SStanislav Sedov 	dev = argv[0];
346e085f869SStanislav Sedov 	c = flags & (FLAG_I | FLAG_M | FLAG_U);
347e085f869SStanislav Sedov 	switch (c) {
348e085f869SStanislav Sedov 		case FLAG_I:
349e085f869SStanislav Sedov 			error = do_cpuid(cmdarg, dev);
350e085f869SStanislav Sedov 			break;
351e085f869SStanislav Sedov 		case FLAG_M:
352e085f869SStanislav Sedov 			error = do_msr(cmdarg, dev);
353e085f869SStanislav Sedov 			break;
354e085f869SStanislav Sedov 		case FLAG_U:
355e085f869SStanislav Sedov 			error = do_update(dev);
356e085f869SStanislav Sedov 			break;
357e085f869SStanislav Sedov 		default:
358e085f869SStanislav Sedov 			usage();	/* Only one command can be selected. */
359e085f869SStanislav Sedov 	}
360e085f869SStanislav Sedov 	SLIST_FREE(&datadirs, next, free);
361e085f869SStanislav Sedov 	return (error);
362e085f869SStanislav Sedov }
363