1181430Sstas/*- 2217046Sstas * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>. 3181430Sstas * All rights reserved. 4181430Sstas * 5181430Sstas * Redistribution and use in source and binary forms, with or without 6181430Sstas * modification, are permitted provided that the following conditions 7181430Sstas * are met: 8181430Sstas * 1. Redistributions of source code must retain the above copyright 9181430Sstas * notice, this list of conditions and the following disclaimer. 10181430Sstas * 2. Redistributions in binary form must reproduce the above copyright 11181430Sstas * notice, this list of conditions and the following disclaimer in the 12181430Sstas * documentation and/or other materials provided with the distribution. 13181430Sstas * 14181430Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15181430Sstas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16181430Sstas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17181430Sstas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18181430Sstas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19181430Sstas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20181430Sstas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21181430Sstas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22181430Sstas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23181430Sstas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24181430Sstas */ 25181430Sstas 26181430Sstas/* 27181430Sstas * This utility provides userland access to the cpuctl(4) pseudo-device 28181430Sstas * features. 29181430Sstas */ 30181430Sstas 31181430Sstas#include <sys/cdefs.h> 32181430Sstas__FBSDID("$FreeBSD: releng/10.3/usr.sbin/cpucontrol/cpucontrol.c 268157 2014-07-02 13:09:26Z kib $"); 33181430Sstas 34181430Sstas#include <assert.h> 35181430Sstas#include <stdio.h> 36181430Sstas#include <stdlib.h> 37181430Sstas#include <string.h> 38181430Sstas#include <unistd.h> 39181430Sstas#include <fcntl.h> 40181430Sstas#include <err.h> 41181430Sstas#include <sysexits.h> 42181430Sstas#include <dirent.h> 43181430Sstas 44181430Sstas#include <sys/queue.h> 45181430Sstas#include <sys/param.h> 46181430Sstas#include <sys/types.h> 47181430Sstas#include <sys/stat.h> 48181430Sstas#include <sys/ioctl.h> 49181430Sstas#include <sys/cpuctl.h> 50181430Sstas 51181430Sstas#include "cpucontrol.h" 52181430Sstas#include "amd.h" 53181430Sstas#include "intel.h" 54228436Sfabient#include "via.h" 55181430Sstas 56181430Sstasint verbosity_level = 0; 57181430Sstas 58181430Sstas#define DEFAULT_DATADIR "/usr/local/share/cpucontrol" 59181430Sstas 60181430Sstas#define FLAG_I 0x01 61181430Sstas#define FLAG_M 0x02 62181430Sstas#define FLAG_U 0x04 63181430Sstas 64195189Sstas#define OP_INVAL 0x00 65195189Sstas#define OP_READ 0x01 66195189Sstas#define OP_WRITE 0x02 67195189Sstas#define OP_OR 0x04 68195189Sstas#define OP_AND 0x08 69195189Sstas 70181430Sstas#define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff) 71181430Sstas#define LOW(val) (uint32_t)((val) & 0xffffffff) 72181430Sstas 73181430Sstas/* 74181430Sstas * Macros for freeing SLISTs, probably must be in /sys/queue.h 75181430Sstas */ 76181430Sstas#define SLIST_FREE(head, field, freef) do { \ 77181430Sstas typeof(SLIST_FIRST(head)) __elm0; \ 78181430Sstas typeof(SLIST_FIRST(head)) __elm; \ 79181430Sstas SLIST_FOREACH_SAFE(__elm, (head), field, __elm0) \ 80181430Sstas (void)(freef)(__elm); \ 81181430Sstas} while(0); 82181430Sstas 83181430Sstasstruct datadir { 84181430Sstas const char *path; 85181430Sstas SLIST_ENTRY(datadir) next; 86181430Sstas}; 87201145Santoinestatic SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs); 88181430Sstas 89241737Sedstatic struct ucode_handler { 90181430Sstas ucode_probe_t *probe; 91181430Sstas ucode_update_t *update; 92181430Sstas} handlers[] = { 93181430Sstas { intel_probe, intel_update }, 94181430Sstas { amd_probe, amd_update }, 95228436Sfabient { via_probe, via_update }, 96181430Sstas}; 97181430Sstas#define NHANDLERS (sizeof(handlers) / sizeof(*handlers)) 98181430Sstas 99181430Sstasstatic void usage(void); 100181430Sstasstatic int isdir(const char *path); 101181430Sstasstatic int do_cpuid(const char *cmdarg, const char *dev); 102268157Skibstatic int do_cpuid_count(const char *cmdarg, const char *dev); 103181430Sstasstatic int do_msr(const char *cmdarg, const char *dev); 104181430Sstasstatic int do_update(const char *dev); 105181430Sstasstatic void datadir_add(const char *path); 106181430Sstas 107181430Sstasstatic void __dead2 108201227Sedusage(void) 109181430Sstas{ 110181430Sstas const char *name; 111181430Sstas 112181430Sstas name = getprogname(); 113181430Sstas if (name == NULL) 114181430Sstas name = "cpuctl"; 115181430Sstas fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | " 116268157Skib "-i level | -i level,level_type | -u] device\n", name); 117181430Sstas exit(EX_USAGE); 118181430Sstas} 119181430Sstas 120181430Sstasstatic int 121181430Sstasisdir(const char *path) 122181430Sstas{ 123181430Sstas int error; 124181430Sstas struct stat st; 125181430Sstas 126181430Sstas error = stat(path, &st); 127181430Sstas if (error < 0) { 128181430Sstas WARN(0, "stat(%s)", path); 129181430Sstas return (error); 130181430Sstas } 131181430Sstas return (st.st_mode & S_IFDIR); 132181430Sstas} 133181430Sstas 134181430Sstasstatic int 135181430Sstasdo_cpuid(const char *cmdarg, const char *dev) 136181430Sstas{ 137181430Sstas unsigned int level; 138181430Sstas cpuctl_cpuid_args_t args; 139181430Sstas int fd, error; 140181430Sstas char *endptr; 141181430Sstas 142181430Sstas assert(cmdarg != NULL); 143181430Sstas assert(dev != NULL); 144181430Sstas 145181430Sstas level = strtoul(cmdarg, &endptr, 16); 146181430Sstas if (*cmdarg == '\0' || *endptr != '\0') { 147181430Sstas WARNX(0, "incorrect operand: %s", cmdarg); 148181430Sstas usage(); 149181430Sstas /* NOTREACHED */ 150181430Sstas } 151181430Sstas 152181430Sstas /* 153181430Sstas * Fill ioctl argument structure. 154181430Sstas */ 155181430Sstas args.level = level; 156181430Sstas fd = open(dev, O_RDONLY); 157181430Sstas if (fd < 0) { 158181628Sstas WARN(0, "error opening %s for reading", dev); 159181430Sstas return (1); 160181430Sstas } 161181430Sstas error = ioctl(fd, CPUCTL_CPUID, &args); 162181430Sstas if (error < 0) { 163181628Sstas WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev); 164181430Sstas close(fd); 165181430Sstas return (error); 166181430Sstas } 167181430Sstas fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", 168181430Sstas level, args.data[0], args.data[1], args.data[2], args.data[3]); 169181430Sstas close(fd); 170181430Sstas return (0); 171181430Sstas} 172181430Sstas 173181430Sstasstatic int 174268157Skibdo_cpuid_count(const char *cmdarg, const char *dev) 175268157Skib{ 176268157Skib char *cmdarg1, *endptr, *endptr1; 177268157Skib unsigned int level, level_type; 178268157Skib cpuctl_cpuid_count_args_t args; 179268157Skib int fd, error; 180268157Skib 181268157Skib assert(cmdarg != NULL); 182268157Skib assert(dev != NULL); 183268157Skib 184268157Skib level = strtoul(cmdarg, &endptr, 16); 185268157Skib if (*cmdarg == '\0' || *endptr == '\0') { 186268157Skib WARNX(0, "incorrect or missing operand: %s", cmdarg); 187268157Skib usage(); 188268157Skib /* NOTREACHED */ 189268157Skib } 190268157Skib /* Locate the comma... */ 191268157Skib cmdarg1 = strstr(endptr, ","); 192268157Skib /* ... and skip past it */ 193268157Skib cmdarg1 += 1; 194268157Skib level_type = strtoul(cmdarg1, &endptr1, 16); 195268157Skib if (*cmdarg1 == '\0' || *endptr1 != '\0') { 196268157Skib WARNX(0, "incorrect or missing operand: %s", cmdarg); 197268157Skib usage(); 198268157Skib /* NOTREACHED */ 199268157Skib } 200268157Skib 201268157Skib /* 202268157Skib * Fill ioctl argument structure. 203268157Skib */ 204268157Skib args.level = level; 205268157Skib args.level_type = level_type; 206268157Skib fd = open(dev, O_RDONLY); 207268157Skib if (fd < 0) { 208268157Skib WARN(0, "error opening %s for reading", dev); 209268157Skib return (1); 210268157Skib } 211268157Skib error = ioctl(fd, CPUCTL_CPUID_COUNT, &args); 212268157Skib if (error < 0) { 213268157Skib WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev); 214268157Skib close(fd); 215268157Skib return (error); 216268157Skib } 217268157Skib fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x " 218268157Skib "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1], 219268157Skib args.data[2], args.data[3]); 220268157Skib close(fd); 221268157Skib return (0); 222268157Skib} 223268157Skib 224268157Skibstatic int 225181430Sstasdo_msr(const char *cmdarg, const char *dev) 226181430Sstas{ 227181430Sstas unsigned int msr; 228181430Sstas cpuctl_msr_args_t args; 229195189Sstas size_t len; 230195189Sstas uint64_t data = 0; 231195189Sstas unsigned long command; 232195189Sstas int do_invert = 0, op; 233181430Sstas int fd, error; 234217046Sstas const char *command_name; 235195189Sstas char *endptr; 236181430Sstas char *p; 237181430Sstas 238181430Sstas assert(cmdarg != NULL); 239181430Sstas assert(dev != NULL); 240195189Sstas len = strlen(cmdarg); 241195189Sstas if (len == 0) { 242195189Sstas WARNX(0, "MSR register expected"); 243195189Sstas usage(); 244195189Sstas /* NOTREACHED */ 245195189Sstas } 246181430Sstas 247195189Sstas /* 248195189Sstas * Parse command string. 249195189Sstas */ 250195189Sstas msr = strtoul(cmdarg, &endptr, 16); 251195189Sstas switch (*endptr) { 252195189Sstas case '\0': 253195189Sstas op = OP_READ; 254195189Sstas break; 255195189Sstas case '=': 256195189Sstas op = OP_WRITE; 257195189Sstas break; 258195189Sstas case '&': 259195189Sstas op = OP_AND; 260195189Sstas endptr++; 261195189Sstas break; 262195189Sstas case '|': 263195189Sstas op = OP_OR; 264195189Sstas endptr++; 265195189Sstas break; 266195189Sstas default: 267195189Sstas op = OP_INVAL; 268195189Sstas } 269195189Sstas if (op != OP_READ) { /* Complex operation. */ 270195189Sstas if (*endptr != '=') 271195189Sstas op = OP_INVAL; 272195189Sstas else { 273195189Sstas p = ++endptr; 274195189Sstas if (*p == '~') { 275195189Sstas do_invert = 1; 276195189Sstas p++; 277195189Sstas } 278195189Sstas data = strtoull(p, &endptr, 16); 279195189Sstas if (*p == '\0' || *endptr != '\0') { 280195189Sstas WARNX(0, "argument required: %s", cmdarg); 281195189Sstas usage(); 282195189Sstas /* NOTREACHED */ 283195189Sstas } 284181430Sstas } 285181430Sstas } 286195189Sstas if (op == OP_INVAL) { 287195189Sstas WARNX(0, "invalid operator: %s", cmdarg); 288181430Sstas usage(); 289181430Sstas /* NOTREACHED */ 290181430Sstas } 291181430Sstas 292181430Sstas /* 293181430Sstas * Fill ioctl argument structure. 294181430Sstas */ 295181430Sstas args.msr = msr; 296195189Sstas if ((do_invert != 0) ^ (op == OP_AND)) 297195189Sstas args.data = ~data; 298195189Sstas else 299195189Sstas args.data = data; 300195189Sstas switch (op) { 301195189Sstas case OP_READ: 302195189Sstas command = CPUCTL_RDMSR; 303217119Sstas command_name = "RDMSR"; 304195189Sstas break; 305195189Sstas case OP_WRITE: 306195189Sstas command = CPUCTL_WRMSR; 307217119Sstas command_name = "WRMSR"; 308195189Sstas break; 309195189Sstas case OP_OR: 310195189Sstas command = CPUCTL_MSRSBIT; 311217119Sstas command_name = "MSRSBIT"; 312195189Sstas break; 313195189Sstas case OP_AND: 314195189Sstas command = CPUCTL_MSRCBIT; 315217119Sstas command_name = "MSRCBIT"; 316195189Sstas break; 317195189Sstas default: 318195189Sstas abort(); 319195189Sstas } 320195189Sstas fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY); 321181430Sstas if (fd < 0) { 322181628Sstas WARN(0, "error opening %s for %s", dev, 323195189Sstas op == OP_READ ? "reading" : "writing"); 324181430Sstas return (1); 325181430Sstas } 326195189Sstas error = ioctl(fd, command, &args); 327181430Sstas if (error < 0) { 328217119Sstas WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev, command_name, command); 329181430Sstas close(fd); 330181430Sstas return (1); 331181430Sstas } 332195189Sstas if (op == OP_READ) 333181430Sstas fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr, 334181430Sstas HIGH(args.data), LOW(args.data)); 335181430Sstas close(fd); 336181430Sstas return (0); 337181430Sstas} 338181430Sstas 339181430Sstasstatic int 340181430Sstasdo_update(const char *dev) 341181430Sstas{ 342181430Sstas int fd; 343181430Sstas unsigned int i; 344181430Sstas int error; 345181430Sstas struct ucode_handler *handler; 346181430Sstas struct datadir *dir; 347235647Sgleb DIR *dirp; 348181430Sstas struct dirent *direntry; 349181430Sstas char buf[MAXPATHLEN]; 350181430Sstas 351181430Sstas fd = open(dev, O_RDONLY); 352181430Sstas if (fd < 0) { 353181628Sstas WARN(0, "error opening %s for reading", dev); 354181430Sstas return (1); 355181430Sstas } 356181430Sstas 357181430Sstas /* 358181430Sstas * Find the appropriate handler for device. 359181430Sstas */ 360181430Sstas for (i = 0; i < NHANDLERS; i++) 361181430Sstas if (handlers[i].probe(fd) == 0) 362181430Sstas break; 363181430Sstas if (i < NHANDLERS) 364181430Sstas handler = &handlers[i]; 365181430Sstas else { 366181430Sstas WARNX(0, "cannot find the appropriate handler for device"); 367181430Sstas close(fd); 368181430Sstas return (1); 369181430Sstas } 370181430Sstas close(fd); 371181430Sstas 372181430Sstas /* 373181430Sstas * Process every image in specified data directories. 374181430Sstas */ 375181430Sstas SLIST_FOREACH(dir, &datadirs, next) { 376235647Sgleb dirp = opendir(dir->path); 377235647Sgleb if (dirp == NULL) { 378181430Sstas WARNX(1, "skipping directory %s: not accessible", dir->path); 379181430Sstas continue; 380181430Sstas } 381235647Sgleb while ((direntry = readdir(dirp)) != NULL) { 382181430Sstas if (direntry->d_namlen == 0) 383181430Sstas continue; 384181430Sstas error = snprintf(buf, sizeof(buf), "%s/%s", dir->path, 385181430Sstas direntry->d_name); 386181430Sstas if ((unsigned)error >= sizeof(buf)) 387181430Sstas WARNX(0, "skipping %s, buffer too short", 388181430Sstas direntry->d_name); 389181430Sstas if (isdir(buf) != 0) { 390181430Sstas WARNX(2, "skipping %s: is a directory", buf); 391181430Sstas continue; 392181430Sstas } 393181430Sstas handler->update(dev, buf); 394181430Sstas } 395235647Sgleb error = closedir(dirp); 396181430Sstas if (error != 0) 397181430Sstas WARN(0, "closedir(%s)", dir->path); 398181430Sstas } 399181430Sstas return (0); 400181430Sstas} 401181430Sstas 402181430Sstas/* 403181430Sstas * Add new data directory to the search list. 404181430Sstas */ 405181430Sstasstatic void 406181430Sstasdatadir_add(const char *path) 407181430Sstas{ 408181430Sstas struct datadir *newdir; 409181430Sstas 410181430Sstas newdir = (struct datadir *)malloc(sizeof(*newdir)); 411181430Sstas if (newdir == NULL) 412181430Sstas err(EX_OSERR, "cannot allocate memory"); 413181430Sstas newdir->path = path; 414181430Sstas SLIST_INSERT_HEAD(&datadirs, newdir, next); 415181430Sstas} 416181430Sstas 417181430Sstasint 418181430Sstasmain(int argc, char *argv[]) 419181430Sstas{ 420181430Sstas int c, flags; 421181430Sstas const char *cmdarg; 422181430Sstas const char *dev; 423181430Sstas int error; 424181430Sstas 425181430Sstas flags = 0; 426181430Sstas error = 0; 427181430Sstas cmdarg = ""; /* To keep gcc3 happy. */ 428181430Sstas 429181430Sstas /* 430181430Sstas * Add all default data dirs to the list first. 431181430Sstas */ 432181430Sstas datadir_add(DEFAULT_DATADIR); 433181430Sstas while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) { 434181430Sstas switch (c) { 435181430Sstas case 'd': 436181430Sstas datadir_add(optarg); 437181430Sstas break; 438181430Sstas case 'i': 439181430Sstas flags |= FLAG_I; 440181430Sstas cmdarg = optarg; 441181430Sstas break; 442181430Sstas case 'm': 443181430Sstas flags |= FLAG_M; 444181430Sstas cmdarg = optarg; 445181430Sstas break; 446181430Sstas case 'u': 447181430Sstas flags |= FLAG_U; 448181430Sstas break; 449181430Sstas case 'v': 450181430Sstas verbosity_level++; 451181430Sstas break; 452181430Sstas case 'h': 453181430Sstas /* FALLTHROUGH */ 454181430Sstas default: 455181430Sstas usage(); 456181430Sstas /* NOTREACHED */ 457181430Sstas } 458181430Sstas } 459181430Sstas argc -= optind; 460181430Sstas argv += optind; 461181430Sstas if (argc < 1) { 462181430Sstas usage(); 463181430Sstas /* NOTREACHED */ 464181430Sstas } 465181430Sstas dev = argv[0]; 466181430Sstas c = flags & (FLAG_I | FLAG_M | FLAG_U); 467181430Sstas switch (c) { 468181430Sstas case FLAG_I: 469268157Skib if (strstr(cmdarg, ",") != NULL) 470268157Skib error = do_cpuid_count(cmdarg, dev); 471268157Skib else 472268157Skib error = do_cpuid(cmdarg, dev); 473181430Sstas break; 474181430Sstas case FLAG_M: 475181430Sstas error = do_msr(cmdarg, dev); 476181430Sstas break; 477181430Sstas case FLAG_U: 478181430Sstas error = do_update(dev); 479181430Sstas break; 480181430Sstas default: 481181430Sstas usage(); /* Only one command can be selected. */ 482181430Sstas } 483181430Sstas SLIST_FREE(&datadirs, next, free); 484181430Sstas return (error); 485181430Sstas} 486