libc/nls: Sync with FreeBSD.
[dragonfly.git] / usr.sbin / cpucontrol / cpucontrol.c
blobd3cb13eee6ec7478d1bd7147d80cfe9cd20ed76a
1 /*-
2 * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 * This utility provides userland access to the cpuctl(4) pseudo-device
28 * features.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: head/usr.sbin/cpucontrol/cpucontrol.c 267673 2014-06-20 13:13:38Z kib $");
34 #include <assert.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <err.h>
41 #include <sysexits.h>
42 #include <dirent.h>
44 #include <sys/queue.h>
45 #include <sys/param.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/ioctl.h>
49 #include <sys/cpuctl.h>
51 #include "cpucontrol.h"
52 #include "amd.h"
53 #include "intel.h"
54 #include "via.h"
56 int verbosity_level = 0;
58 #define DEFAULT_DATADIR "/usr/local/share/cpucontrol"
60 #define FLAG_I 0x01
61 #define FLAG_M 0x02
62 #define FLAG_U 0x04
64 #define OP_INVAL 0x00
65 #define OP_READ 0x01
66 #define OP_WRITE 0x02
67 #define OP_OR 0x04
68 #define OP_AND 0x08
70 #define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
71 #define LOW(val) (uint32_t)((val) & 0xffffffff)
74 * Macros for freeing SLISTs, probably must be in /sys/queue.h
76 #define SLIST_FREE(head, field, freef) do { \
77 typeof(SLIST_FIRST(head)) __elm0; \
78 typeof(SLIST_FIRST(head)) __elm; \
79 SLIST_FOREACH_MUTABLE(__elm, (head), field, __elm0) \
80 (void)(freef)(__elm); \
81 } while(0);
83 struct datadir {
84 const char *path;
85 SLIST_ENTRY(datadir) next;
87 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
89 static struct ucode_handler {
90 ucode_probe_t *probe;
91 ucode_update_t *update;
92 } handlers[] = {
93 { intel_probe, intel_update },
94 { amd_probe, amd_update },
95 { via_probe, via_update },
97 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
99 static void usage(void);
100 static int isdir(const char *path);
101 static int do_cpuid(const char *cmdarg, const char *dev);
102 static int do_cpuid_count(const char *cmdarg, const char *dev);
103 static int do_msr(const char *cmdarg, const char *dev);
104 static int do_update(const char *dev);
105 static void datadir_add(const char *path);
107 static void __dead2
108 usage(void)
110 const char *name;
112 name = getprogname();
113 if (name == NULL)
114 name = "cpuctl";
115 fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
116 "-i level | -i level,level_type | -u] device\n", name);
117 exit(EX_USAGE);
120 static int
121 isdir(const char *path)
123 int error;
124 struct stat st;
126 error = stat(path, &st);
127 if (error < 0) {
128 WARN(0, "stat(%s)", path);
129 return (error);
131 return (st.st_mode & S_IFDIR);
134 static int
135 do_cpuid(const char *cmdarg, const char *dev)
137 unsigned int level;
138 cpuctl_cpuid_args_t args;
139 int fd, error;
140 char *endptr;
142 assert(cmdarg != NULL);
143 assert(dev != NULL);
145 level = strtoul(cmdarg, &endptr, 16);
146 if (*cmdarg == '\0' || *endptr != '\0') {
147 WARNX(0, "incorrect operand: %s", cmdarg);
148 usage();
149 /* NOTREACHED */
153 * Fill ioctl argument structure.
155 args.level = level;
156 fd = open(dev, O_RDONLY);
157 if (fd < 0) {
158 WARN(0, "error opening %s for reading", dev);
159 return (1);
161 error = ioctl(fd, CPUCTL_CPUID, &args);
162 if (error < 0) {
163 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
164 close(fd);
165 return (error);
167 fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
168 level, args.data[0], args.data[1], args.data[2], args.data[3]);
169 close(fd);
170 return (0);
173 static int
174 do_cpuid_count(const char *cmdarg, const char *dev)
176 char *cmdarg1, *endptr, *endptr1;
177 unsigned int level, level_type;
178 cpuctl_cpuid_count_args_t args;
179 int fd, error;
181 assert(cmdarg != NULL);
182 assert(dev != NULL);
184 level = strtoul(cmdarg, &endptr, 16);
185 if (*cmdarg == '\0' || *endptr == '\0') {
186 WARNX(0, "incorrect or missing operand: %s", cmdarg);
187 usage();
188 /* NOTREACHED */
190 /* Locate the comma... */
191 cmdarg1 = strstr(endptr, ",");
192 /* ... and skip past it */
193 cmdarg1 += 1;
194 level_type = strtoul(cmdarg1, &endptr1, 16);
195 if (*cmdarg1 == '\0' || *endptr1 != '\0') {
196 WARNX(0, "incorrect or missing operand: %s", cmdarg);
197 usage();
198 /* NOTREACHED */
202 * Fill ioctl argument structure.
204 args.level = level;
205 args.level_type = level_type;
206 fd = open(dev, O_RDONLY);
207 if (fd < 0) {
208 WARN(0, "error opening %s for reading", dev);
209 return (1);
211 error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
212 if (error < 0) {
213 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
214 close(fd);
215 return (error);
217 fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
218 "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
219 args.data[2], args.data[3]);
220 close(fd);
221 return (0);
224 static int
225 do_msr(const char *cmdarg, const char *dev)
227 unsigned int msr;
228 cpuctl_msr_args_t args;
229 size_t len;
230 uint64_t data = 0;
231 unsigned long command;
232 int do_invert = 0, op;
233 int fd, error;
234 const char *command_name;
235 char *endptr;
236 char *p;
238 assert(cmdarg != NULL);
239 assert(dev != NULL);
240 len = strlen(cmdarg);
241 if (len == 0) {
242 WARNX(0, "MSR register expected");
243 usage();
244 /* NOTREACHED */
248 * Parse command string.
250 msr = strtoul(cmdarg, &endptr, 16);
251 switch (*endptr) {
252 case '\0':
253 op = OP_READ;
254 break;
255 case '=':
256 op = OP_WRITE;
257 break;
258 case '&':
259 op = OP_AND;
260 endptr++;
261 break;
262 case '|':
263 op = OP_OR;
264 endptr++;
265 break;
266 default:
267 op = OP_INVAL;
269 if (op != OP_READ) { /* Complex operation. */
270 if (*endptr != '=')
271 op = OP_INVAL;
272 else {
273 p = ++endptr;
274 if (*p == '~') {
275 do_invert = 1;
276 p++;
278 data = strtoull(p, &endptr, 16);
279 if (*p == '\0' || *endptr != '\0') {
280 WARNX(0, "argument required: %s", cmdarg);
281 usage();
282 /* NOTREACHED */
286 if (op == OP_INVAL) {
287 WARNX(0, "invalid operator: %s", cmdarg);
288 usage();
289 /* NOTREACHED */
293 * Fill ioctl argument structure.
295 args.msr = msr;
296 if ((do_invert != 0) ^ (op == OP_AND))
297 args.data = ~data;
298 else
299 args.data = data;
300 switch (op) {
301 case OP_READ:
302 command = CPUCTL_RDMSR;
303 command_name = "RDMSR";
304 break;
305 case OP_WRITE:
306 command = CPUCTL_WRMSR;
307 command_name = "WRMSR";
308 break;
309 case OP_OR:
310 command = CPUCTL_MSRSBIT;
311 command_name = "MSRSBIT";
312 break;
313 case OP_AND:
314 command = CPUCTL_MSRCBIT;
315 command_name = "MSRCBIT";
316 break;
317 default:
318 abort();
320 fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
321 if (fd < 0) {
322 WARN(0, "error opening %s for %s", dev,
323 op == OP_READ ? "reading" : "writing");
324 return (1);
326 error = ioctl(fd, command, &args);
327 if (error < 0) {
328 WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev, command_name, command);
329 close(fd);
330 return (1);
332 if (op == OP_READ)
333 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
334 HIGH(args.data), LOW(args.data));
335 close(fd);
336 return (0);
339 static int
340 do_update(const char *dev)
342 int fd;
343 unsigned int i;
344 int error;
345 struct ucode_handler *handler;
346 struct datadir *dir;
347 DIR *dirp;
348 struct dirent *direntry;
349 char buf[MAXPATHLEN];
351 fd = open(dev, O_RDONLY);
352 if (fd < 0) {
353 WARN(0, "error opening %s for reading", dev);
354 return (1);
358 * Find the appropriate handler for device.
360 for (i = 0; i < NHANDLERS; i++)
361 if (handlers[i].probe(fd) == 0)
362 break;
363 if (i < NHANDLERS)
364 handler = &handlers[i];
365 else {
366 WARNX(0, "cannot find the appropriate handler for device");
367 close(fd);
368 return (1);
370 close(fd);
373 * Process every image in specified data directories.
375 SLIST_FOREACH(dir, &datadirs, next) {
376 dirp = opendir(dir->path);
377 if (dirp == NULL) {
378 WARNX(1, "skipping directory %s: not accessible", dir->path);
379 continue;
381 while ((direntry = readdir(dirp)) != NULL) {
382 if (direntry->d_namlen == 0)
383 continue;
384 error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
385 direntry->d_name);
386 if ((unsigned)error >= sizeof(buf))
387 WARNX(0, "skipping %s, buffer too short",
388 direntry->d_name);
389 if (isdir(buf) != 0) {
390 WARNX(2, "skipping %s: is a directory", buf);
391 continue;
393 handler->update(dev, buf);
395 error = closedir(dirp);
396 if (error != 0)
397 WARN(0, "closedir(%s)", dir->path);
399 return (0);
403 * Add new data directory to the search list.
405 static void
406 datadir_add(const char *path)
408 struct datadir *newdir;
410 newdir = (struct datadir *)malloc(sizeof(*newdir));
411 if (newdir == NULL)
412 err(EX_OSERR, "cannot allocate memory");
413 newdir->path = path;
414 SLIST_INSERT_HEAD(&datadirs, newdir, next);
418 main(int argc, char *argv[])
420 int c, flags;
421 const char *cmdarg;
422 const char *dev;
423 int error;
425 flags = 0;
426 error = 0;
427 cmdarg = ""; /* To keep gcc3 happy. */
430 * Add all default data dirs to the list first.
432 datadir_add(DEFAULT_DATADIR);
433 while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
434 switch (c) {
435 case 'd':
436 datadir_add(optarg);
437 break;
438 case 'i':
439 flags |= FLAG_I;
440 cmdarg = optarg;
441 break;
442 case 'm':
443 flags |= FLAG_M;
444 cmdarg = optarg;
445 break;
446 case 'u':
447 flags |= FLAG_U;
448 break;
449 case 'v':
450 verbosity_level++;
451 break;
452 case 'h':
453 /* FALLTHROUGH */
454 default:
455 usage();
456 /* NOTREACHED */
459 argc -= optind;
460 argv += optind;
461 if (argc < 1) {
462 usage();
463 /* NOTREACHED */
465 dev = argv[0];
466 c = flags & (FLAG_I | FLAG_M | FLAG_U);
467 switch (c) {
468 case FLAG_I:
469 if (strstr(cmdarg, ",") != NULL)
470 error = do_cpuid_count(cmdarg, dev);
471 else
472 error = do_cpuid(cmdarg, dev);
473 break;
474 case FLAG_M:
475 error = do_msr(cmdarg, dev);
476 break;
477 case FLAG_U:
478 error = do_update(dev);
479 break;
480 default:
481 usage(); /* Only one command can be selected. */
483 SLIST_FREE(&datadirs, next, free);
484 return (error);