2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * This utility provides userland access to the cpuctl(4) pseudo-device
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
46 #include <sys/queue.h>
47 #include <sys/param.h>
48 #include <sys/types.h>
50 #include <sys/ioctl.h>
51 #include <sys/cpuctl.h>
53 #include "cpucontrol.h"
58 int verbosity_level
= 0;
60 #define DEFAULT_DATADIR "/usr/local/share/cpucontrol"
74 #define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
75 #define LOW(val) (uint32_t)((val) & 0xffffffff)
78 * Macros for freeing SLISTs, probably must be in /sys/queue.h
82 SLIST_ENTRY(datadir
) next
;
84 static SLIST_HEAD(, datadir
) datadirs
= SLIST_HEAD_INITIALIZER(datadirs
);
86 static struct ucode_handler
{
88 ucode_update_t
*update
;
90 { intel_probe
, intel_update
},
91 { amd10h_probe
, amd10h_update
},
92 { amd_probe
, amd_update
},
93 { via_probe
, via_update
},
95 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
97 static void usage(void);
98 static int isdir(const char *path
);
99 static int do_cpuid(const char *cmdarg
, const char *dev
);
100 static int do_cpuid_count(const char *cmdarg
, const char *dev
);
101 static int do_msr(const char *cmdarg
, const char *dev
);
102 static int do_update(const char *dev
);
103 static void datadir_add(const char *path
);
110 name
= getprogname();
113 fprintf(stderr
, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
114 "-i level | -i level,level_type | -e | -u] device\n", name
);
119 isdir(const char *path
)
124 error
= stat(path
, &st
);
126 WARN(0, "stat(%s)", path
);
129 return (st
.st_mode
& S_IFDIR
);
133 do_cpuid(const char *cmdarg
, const char *dev
)
136 cpuctl_cpuid_args_t args
;
140 assert(cmdarg
!= NULL
);
143 level
= strtoul(cmdarg
, &endptr
, 16);
144 if (*cmdarg
== '\0' || *endptr
!= '\0') {
145 WARNX(0, "incorrect operand: %s", cmdarg
);
151 * Fill ioctl argument structure.
154 fd
= open(dev
, O_RDONLY
);
156 WARN(0, "error opening %s for reading", dev
);
159 error
= ioctl(fd
, CPUCTL_CPUID
, &args
);
161 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev
);
165 fprintf(stdout
, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
166 level
, args
.data
[0], args
.data
[1], args
.data
[2], args
.data
[3]);
172 do_cpuid_count(const char *cmdarg
, const char *dev
)
174 char *cmdarg1
, *endptr
, *endptr1
;
175 unsigned int level
, level_type
;
176 cpuctl_cpuid_count_args_t args
;
179 assert(cmdarg
!= NULL
);
182 level
= strtoul(cmdarg
, &endptr
, 16);
183 if (*cmdarg
== '\0' || *endptr
== '\0') {
184 WARNX(0, "incorrect or missing operand: %s", cmdarg
);
188 /* Locate the comma... */
189 cmdarg1
= strstr(endptr
, ",");
190 /* ... and skip past it */
192 level_type
= strtoul(cmdarg1
, &endptr1
, 16);
193 if (*cmdarg1
== '\0' || *endptr1
!= '\0') {
194 WARNX(0, "incorrect or missing operand: %s", cmdarg
);
200 * Fill ioctl argument structure.
203 args
.level_type
= level_type
;
204 fd
= open(dev
, O_RDONLY
);
206 WARN(0, "error opening %s for reading", dev
);
209 error
= ioctl(fd
, CPUCTL_CPUID_COUNT
, &args
);
211 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev
);
215 fprintf(stdout
, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
216 "0x%.8x 0x%.8x\n", level
, level_type
, args
.data
[0], args
.data
[1],
217 args
.data
[2], args
.data
[3]);
223 do_msr(const char *cmdarg
, const char *dev
)
226 cpuctl_msr_args_t args
;
229 unsigned long command
;
230 int do_invert
= 0, op
;
232 const char *command_name
;
236 assert(cmdarg
!= NULL
);
238 len
= strlen(cmdarg
);
240 WARNX(0, "MSR register expected");
246 * Parse command string.
248 msr
= strtoul(cmdarg
, &endptr
, 16);
267 if (op
!= OP_READ
) { /* Complex operation. */
276 data
= strtoull(p
, &endptr
, 16);
277 if (*p
== '\0' || *endptr
!= '\0') {
278 WARNX(0, "argument required: %s", cmdarg
);
284 if (op
== OP_INVAL
) {
285 WARNX(0, "invalid operator: %s", cmdarg
);
291 * Fill ioctl argument structure.
294 if ((do_invert
!= 0) ^ (op
== OP_AND
))
300 command
= CPUCTL_RDMSR
;
301 command_name
= "RDMSR";
304 command
= CPUCTL_WRMSR
;
305 command_name
= "WRMSR";
308 command
= CPUCTL_MSRSBIT
;
309 command_name
= "MSRSBIT";
312 command
= CPUCTL_MSRCBIT
;
313 command_name
= "MSRCBIT";
318 fd
= open(dev
, op
== OP_READ
? O_RDONLY
: O_WRONLY
);
320 WARN(0, "error opening %s for %s", dev
,
321 op
== OP_READ
? "reading" : "writing");
324 error
= ioctl(fd
, command
, &args
);
326 WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev
, command_name
, command
);
331 fprintf(stdout
, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr
,
332 HIGH(args
.data
), LOW(args
.data
));
338 do_eval_cpu_features(const char *dev
)
344 fd
= open(dev
, O_RDWR
);
346 WARN(0, "error opening %s for writing", dev
);
350 error
= ioctl(fd
, CPUCTL_EVAL_CPU_FEATURES
, NULL
);
352 WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev
);
359 do_update(const char *dev
)
364 struct ucode_handler
*handler
;
367 struct dirent
*direntry
;
368 char buf
[MAXPATHLEN
];
370 fd
= open(dev
, O_RDONLY
);
372 WARN(0, "error opening %s for reading", dev
);
377 * Find the appropriate handler for device.
379 for (i
= 0; i
< NHANDLERS
; i
++)
380 if (handlers
[i
].probe(fd
) == 0)
383 handler
= &handlers
[i
];
385 WARNX(0, "cannot find the appropriate handler for device");
392 * Process every image in specified data directories.
394 SLIST_FOREACH(dir
, &datadirs
, next
) {
395 dirp
= opendir(dir
->path
);
397 WARNX(1, "skipping directory %s: not accessible", dir
->path
);
400 while ((direntry
= readdir(dirp
)) != NULL
) {
401 if (direntry
->d_namlen
== 0)
403 error
= snprintf(buf
, sizeof(buf
), "%s/%s", dir
->path
,
405 if ((unsigned)error
>= sizeof(buf
))
406 WARNX(0, "skipping %s, buffer too short",
408 if (isdir(buf
) != 0) {
409 WARNX(2, "skipping %s: is a directory", buf
);
412 handler
->update(dev
, buf
);
414 error
= closedir(dirp
);
416 WARN(0, "closedir(%s)", dir
->path
);
422 * Add new data directory to the search list.
425 datadir_add(const char *path
)
427 struct datadir
*newdir
;
429 newdir
= (struct datadir
*)malloc(sizeof(*newdir
));
431 err(EX_OSERR
, "cannot allocate memory");
433 SLIST_INSERT_HEAD(&datadirs
, newdir
, next
);
437 main(int argc
, char *argv
[])
446 cmdarg
= ""; /* To keep gcc3 happy. */
448 while ((c
= getopt(argc
, argv
, "d:ehi:m:nuv")) != -1) {
486 if ((flags
& FLAG_N
) == 0)
487 datadir_add(DEFAULT_DATADIR
);
489 c
= flags
& (FLAG_E
| FLAG_I
| FLAG_M
| FLAG_U
);
492 if (strstr(cmdarg
, ",") != NULL
)
493 error
= do_cpuid_count(cmdarg
, dev
);
495 error
= do_cpuid(cmdarg
, dev
);
498 error
= do_msr(cmdarg
, dev
);
501 error
= do_update(dev
);
504 error
= do_eval_cpu_features(dev
);
507 usage(); /* Only one command can be selected. */
509 return (error
== 0 ? 0 : 1);