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
80 #define SLIST_FREE(head, field, freef) do { \
81 typeof(SLIST_FIRST(head)) __elm0; \
82 typeof(SLIST_FIRST(head)) __elm; \
83 SLIST_FOREACH_SAFE(__elm, (head), field, __elm0) \
84 (void)(freef)(__elm); \
89 SLIST_ENTRY(datadir) next;
91 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
93 static struct ucode_handler {
95 ucode_update_t *update;
97 { intel_probe, intel_update },
98 { amd10h_probe, amd10h_update },
99 { amd_probe, amd_update },
100 { via_probe, via_update },
102 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
104 static void usage(void);
105 static int isdir(const char *path);
106 static int do_cpuid(const char *cmdarg, const char *dev);
107 static int do_cpuid_count(const char *cmdarg, const char *dev);
108 static int do_msr(const char *cmdarg, const char *dev);
109 static int do_update(const char *dev);
110 static void datadir_add(const char *path);
117 name = getprogname();
120 fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
121 "-i level | -i level,level_type | -e | -u] device\n", name);
126 isdir(const char *path)
131 error = stat(path, &st);
133 WARN(0, "stat(%s)", path);
136 return (st.st_mode & S_IFDIR);
140 do_cpuid(const char *cmdarg, const char *dev)
143 cpuctl_cpuid_args_t args;
147 assert(cmdarg != NULL);
150 level = strtoul(cmdarg, &endptr, 16);
151 if (*cmdarg == '\0' || *endptr != '\0') {
152 WARNX(0, "incorrect operand: %s", cmdarg);
158 * Fill ioctl argument structure.
161 fd = open(dev, O_RDONLY);
163 WARN(0, "error opening %s for reading", dev);
166 error = ioctl(fd, CPUCTL_CPUID, &args);
168 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
172 fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
173 level, args.data[0], args.data[1], args.data[2], args.data[3]);
179 do_cpuid_count(const char *cmdarg, const char *dev)
181 char *cmdarg1, *endptr, *endptr1;
182 unsigned int level, level_type;
183 cpuctl_cpuid_count_args_t args;
186 assert(cmdarg != NULL);
189 level = strtoul(cmdarg, &endptr, 16);
190 if (*cmdarg == '\0' || *endptr == '\0') {
191 WARNX(0, "incorrect or missing operand: %s", cmdarg);
195 /* Locate the comma... */
196 cmdarg1 = strstr(endptr, ",");
197 /* ... and skip past it */
199 level_type = strtoul(cmdarg1, &endptr1, 16);
200 if (*cmdarg1 == '\0' || *endptr1 != '\0') {
201 WARNX(0, "incorrect or missing operand: %s", cmdarg);
207 * Fill ioctl argument structure.
210 args.level_type = level_type;
211 fd = open(dev, O_RDONLY);
213 WARN(0, "error opening %s for reading", dev);
216 error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
218 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
222 fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
223 "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
224 args.data[2], args.data[3]);
230 do_msr(const char *cmdarg, const char *dev)
233 cpuctl_msr_args_t args;
236 unsigned long command;
237 int do_invert = 0, op;
239 const char *command_name;
243 assert(cmdarg != NULL);
245 len = strlen(cmdarg);
247 WARNX(0, "MSR register expected");
253 * Parse command string.
255 msr = strtoul(cmdarg, &endptr, 16);
274 if (op != OP_READ) { /* Complex operation. */
283 data = strtoull(p, &endptr, 16);
284 if (*p == '\0' || *endptr != '\0') {
285 WARNX(0, "argument required: %s", cmdarg);
291 if (op == OP_INVAL) {
292 WARNX(0, "invalid operator: %s", cmdarg);
298 * Fill ioctl argument structure.
301 if ((do_invert != 0) ^ (op == OP_AND))
307 command = CPUCTL_RDMSR;
308 command_name = "RDMSR";
311 command = CPUCTL_WRMSR;
312 command_name = "WRMSR";
315 command = CPUCTL_MSRSBIT;
316 command_name = "MSRSBIT";
319 command = CPUCTL_MSRCBIT;
320 command_name = "MSRCBIT";
325 fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
327 WARN(0, "error opening %s for %s", dev,
328 op == OP_READ ? "reading" : "writing");
331 error = ioctl(fd, command, &args);
333 WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev, command_name, command);
338 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
339 HIGH(args.data), LOW(args.data));
345 do_eval_cpu_features(const char *dev)
351 fd = open(dev, O_RDWR);
353 WARN(0, "error opening %s for writing", dev);
356 error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
358 WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
364 do_update(const char *dev)
369 struct ucode_handler *handler;
372 struct dirent *direntry;
373 char buf[MAXPATHLEN];
375 fd = open(dev, O_RDONLY);
377 WARN(0, "error opening %s for reading", dev);
382 * Find the appropriate handler for device.
384 for (i = 0; i < NHANDLERS; i++)
385 if (handlers[i].probe(fd) == 0)
388 handler = &handlers[i];
390 WARNX(0, "cannot find the appropriate handler for device");
397 * Process every image in specified data directories.
399 SLIST_FOREACH(dir, &datadirs, next) {
400 dirp = opendir(dir->path);
402 WARNX(1, "skipping directory %s: not accessible", dir->path);
405 while ((direntry = readdir(dirp)) != NULL) {
406 if (direntry->d_namlen == 0)
408 error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
410 if ((unsigned)error >= sizeof(buf))
411 WARNX(0, "skipping %s, buffer too short",
413 if (isdir(buf) != 0) {
414 WARNX(2, "skipping %s: is a directory", buf);
417 handler->update(dev, buf);
419 error = closedir(dirp);
421 WARN(0, "closedir(%s)", dir->path);
427 * Add new data directory to the search list.
430 datadir_add(const char *path)
432 struct datadir *newdir;
434 newdir = (struct datadir *)malloc(sizeof(*newdir));
436 err(EX_OSERR, "cannot allocate memory");
438 SLIST_INSERT_HEAD(&datadirs, newdir, next);
442 main(int argc, char *argv[])
451 cmdarg = ""; /* To keep gcc3 happy. */
453 while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
491 if ((flags & FLAG_N) == 0)
492 datadir_add(DEFAULT_DATADIR);
494 c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
497 if (strstr(cmdarg, ",") != NULL)
498 error = do_cpuid_count(cmdarg, dev);
500 error = do_cpuid(cmdarg, dev);
503 error = do_msr(cmdarg, dev);
506 error = do_update(dev);
509 error = do_eval_cpu_features(dev);
512 usage(); /* Only one command can be selected. */
514 SLIST_FREE(&datadirs, next, free);
515 return (error == 0 ? 0 : 1);