1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 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. 15 * 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. 26 */ 27 28 /* 29 * This utility provides userland access to the cpuctl(4) pseudo-device 30 * features. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <assert.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <fcntl.h> 42 #include <err.h> 43 #include <sysexits.h> 44 #include <dirent.h> 45 46 #include <sys/queue.h> 47 #include <sys/param.h> 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 #include <sys/ioctl.h> 51 #include <sys/cpuctl.h> 52 53 #include "cpucontrol.h" 54 #include "amd.h" 55 #include "intel.h" 56 #include "via.h" 57 58 int verbosity_level = 0; 59 60 #define DEFAULT_DATADIR "/usr/local/share/cpucontrol" 61 62 #define FLAG_I 0x01 63 #define FLAG_M 0x02 64 #define FLAG_U 0x04 65 #define FLAG_N 0x08 66 #define FLAG_E 0x10 67 68 #define OP_INVAL 0x00 69 #define OP_READ 0x01 70 #define OP_WRITE 0x02 71 #define OP_OR 0x04 72 #define OP_AND 0x08 73 74 #define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff) 75 #define LOW(val) (uint32_t)((val) & 0xffffffff) 76 77 /* 78 * Macros for freeing SLISTs, probably must be in /sys/queue.h 79 */ 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); \ 85 } while(0); 86 87 struct datadir { 88 const char *path; 89 SLIST_ENTRY(datadir) next; 90 }; 91 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs); 92 93 static struct ucode_handler { 94 ucode_probe_t *probe; 95 ucode_update_t *update; 96 } handlers[] = { 97 { intel_probe, intel_update }, 98 { amd10h_probe, amd10h_update }, 99 { amd_probe, amd_update }, 100 { via_probe, via_update }, 101 }; 102 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers)) 103 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); 111 112 static void __dead2 113 usage(void) 114 { 115 const char *name; 116 117 name = getprogname(); 118 if (name == NULL) 119 name = "cpuctl"; 120 fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | " 121 "-i level | -i level,level_type | -e | -u] device\n", name); 122 exit(EX_USAGE); 123 } 124 125 static int 126 isdir(const char *path) 127 { 128 int error; 129 struct stat st; 130 131 error = stat(path, &st); 132 if (error < 0) { 133 WARN(0, "stat(%s)", path); 134 return (error); 135 } 136 return (st.st_mode & S_IFDIR); 137 } 138 139 static int 140 do_cpuid(const char *cmdarg, const char *dev) 141 { 142 unsigned int level; 143 cpuctl_cpuid_args_t args; 144 int fd, error; 145 char *endptr; 146 147 assert(cmdarg != NULL); 148 assert(dev != NULL); 149 150 level = strtoul(cmdarg, &endptr, 16); 151 if (*cmdarg == '\0' || *endptr != '\0') { 152 WARNX(0, "incorrect operand: %s", cmdarg); 153 usage(); 154 /* NOTREACHED */ 155 } 156 157 /* 158 * Fill ioctl argument structure. 159 */ 160 args.level = level; 161 fd = open(dev, O_RDONLY); 162 if (fd < 0) { 163 WARN(0, "error opening %s for reading", dev); 164 return (1); 165 } 166 error = ioctl(fd, CPUCTL_CPUID, &args); 167 if (error < 0) { 168 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev); 169 close(fd); 170 return (error); 171 } 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]); 174 close(fd); 175 return (0); 176 } 177 178 static int 179 do_cpuid_count(const char *cmdarg, const char *dev) 180 { 181 char *cmdarg1, *endptr, *endptr1; 182 unsigned int level, level_type; 183 cpuctl_cpuid_count_args_t args; 184 int fd, error; 185 186 assert(cmdarg != NULL); 187 assert(dev != NULL); 188 189 level = strtoul(cmdarg, &endptr, 16); 190 if (*cmdarg == '\0' || *endptr == '\0') { 191 WARNX(0, "incorrect or missing operand: %s", cmdarg); 192 usage(); 193 /* NOTREACHED */ 194 } 195 /* Locate the comma... */ 196 cmdarg1 = strstr(endptr, ","); 197 /* ... and skip past it */ 198 cmdarg1 += 1; 199 level_type = strtoul(cmdarg1, &endptr1, 16); 200 if (*cmdarg1 == '\0' || *endptr1 != '\0') { 201 WARNX(0, "incorrect or missing operand: %s", cmdarg); 202 usage(); 203 /* NOTREACHED */ 204 } 205 206 /* 207 * Fill ioctl argument structure. 208 */ 209 args.level = level; 210 args.level_type = level_type; 211 fd = open(dev, O_RDONLY); 212 if (fd < 0) { 213 WARN(0, "error opening %s for reading", dev); 214 return (1); 215 } 216 error = ioctl(fd, CPUCTL_CPUID_COUNT, &args); 217 if (error < 0) { 218 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev); 219 close(fd); 220 return (error); 221 } 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]); 225 close(fd); 226 return (0); 227 } 228 229 static int 230 do_msr(const char *cmdarg, const char *dev) 231 { 232 unsigned int msr; 233 cpuctl_msr_args_t args; 234 size_t len; 235 uint64_t data = 0; 236 unsigned long command; 237 int do_invert = 0, op; 238 int fd, error; 239 const char *command_name; 240 char *endptr; 241 char *p; 242 243 assert(cmdarg != NULL); 244 assert(dev != NULL); 245 len = strlen(cmdarg); 246 if (len == 0) { 247 WARNX(0, "MSR register expected"); 248 usage(); 249 /* NOTREACHED */ 250 } 251 252 /* 253 * Parse command string. 254 */ 255 msr = strtoul(cmdarg, &endptr, 16); 256 switch (*endptr) { 257 case '\0': 258 op = OP_READ; 259 break; 260 case '=': 261 op = OP_WRITE; 262 break; 263 case '&': 264 op = OP_AND; 265 endptr++; 266 break; 267 case '|': 268 op = OP_OR; 269 endptr++; 270 break; 271 default: 272 op = OP_INVAL; 273 } 274 if (op != OP_READ) { /* Complex operation. */ 275 if (*endptr != '=') 276 op = OP_INVAL; 277 else { 278 p = ++endptr; 279 if (*p == '~') { 280 do_invert = 1; 281 p++; 282 } 283 data = strtoull(p, &endptr, 16); 284 if (*p == '\0' || *endptr != '\0') { 285 WARNX(0, "argument required: %s", cmdarg); 286 usage(); 287 /* NOTREACHED */ 288 } 289 } 290 } 291 if (op == OP_INVAL) { 292 WARNX(0, "invalid operator: %s", cmdarg); 293 usage(); 294 /* NOTREACHED */ 295 } 296 297 /* 298 * Fill ioctl argument structure. 299 */ 300 args.msr = msr; 301 if ((do_invert != 0) ^ (op == OP_AND)) 302 args.data = ~data; 303 else 304 args.data = data; 305 switch (op) { 306 case OP_READ: 307 command = CPUCTL_RDMSR; 308 command_name = "RDMSR"; 309 break; 310 case OP_WRITE: 311 command = CPUCTL_WRMSR; 312 command_name = "WRMSR"; 313 break; 314 case OP_OR: 315 command = CPUCTL_MSRSBIT; 316 command_name = "MSRSBIT"; 317 break; 318 case OP_AND: 319 command = CPUCTL_MSRCBIT; 320 command_name = "MSRCBIT"; 321 break; 322 default: 323 abort(); 324 } 325 fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY); 326 if (fd < 0) { 327 WARN(0, "error opening %s for %s", dev, 328 op == OP_READ ? "reading" : "writing"); 329 return (1); 330 } 331 error = ioctl(fd, command, &args); 332 if (error < 0) { 333 WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev, command_name, command); 334 close(fd); 335 return (1); 336 } 337 if (op == OP_READ) 338 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr, 339 HIGH(args.data), LOW(args.data)); 340 close(fd); 341 return (0); 342 } 343 344 static int 345 do_eval_cpu_features(const char *dev) 346 { 347 int fd, error; 348 349 assert(dev != NULL); 350 351 fd = open(dev, O_RDWR); 352 if (fd < 0) { 353 WARN(0, "error opening %s for writing", dev); 354 return (1); 355 } 356 error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL); 357 if (error < 0) 358 WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev); 359 close(fd); 360 return (error); 361 } 362 363 static int 364 do_update(const char *dev) 365 { 366 int fd; 367 unsigned int i; 368 int error; 369 struct ucode_handler *handler; 370 struct datadir *dir; 371 DIR *dirp; 372 struct dirent *direntry; 373 char buf[MAXPATHLEN]; 374 375 fd = open(dev, O_RDONLY); 376 if (fd < 0) { 377 WARN(0, "error opening %s for reading", dev); 378 return (1); 379 } 380 381 /* 382 * Find the appropriate handler for device. 383 */ 384 for (i = 0; i < NHANDLERS; i++) 385 if (handlers[i].probe(fd) == 0) 386 break; 387 if (i < NHANDLERS) 388 handler = &handlers[i]; 389 else { 390 WARNX(0, "cannot find the appropriate handler for device"); 391 close(fd); 392 return (1); 393 } 394 close(fd); 395 396 /* 397 * Process every image in specified data directories. 398 */ 399 SLIST_FOREACH(dir, &datadirs, next) { 400 dirp = opendir(dir->path); 401 if (dirp == NULL) { 402 WARNX(1, "skipping directory %s: not accessible", dir->path); 403 continue; 404 } 405 while ((direntry = readdir(dirp)) != NULL) { 406 if (direntry->d_namlen == 0) 407 continue; 408 error = snprintf(buf, sizeof(buf), "%s/%s", dir->path, 409 direntry->d_name); 410 if ((unsigned)error >= sizeof(buf)) 411 WARNX(0, "skipping %s, buffer too short", 412 direntry->d_name); 413 if (isdir(buf) != 0) { 414 WARNX(2, "skipping %s: is a directory", buf); 415 continue; 416 } 417 handler->update(dev, buf); 418 } 419 error = closedir(dirp); 420 if (error != 0) 421 WARN(0, "closedir(%s)", dir->path); 422 } 423 return (0); 424 } 425 426 /* 427 * Add new data directory to the search list. 428 */ 429 static void 430 datadir_add(const char *path) 431 { 432 struct datadir *newdir; 433 434 newdir = (struct datadir *)malloc(sizeof(*newdir)); 435 if (newdir == NULL) 436 err(EX_OSERR, "cannot allocate memory"); 437 newdir->path = path; 438 SLIST_INSERT_HEAD(&datadirs, newdir, next); 439 } 440 441 int 442 main(int argc, char *argv[]) 443 { 444 int c, flags; 445 const char *cmdarg; 446 const char *dev; 447 int error; 448 449 flags = 0; 450 error = 0; 451 cmdarg = ""; /* To keep gcc3 happy. */ 452 453 while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) { 454 switch (c) { 455 case 'd': 456 datadir_add(optarg); 457 break; 458 case 'e': 459 flags |= FLAG_E; 460 break; 461 case 'i': 462 flags |= FLAG_I; 463 cmdarg = optarg; 464 break; 465 case 'm': 466 flags |= FLAG_M; 467 cmdarg = optarg; 468 break; 469 case 'n': 470 flags |= FLAG_N; 471 break; 472 case 'u': 473 flags |= FLAG_U; 474 break; 475 case 'v': 476 verbosity_level++; 477 break; 478 case 'h': 479 /* FALLTHROUGH */ 480 default: 481 usage(); 482 /* NOTREACHED */ 483 } 484 } 485 argc -= optind; 486 argv += optind; 487 if (argc < 1) { 488 usage(); 489 /* NOTREACHED */ 490 } 491 if ((flags & FLAG_N) == 0) 492 datadir_add(DEFAULT_DATADIR); 493 dev = argv[0]; 494 c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U); 495 switch (c) { 496 case FLAG_I: 497 if (strstr(cmdarg, ",") != NULL) 498 error = do_cpuid_count(cmdarg, dev); 499 else 500 error = do_cpuid(cmdarg, dev); 501 break; 502 case FLAG_M: 503 error = do_msr(cmdarg, dev); 504 break; 505 case FLAG_U: 506 error = do_update(dev); 507 break; 508 case FLAG_E: 509 error = do_eval_cpu_features(dev); 510 break; 511 default: 512 usage(); /* Only one command can be selected. */ 513 } 514 SLIST_FREE(&datadirs, next, free); 515 return (error == 0 ? 0 : 1); 516 } 517