2 * SPDX-License-Identifier: BSD-2-Clause
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>
46 #include <sys/queue.h>
47 #include <sys/param.h>
48 #include <sys/types.h>
51 #include <sys/ioctl.h>
52 #include <sys/cpuctl.h>
54 #include "cpucontrol.h"
59 int verbosity_level = 0;
61 #define DEFAULT_DATADIR _PATH_LOCALBASE "/share/cpucontrol"
75 #define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
76 #define LOW(val) (uint32_t)((val) & 0xffffffff)
80 SLIST_ENTRY(datadir) next;
82 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
84 static struct ucode_handler {
86 ucode_update_t *update;
88 { intel_probe, intel_update },
89 { amd10h_probe, amd10h_update },
90 { amd_probe, amd_update },
91 { via_probe, via_update },
93 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
95 static void usage(void);
96 static int do_cpuid(const char *cmdarg, const char *dev);
97 static int do_cpuid_count(const char *cmdarg, const char *dev);
98 static int do_msr(const char *cmdarg, const char *dev);
99 static int do_update(const char *dev);
100 static void datadir_add(const char *path);
107 name = getprogname();
110 fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
111 "-i level | -i level,level_type | -e | -u] device\n", name);
116 do_cpuid(const char *cmdarg, const char *dev)
119 cpuctl_cpuid_args_t args;
123 assert(cmdarg != NULL);
126 level = strtoul(cmdarg, &endptr, 16);
127 if (*cmdarg == '\0' || *endptr != '\0') {
128 WARNX(0, "incorrect operand: %s", cmdarg);
134 * Fill ioctl argument structure.
137 fd = open(dev, O_RDONLY);
139 WARN(0, "error opening %s for reading", dev);
142 error = ioctl(fd, CPUCTL_CPUID, &args);
144 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
148 fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
149 level, args.data[0], args.data[1], args.data[2], args.data[3]);
155 do_cpuid_count(const char *cmdarg, const char *dev)
157 char *cmdarg1, *endptr, *endptr1;
158 unsigned int level, level_type;
159 cpuctl_cpuid_count_args_t args;
162 assert(cmdarg != NULL);
165 level = strtoul(cmdarg, &endptr, 16);
166 if (*cmdarg == '\0' || *endptr == '\0') {
167 WARNX(0, "incorrect or missing operand: %s", cmdarg);
171 /* Locate the comma... */
172 cmdarg1 = strstr(endptr, ",");
173 /* ... and skip past it */
175 level_type = strtoul(cmdarg1, &endptr1, 16);
176 if (*cmdarg1 == '\0' || *endptr1 != '\0') {
177 WARNX(0, "incorrect or missing operand: %s", cmdarg);
183 * Fill ioctl argument structure.
186 args.level_type = level_type;
187 fd = open(dev, O_RDONLY);
189 WARN(0, "error opening %s for reading", dev);
192 error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
194 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
198 fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
199 "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
200 args.data[2], args.data[3]);
206 do_msr(const char *cmdarg, const char *dev)
209 cpuctl_msr_args_t args;
212 unsigned long command;
213 int do_invert = 0, op;
215 const char *command_name;
219 assert(cmdarg != NULL);
221 len = strlen(cmdarg);
223 WARNX(0, "MSR register expected");
229 * Parse command string.
231 msr = strtoul(cmdarg, &endptr, 16);
250 if (op != OP_READ) { /* Complex operation. */
259 data = strtoull(p, &endptr, 16);
260 if (*p == '\0' || *endptr != '\0') {
261 WARNX(0, "argument required: %s", cmdarg);
267 if (op == OP_INVAL) {
268 WARNX(0, "invalid operator: %s", cmdarg);
274 * Fill ioctl argument structure.
277 if ((do_invert != 0) ^ (op == OP_AND))
283 command = CPUCTL_RDMSR;
284 command_name = "RDMSR";
287 command = CPUCTL_WRMSR;
288 command_name = "WRMSR";
291 command = CPUCTL_MSRSBIT;
292 command_name = "MSRSBIT";
295 command = CPUCTL_MSRCBIT;
296 command_name = "MSRCBIT";
301 fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
303 WARN(0, "error opening %s for %s", dev,
304 op == OP_READ ? "reading" : "writing");
307 error = ioctl(fd, command, &args);
309 WARN(0, "ioctl(%s, CPUCTL_%s (%#x))", dev, command_name, msr);
314 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
315 HIGH(args.data), LOW(args.data));
321 do_eval_cpu_features(const char *dev)
327 fd = open(dev, O_RDWR);
329 WARN(0, "error opening %s for writing", dev);
332 error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
334 WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
340 try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
341 const char *fname, struct ucode_handler *handler)
343 struct ucode_update_params parm;
352 fwfd = openat(fwdfd, fname, O_RDONLY);
354 WARN(0, "openat(%s, %s)", dpath, fname);
358 rc = asprintf(&fw_path, "%s/%s", dpath, fname);
360 WARNX(0, "out of memory");
365 rc = fstat(fwfd, &st);
367 WARN(0, "fstat(%s)", fw_path);
371 if (!S_ISREG(st.st_mode))
373 if (st.st_size <= 0) {
374 WARN(0, "%s: empty", fw_path);
378 fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
379 if (fw_map == MAP_FAILED) {
380 WARN(0, "mmap(%s)", fw_path);
385 memset(&parm, 0, sizeof(parm));
387 parm.fwimage = fw_map;
388 parm.fwsize = st.st_size;
389 parm.dev_path = dev_path;
390 parm.fw_path = fw_path;
392 handler->update(&parm);
395 if (fw_map != MAP_FAILED)
396 munmap(fw_map, st.st_size);
404 do_update(const char *dev)
409 struct ucode_handler *handler;
412 struct dirent *direntry;
414 fd = open(dev, O_RDONLY);
416 WARN(0, "error opening %s for reading", dev);
421 * Find the appropriate handler for CPU.
423 for (i = 0; i < NHANDLERS; i++)
424 if (handlers[i].probe(fd) == 0)
427 handler = &handlers[i];
429 WARNX(0, "cannot find the appropriate handler for %s", dev);
435 fd = open(dev, O_RDWR);
437 WARN(0, "error opening %s for writing", dev);
442 * Process every image in specified data directories.
444 SLIST_FOREACH(dir, &datadirs, next) {
445 fwdfd = open(dir->path, O_RDONLY);
447 WARN(1, "skipping directory %s: not accessible", dir->path);
450 dirp = fdopendir(fwdfd);
452 WARNX(0, "out of memory");
458 while ((direntry = readdir(dirp)) != NULL) {
459 if (direntry->d_namlen == 0)
461 if (direntry->d_type == DT_DIR)
464 error = try_a_fw_image(dev, fd, fwdfd, dir->path,
465 direntry->d_name, handler);
472 error = closedir(dirp);
474 WARN(0, "closedir(%s)", dir->path);
481 * Add new data directory to the search list.
484 datadir_add(const char *path)
486 struct datadir *newdir;
488 newdir = (struct datadir *)malloc(sizeof(*newdir));
490 err(EX_OSERR, "cannot allocate memory");
492 SLIST_INSERT_HEAD(&datadirs, newdir, next);
496 main(int argc, char *argv[])
506 cmdarg = ""; /* To keep gcc3 happy. */
508 while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
546 if ((flags & FLAG_N) == 0)
547 datadir_add(DEFAULT_DATADIR);
549 c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
552 if (strstr(cmdarg, ",") != NULL)
553 error = do_cpuid_count(cmdarg, dev);
555 error = do_cpuid(cmdarg, dev);
558 error = do_msr(cmdarg, dev);
561 error = do_update(dev);
564 error = do_eval_cpu_features(dev);
567 usage(); /* Only one command can be selected. */
569 while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
570 SLIST_REMOVE_HEAD(&datadirs, next);
573 return (error == 0 ? 0 : 1);