2 * Copyright (c) 2008 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$");
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_SAFE(__elm, (head), field, __elm0) \
80 (void)(freef)(__elm); \
85 SLIST_ENTRY(datadir) next;
87 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
89 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_msr(const char *cmdarg, const char *dev);
103 static int do_update(const char *dev);
104 static void datadir_add(const char *path);
111 name = getprogname();
114 fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
115 "-i level | -u] device\n", name);
120 isdir(const char *path)
125 error = stat(path, &st);
127 WARN(0, "stat(%s)", path);
130 return (st.st_mode & S_IFDIR);
134 do_cpuid(const char *cmdarg, const char *dev)
137 cpuctl_cpuid_args_t args;
141 assert(cmdarg != NULL);
144 level = strtoul(cmdarg, &endptr, 16);
145 if (*cmdarg == '\0' || *endptr != '\0') {
146 WARNX(0, "incorrect operand: %s", cmdarg);
152 * Fill ioctl argument structure.
155 fd = open(dev, O_RDONLY);
157 WARN(0, "error opening %s for reading", dev);
160 error = ioctl(fd, CPUCTL_CPUID, &args);
162 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
166 fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
167 level, args.data[0], args.data[1], args.data[2], args.data[3]);
173 do_msr(const char *cmdarg, const char *dev)
176 cpuctl_msr_args_t args;
179 unsigned long command;
180 int do_invert = 0, op;
185 assert(cmdarg != NULL);
187 len = strlen(cmdarg);
189 WARNX(0, "MSR register expected");
195 * Parse command string.
197 msr = strtoul(cmdarg, &endptr, 16);
216 if (op != OP_READ) { /* Complex operation. */
225 data = strtoull(p, &endptr, 16);
226 if (*p == '\0' || *endptr != '\0') {
227 WARNX(0, "argument required: %s", cmdarg);
233 if (op == OP_INVAL) {
234 WARNX(0, "invalid operator: %s", cmdarg);
240 * Fill ioctl argument structure.
243 if ((do_invert != 0) ^ (op == OP_AND))
249 command = CPUCTL_RDMSR;
252 command = CPUCTL_WRMSR;
255 command = CPUCTL_MSRSBIT;
258 command = CPUCTL_MSRCBIT;
263 fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
265 WARN(0, "error opening %s for %s", dev,
266 op == OP_READ ? "reading" : "writing");
269 error = ioctl(fd, command, &args);
271 WARN(0, "ioctl(%s, %lu)", dev, command);
276 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
277 HIGH(args.data), LOW(args.data));
283 do_update(const char *dev)
288 struct ucode_handler *handler;
291 struct dirent *direntry;
292 char buf[MAXPATHLEN];
294 fd = open(dev, O_RDONLY);
296 WARN(0, "error opening %s for reading", dev);
301 * Find the appropriate handler for device.
303 for (i = 0; i < NHANDLERS; i++)
304 if (handlers[i].probe(fd) == 0)
307 handler = &handlers[i];
309 WARNX(0, "cannot find the appropriate handler for device");
316 * Process every image in specified data directories.
318 SLIST_FOREACH(dir, &datadirs, next) {
319 dirfd = opendir(dir->path);
321 WARNX(1, "skipping directory %s: not accessible", dir->path);
324 while ((direntry = readdir(dirfd)) != NULL) {
325 if (direntry->d_namlen == 0)
327 error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
329 if ((unsigned)error >= sizeof(buf))
330 WARNX(0, "skipping %s, buffer too short",
332 if (isdir(buf) != 0) {
333 WARNX(2, "skipping %s: is a directory", buf);
336 handler->update(dev, buf);
338 error = closedir(dirfd);
340 WARN(0, "closedir(%s)", dir->path);
346 * Add new data directory to the search list.
349 datadir_add(const char *path)
351 struct datadir *newdir;
353 newdir = (struct datadir *)malloc(sizeof(*newdir));
355 err(EX_OSERR, "cannot allocate memory");
357 SLIST_INSERT_HEAD(&datadirs, newdir, next);
361 main(int argc, char *argv[])
370 cmdarg = ""; /* To keep gcc3 happy. */
373 * Add all default data dirs to the list first.
375 datadir_add(DEFAULT_DATADIR);
376 while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
409 c = flags & (FLAG_I | FLAG_M | FLAG_U);
412 error = do_cpuid(cmdarg, dev);
415 error = do_msr(cmdarg, dev);
418 error = do_update(dev);
421 usage(); /* Only one command can be selected. */
423 SLIST_FREE(&datadirs, next, free);