2 * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: head/usr.sbin/cpucontrol/cpucontrol.c 267673 2014-06-20 13:13:38Z kib $");
44 #include <sys/queue.h>
45 #include <sys/param.h>
46 #include <sys/types.h>
48 #include <sys/ioctl.h>
49 #include <sys/cpuctl.h>
51 #include "cpucontrol.h"
56 int verbosity_level
= 0;
58 #define DEFAULT_DATADIR "/usr/local/share/cpucontrol"
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); \
85 SLIST_ENTRY(datadir
) next
;
87 static SLIST_HEAD(, datadir
) datadirs
= SLIST_HEAD_INITIALIZER(datadirs
);
89 static struct ucode_handler
{
91 ucode_update_t
*update
;
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
);
112 name
= getprogname();
115 fprintf(stderr
, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
116 "-i level | -i level,level_type | -u] device\n", name
);
121 isdir(const char *path
)
126 error
= stat(path
, &st
);
128 WARN(0, "stat(%s)", path
);
131 return (st
.st_mode
& S_IFDIR
);
135 do_cpuid(const char *cmdarg
, const char *dev
)
138 cpuctl_cpuid_args_t args
;
142 assert(cmdarg
!= NULL
);
145 level
= strtoul(cmdarg
, &endptr
, 16);
146 if (*cmdarg
== '\0' || *endptr
!= '\0') {
147 WARNX(0, "incorrect operand: %s", cmdarg
);
153 * Fill ioctl argument structure.
156 fd
= open(dev
, O_RDONLY
);
158 WARN(0, "error opening %s for reading", dev
);
161 error
= ioctl(fd
, CPUCTL_CPUID
, &args
);
163 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev
);
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]);
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
;
181 assert(cmdarg
!= NULL
);
184 level
= strtoul(cmdarg
, &endptr
, 16);
185 if (*cmdarg
== '\0' || *endptr
== '\0') {
186 WARNX(0, "incorrect or missing operand: %s", cmdarg
);
190 /* Locate the comma... */
191 cmdarg1
= strstr(endptr
, ",");
192 /* ... and skip past it */
194 level_type
= strtoul(cmdarg1
, &endptr1
, 16);
195 if (*cmdarg1
== '\0' || *endptr1
!= '\0') {
196 WARNX(0, "incorrect or missing operand: %s", cmdarg
);
202 * Fill ioctl argument structure.
205 args
.level_type
= level_type
;
206 fd
= open(dev
, O_RDONLY
);
208 WARN(0, "error opening %s for reading", dev
);
211 error
= ioctl(fd
, CPUCTL_CPUID_COUNT
, &args
);
213 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev
);
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]);
225 do_msr(const char *cmdarg
, const char *dev
)
228 cpuctl_msr_args_t args
;
231 unsigned long command
;
232 int do_invert
= 0, op
;
234 const char *command_name
;
238 assert(cmdarg
!= NULL
);
240 len
= strlen(cmdarg
);
242 WARNX(0, "MSR register expected");
248 * Parse command string.
250 msr
= strtoul(cmdarg
, &endptr
, 16);
269 if (op
!= OP_READ
) { /* Complex operation. */
278 data
= strtoull(p
, &endptr
, 16);
279 if (*p
== '\0' || *endptr
!= '\0') {
280 WARNX(0, "argument required: %s", cmdarg
);
286 if (op
== OP_INVAL
) {
287 WARNX(0, "invalid operator: %s", cmdarg
);
293 * Fill ioctl argument structure.
296 if ((do_invert
!= 0) ^ (op
== OP_AND
))
302 command
= CPUCTL_RDMSR
;
303 command_name
= "RDMSR";
306 command
= CPUCTL_WRMSR
;
307 command_name
= "WRMSR";
310 command
= CPUCTL_MSRSBIT
;
311 command_name
= "MSRSBIT";
314 command
= CPUCTL_MSRCBIT
;
315 command_name
= "MSRCBIT";
320 fd
= open(dev
, op
== OP_READ
? O_RDONLY
: O_WRONLY
);
322 WARN(0, "error opening %s for %s", dev
,
323 op
== OP_READ
? "reading" : "writing");
326 error
= ioctl(fd
, command
, &args
);
328 WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev
, command_name
, command
);
333 fprintf(stdout
, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr
,
334 HIGH(args
.data
), LOW(args
.data
));
340 do_update(const char *dev
)
345 struct ucode_handler
*handler
;
348 struct dirent
*direntry
;
349 char buf
[MAXPATHLEN
];
351 fd
= open(dev
, O_RDONLY
);
353 WARN(0, "error opening %s for reading", dev
);
358 * Find the appropriate handler for device.
360 for (i
= 0; i
< NHANDLERS
; i
++)
361 if (handlers
[i
].probe(fd
) == 0)
364 handler
= &handlers
[i
];
366 WARNX(0, "cannot find the appropriate handler for device");
373 * Process every image in specified data directories.
375 SLIST_FOREACH(dir
, &datadirs
, next
) {
376 dirp
= opendir(dir
->path
);
378 WARNX(1, "skipping directory %s: not accessible", dir
->path
);
381 while ((direntry
= readdir(dirp
)) != NULL
) {
382 if (direntry
->d_namlen
== 0)
384 error
= snprintf(buf
, sizeof(buf
), "%s/%s", dir
->path
,
386 if ((unsigned)error
>= sizeof(buf
))
387 WARNX(0, "skipping %s, buffer too short",
389 if (isdir(buf
) != 0) {
390 WARNX(2, "skipping %s: is a directory", buf
);
393 handler
->update(dev
, buf
);
395 error
= closedir(dirp
);
397 WARN(0, "closedir(%s)", dir
->path
);
403 * Add new data directory to the search list.
406 datadir_add(const char *path
)
408 struct datadir
*newdir
;
410 newdir
= (struct datadir
*)malloc(sizeof(*newdir
));
412 err(EX_OSERR
, "cannot allocate memory");
414 SLIST_INSERT_HEAD(&datadirs
, newdir
, next
);
418 main(int argc
, char *argv
[])
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) {
466 c
= flags
& (FLAG_I
| FLAG_M
| FLAG_U
);
469 if (strstr(cmdarg
, ",") != NULL
)
470 error
= do_cpuid_count(cmdarg
, dev
);
472 error
= do_cpuid(cmdarg
, dev
);
475 error
= do_msr(cmdarg
, dev
);
478 error
= do_update(dev
);
481 usage(); /* Only one command can be selected. */
483 SLIST_FREE(&datadirs
, next
, free
);