1e085f869SStanislav Sedov /*- 2e085f869SStanislav Sedov * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>. 3e085f869SStanislav Sedov * All rights reserved. 4e085f869SStanislav Sedov * 5e085f869SStanislav Sedov * Redistribution and use in source and binary forms, with or without 6e085f869SStanislav Sedov * modification, are permitted provided that the following conditions 7e085f869SStanislav Sedov * are met: 8e085f869SStanislav Sedov * 1. Redistributions of source code must retain the above copyright 9e085f869SStanislav Sedov * notice, this list of conditions and the following disclaimer. 10e085f869SStanislav Sedov * 2. Redistributions in binary form must reproduce the above copyright 11e085f869SStanislav Sedov * notice, this list of conditions and the following disclaimer in the 12e085f869SStanislav Sedov * documentation and/or other materials provided with the distribution. 13e085f869SStanislav Sedov * 14e085f869SStanislav Sedov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15e085f869SStanislav Sedov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16e085f869SStanislav Sedov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17e085f869SStanislav Sedov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18e085f869SStanislav Sedov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19e085f869SStanislav Sedov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20e085f869SStanislav Sedov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21e085f869SStanislav Sedov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22e085f869SStanislav Sedov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23e085f869SStanislav Sedov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24e085f869SStanislav Sedov */ 25e085f869SStanislav Sedov 26e085f869SStanislav Sedov /* 27e085f869SStanislav Sedov * This utility provides userland access to the cpuctl(4) pseudo-device 28e085f869SStanislav Sedov * features. 29e085f869SStanislav Sedov */ 30e085f869SStanislav Sedov 31e085f869SStanislav Sedov #include <sys/cdefs.h> 32e085f869SStanislav Sedov __FBSDID("$FreeBSD$"); 33e085f869SStanislav Sedov 34e085f869SStanislav Sedov #include <assert.h> 35e085f869SStanislav Sedov #include <stdio.h> 36e085f869SStanislav Sedov #include <stdlib.h> 37e085f869SStanislav Sedov #include <string.h> 38e085f869SStanislav Sedov #include <unistd.h> 39e085f869SStanislav Sedov #include <fcntl.h> 40e085f869SStanislav Sedov #include <err.h> 41e085f869SStanislav Sedov #include <sysexits.h> 42e085f869SStanislav Sedov #include <dirent.h> 43e085f869SStanislav Sedov 44e085f869SStanislav Sedov #include <sys/queue.h> 45e085f869SStanislav Sedov #include <sys/param.h> 46e085f869SStanislav Sedov #include <sys/types.h> 47e085f869SStanislav Sedov #include <sys/stat.h> 48e085f869SStanislav Sedov #include <sys/ioctl.h> 49e085f869SStanislav Sedov #include <sys/cpuctl.h> 50e085f869SStanislav Sedov 51e085f869SStanislav Sedov #include "cpucontrol.h" 52e085f869SStanislav Sedov #include "amd.h" 53e085f869SStanislav Sedov #include "intel.h" 54e085f869SStanislav Sedov 55e085f869SStanislav Sedov int verbosity_level = 0; 56e085f869SStanislav Sedov 57e085f869SStanislav Sedov #define DEFAULT_DATADIR "/usr/local/share/cpucontrol" 58e085f869SStanislav Sedov 59e085f869SStanislav Sedov #define FLAG_I 0x01 60e085f869SStanislav Sedov #define FLAG_M 0x02 61e085f869SStanislav Sedov #define FLAG_U 0x04 62e085f869SStanislav Sedov 63e085f869SStanislav Sedov #define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff) 64e085f869SStanislav Sedov #define LOW(val) (uint32_t)((val) & 0xffffffff) 65e085f869SStanislav Sedov 66e085f869SStanislav Sedov /* 67e085f869SStanislav Sedov * Macros for freeing SLISTs, probably must be in /sys/queue.h 68e085f869SStanislav Sedov */ 69e085f869SStanislav Sedov #define SLIST_FREE(head, field, freef) do { \ 70e085f869SStanislav Sedov typeof(SLIST_FIRST(head)) __elm0; \ 71e085f869SStanislav Sedov typeof(SLIST_FIRST(head)) __elm; \ 72e085f869SStanislav Sedov SLIST_FOREACH_SAFE(__elm, (head), field, __elm0) \ 73e085f869SStanislav Sedov (void)(freef)(__elm); \ 74e085f869SStanislav Sedov } while(0); 75e085f869SStanislav Sedov 76e085f869SStanislav Sedov struct datadir { 77e085f869SStanislav Sedov const char *path; 78e085f869SStanislav Sedov SLIST_ENTRY(datadir) next; 79e085f869SStanislav Sedov }; 80e085f869SStanislav Sedov static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(&datadirs); 81e085f869SStanislav Sedov 82e085f869SStanislav Sedov struct ucode_handler { 83e085f869SStanislav Sedov ucode_probe_t *probe; 84e085f869SStanislav Sedov ucode_update_t *update; 85e085f869SStanislav Sedov } handlers[] = { 86e085f869SStanislav Sedov { intel_probe, intel_update }, 87e085f869SStanislav Sedov { amd_probe, amd_update }, 88e085f869SStanislav Sedov }; 89e085f869SStanislav Sedov #define NHANDLERS (sizeof(handlers) / sizeof(*handlers)) 90e085f869SStanislav Sedov 91e085f869SStanislav Sedov static void usage(void); 92e085f869SStanislav Sedov static int isdir(const char *path); 93e085f869SStanislav Sedov static int do_cpuid(const char *cmdarg, const char *dev); 94e085f869SStanislav Sedov static int do_msr(const char *cmdarg, const char *dev); 95e085f869SStanislav Sedov static int do_update(const char *dev); 96e085f869SStanislav Sedov static void datadir_add(const char *path); 97e085f869SStanislav Sedov 98e085f869SStanislav Sedov static void __dead2 99e085f869SStanislav Sedov usage() 100e085f869SStanislav Sedov { 101e085f869SStanislav Sedov const char *name; 102e085f869SStanislav Sedov 103e085f869SStanislav Sedov name = getprogname(); 104e085f869SStanislav Sedov if (name == NULL) 105e085f869SStanislav Sedov name = "cpuctl"; 106e085f869SStanislav Sedov fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | " 107e085f869SStanislav Sedov "-i level | -u] device\n", name); 108e085f869SStanislav Sedov exit(EX_USAGE); 109e085f869SStanislav Sedov } 110e085f869SStanislav Sedov 111e085f869SStanislav Sedov static int 112e085f869SStanislav Sedov isdir(const char *path) 113e085f869SStanislav Sedov { 114e085f869SStanislav Sedov int error; 115e085f869SStanislav Sedov struct stat st; 116e085f869SStanislav Sedov 117e085f869SStanislav Sedov error = stat(path, &st); 118e085f869SStanislav Sedov if (error < 0) { 119e085f869SStanislav Sedov WARN(0, "stat(%s)", path); 120e085f869SStanislav Sedov return (error); 121e085f869SStanislav Sedov } 122e085f869SStanislav Sedov return (st.st_mode & S_IFDIR); 123e085f869SStanislav Sedov } 124e085f869SStanislav Sedov 125e085f869SStanislav Sedov static int 126e085f869SStanislav Sedov do_cpuid(const char *cmdarg, const char *dev) 127e085f869SStanislav Sedov { 128e085f869SStanislav Sedov unsigned int level; 129e085f869SStanislav Sedov cpuctl_cpuid_args_t args; 130e085f869SStanislav Sedov int fd, error; 131e085f869SStanislav Sedov char *endptr; 132e085f869SStanislav Sedov 133e085f869SStanislav Sedov assert(cmdarg != NULL); 134e085f869SStanislav Sedov assert(dev != NULL); 135e085f869SStanislav Sedov 136e085f869SStanislav Sedov level = strtoul(cmdarg, &endptr, 16); 137e085f869SStanislav Sedov if (*cmdarg == '\0' || *endptr != '\0') { 138e085f869SStanislav Sedov WARNX(0, "incorrect operand: %s", cmdarg); 139e085f869SStanislav Sedov usage(); 140e085f869SStanislav Sedov /* NOTREACHED */ 141e085f869SStanislav Sedov } 142e085f869SStanislav Sedov 143e085f869SStanislav Sedov /* 144e085f869SStanislav Sedov * Fill ioctl argument structure. 145e085f869SStanislav Sedov */ 146e085f869SStanislav Sedov args.level = level; 147e085f869SStanislav Sedov fd = open(dev, O_RDONLY); 148e085f869SStanislav Sedov if (fd < 0) { 149e085f869SStanislav Sedov WARNX(0, "error opening %s for reading", dev); 150e085f869SStanislav Sedov return (1); 151e085f869SStanislav Sedov } 152e085f869SStanislav Sedov error = ioctl(fd, CPUCTL_CPUID, &args); 153e085f869SStanislav Sedov if (error < 0) { 154e085f869SStanislav Sedov WARNX(0, "ioctl(%s, CPUCTL_CPUID)", dev); 155e085f869SStanislav Sedov close(fd); 156e085f869SStanislav Sedov return (error); 157e085f869SStanislav Sedov } 158e085f869SStanislav Sedov fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", 159e085f869SStanislav Sedov level, args.data[0], args.data[1], args.data[2], args.data[3]); 160e085f869SStanislav Sedov close(fd); 161e085f869SStanislav Sedov return (0); 162e085f869SStanislav Sedov } 163e085f869SStanislav Sedov 164e085f869SStanislav Sedov static int 165e085f869SStanislav Sedov do_msr(const char *cmdarg, const char *dev) 166e085f869SStanislav Sedov { 167e085f869SStanislav Sedov unsigned int msr; 168e085f869SStanislav Sedov cpuctl_msr_args_t args; 169e085f869SStanislav Sedov int fd, error; 170e085f869SStanislav Sedov int wr = 0; 171e085f869SStanislav Sedov char *p; 172e085f869SStanislav Sedov char *endptr; 173e085f869SStanislav Sedov 174e085f869SStanislav Sedov assert(cmdarg != NULL); 175e085f869SStanislav Sedov assert(dev != NULL); 176e085f869SStanislav Sedov 177e085f869SStanislav Sedov p = strchr(cmdarg, '='); 178e085f869SStanislav Sedov if (p != NULL) { 179e085f869SStanislav Sedov wr = 1; 180e085f869SStanislav Sedov *p++ = '\0'; 181e085f869SStanislav Sedov args.data = strtoull(p, &endptr, 16); 182e085f869SStanislav Sedov if (*p == '\0' || *endptr != '\0') { 183e085f869SStanislav Sedov WARNX(0, "incorrect MSR value: %s", p); 184e085f869SStanislav Sedov usage(); 185e085f869SStanislav Sedov /* NOTREACHED */ 186e085f869SStanislav Sedov } 187e085f869SStanislav Sedov } 188e085f869SStanislav Sedov msr = strtoul(cmdarg, &endptr, 16); 189e085f869SStanislav Sedov if (*cmdarg == '\0' || *endptr != '\0') { 190e085f869SStanislav Sedov WARNX(0, "incorrect MSR register: %s", cmdarg); 191e085f869SStanislav Sedov usage(); 192e085f869SStanislav Sedov /* NOTREACHED */ 193e085f869SStanislav Sedov } 194e085f869SStanislav Sedov 195e085f869SStanislav Sedov /* 196e085f869SStanislav Sedov * Fill ioctl argument structure. 197e085f869SStanislav Sedov */ 198e085f869SStanislav Sedov args.msr = msr; 199e085f869SStanislav Sedov fd = open(dev, wr == 0 ? O_RDONLY : O_WRONLY); 200e085f869SStanislav Sedov if (fd < 0) { 201e085f869SStanislav Sedov WARNX(0, "error opening %s for %s", dev, 202e085f869SStanislav Sedov wr == 0 ? "reading" : "writing"); 203e085f869SStanislav Sedov return (1); 204e085f869SStanislav Sedov } 205e085f869SStanislav Sedov error = ioctl(fd, wr == 0 ? CPUCTL_RDMSR : CPUCTL_WRMSR, &args); 206e085f869SStanislav Sedov if (error < 0) { 207e085f869SStanislav Sedov WARNX(0, "ioctl(%s, %s)", dev, 208e085f869SStanislav Sedov wr == 0 ? "CPUCTL_RDMSR" : "CPUCTL_WRMSR"); 209e085f869SStanislav Sedov close(fd); 210e085f869SStanislav Sedov return (1); 211e085f869SStanislav Sedov } 212e085f869SStanislav Sedov if (wr == 0) 213e085f869SStanislav Sedov fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr, 214e085f869SStanislav Sedov HIGH(args.data), LOW(args.data)); 215e085f869SStanislav Sedov close(fd); 216e085f869SStanislav Sedov return (0); 217e085f869SStanislav Sedov } 218e085f869SStanislav Sedov 219e085f869SStanislav Sedov static int 220e085f869SStanislav Sedov do_update(const char *dev) 221e085f869SStanislav Sedov { 222e085f869SStanislav Sedov int fd; 223e085f869SStanislav Sedov unsigned int i; 224e085f869SStanislav Sedov int error; 225e085f869SStanislav Sedov struct ucode_handler *handler; 226e085f869SStanislav Sedov struct datadir *dir; 227e085f869SStanislav Sedov DIR *dirfd; 228e085f869SStanislav Sedov struct dirent *direntry; 229e085f869SStanislav Sedov char buf[MAXPATHLEN]; 230e085f869SStanislav Sedov 231e085f869SStanislav Sedov fd = open(dev, O_RDONLY); 232e085f869SStanislav Sedov if (fd < 0) { 233e085f869SStanislav Sedov WARNX(0, "error opening %s for reading", dev); 234e085f869SStanislav Sedov return (1); 235e085f869SStanislav Sedov } 236e085f869SStanislav Sedov 237e085f869SStanislav Sedov /* 238e085f869SStanislav Sedov * Find the appropriate handler for device. 239e085f869SStanislav Sedov */ 240e085f869SStanislav Sedov for (i = 0; i < NHANDLERS; i++) 241e085f869SStanislav Sedov if (handlers[i].probe(fd) == 0) 242e085f869SStanislav Sedov break; 243e085f869SStanislav Sedov if (i < NHANDLERS) 244e085f869SStanislav Sedov handler = &handlers[i]; 245e085f869SStanislav Sedov else { 246e085f869SStanislav Sedov WARNX(0, "cannot find the appropriate handler for device"); 247e085f869SStanislav Sedov close(fd); 248e085f869SStanislav Sedov return (1); 249e085f869SStanislav Sedov } 250e085f869SStanislav Sedov close(fd); 251e085f869SStanislav Sedov 252e085f869SStanislav Sedov /* 253e085f869SStanislav Sedov * Process every image in specified data directories. 254e085f869SStanislav Sedov */ 255e085f869SStanislav Sedov SLIST_FOREACH(dir, &datadirs, next) { 256e085f869SStanislav Sedov dirfd = opendir(dir->path); 257e085f869SStanislav Sedov if (dirfd == NULL) { 258e085f869SStanislav Sedov WARNX(1, "skipping directory %s: not accessible", dir->path); 259e085f869SStanislav Sedov continue; 260e085f869SStanislav Sedov } 261e085f869SStanislav Sedov while ((direntry = readdir(dirfd)) != NULL) { 262e085f869SStanislav Sedov if (direntry->d_namlen == 0) 263e085f869SStanislav Sedov continue; 264e085f869SStanislav Sedov error = snprintf(buf, sizeof(buf), "%s/%s", dir->path, 265e085f869SStanislav Sedov direntry->d_name); 266e085f869SStanislav Sedov if ((unsigned)error >= sizeof(buf)) 267e085f869SStanislav Sedov WARNX(0, "skipping %s, buffer too short", 268e085f869SStanislav Sedov direntry->d_name); 269e085f869SStanislav Sedov if (isdir(buf) != 0) { 270e085f869SStanislav Sedov WARNX(2, "skipping %s: is a directory", buf); 271e085f869SStanislav Sedov continue; 272e085f869SStanislav Sedov } 273e085f869SStanislav Sedov handler->update(dev, buf); 274e085f869SStanislav Sedov } 275e085f869SStanislav Sedov error = closedir(dirfd); 276e085f869SStanislav Sedov if (error != 0) 277e085f869SStanislav Sedov WARN(0, "closedir(%s)", dir->path); 278e085f869SStanislav Sedov } 279e085f869SStanislav Sedov return (0); 280e085f869SStanislav Sedov } 281e085f869SStanislav Sedov 282e085f869SStanislav Sedov /* 283e085f869SStanislav Sedov * Add new data directory to the search list. 284e085f869SStanislav Sedov */ 285e085f869SStanislav Sedov static void 286e085f869SStanislav Sedov datadir_add(const char *path) 287e085f869SStanislav Sedov { 288e085f869SStanislav Sedov struct datadir *newdir; 289e085f869SStanislav Sedov 290e085f869SStanislav Sedov newdir = (struct datadir *)malloc(sizeof(*newdir)); 291e085f869SStanislav Sedov if (newdir == NULL) 292e085f869SStanislav Sedov err(EX_OSERR, "cannot allocate memory"); 293e085f869SStanislav Sedov newdir->path = path; 294e085f869SStanislav Sedov SLIST_INSERT_HEAD(&datadirs, newdir, next); 295e085f869SStanislav Sedov } 296e085f869SStanislav Sedov 297e085f869SStanislav Sedov int 298e085f869SStanislav Sedov main(int argc, char *argv[]) 299e085f869SStanislav Sedov { 300e085f869SStanislav Sedov int c, flags; 301e085f869SStanislav Sedov const char *cmdarg; 302e085f869SStanislav Sedov const char *dev; 303e085f869SStanislav Sedov int error; 304e085f869SStanislav Sedov 305e085f869SStanislav Sedov flags = 0; 306e085f869SStanislav Sedov error = 0; 307e085f869SStanislav Sedov cmdarg = ""; /* To keep gcc3 happy. */ 308e085f869SStanislav Sedov 309e085f869SStanislav Sedov /* 310e085f869SStanislav Sedov * Add all default data dirs to the list first. 311e085f869SStanislav Sedov */ 312e085f869SStanislav Sedov datadir_add(DEFAULT_DATADIR); 313e085f869SStanislav Sedov while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) { 314e085f869SStanislav Sedov switch (c) { 315e085f869SStanislav Sedov case 'd': 316e085f869SStanislav Sedov datadir_add(optarg); 317e085f869SStanislav Sedov break; 318e085f869SStanislav Sedov case 'i': 319e085f869SStanislav Sedov flags |= FLAG_I; 320e085f869SStanislav Sedov cmdarg = optarg; 321e085f869SStanislav Sedov break; 322e085f869SStanislav Sedov case 'm': 323e085f869SStanislav Sedov flags |= FLAG_M; 324e085f869SStanislav Sedov cmdarg = optarg; 325e085f869SStanislav Sedov break; 326e085f869SStanislav Sedov case 'u': 327e085f869SStanislav Sedov flags |= FLAG_U; 328e085f869SStanislav Sedov break; 329e085f869SStanislav Sedov case 'v': 330e085f869SStanislav Sedov verbosity_level++; 331e085f869SStanislav Sedov break; 332e085f869SStanislav Sedov case 'h': 333e085f869SStanislav Sedov /* FALLTHROUGH */ 334e085f869SStanislav Sedov default: 335e085f869SStanislav Sedov usage(); 336e085f869SStanislav Sedov /* NOTREACHED */ 337e085f869SStanislav Sedov } 338e085f869SStanislav Sedov } 339e085f869SStanislav Sedov argc -= optind; 340e085f869SStanislav Sedov argv += optind; 341e085f869SStanislav Sedov if (argc < 1) { 342e085f869SStanislav Sedov usage(); 343e085f869SStanislav Sedov /* NOTREACHED */ 344e085f869SStanislav Sedov } 345e085f869SStanislav Sedov dev = argv[0]; 346e085f869SStanislav Sedov c = flags & (FLAG_I | FLAG_M | FLAG_U); 347e085f869SStanislav Sedov switch (c) { 348e085f869SStanislav Sedov case FLAG_I: 349e085f869SStanislav Sedov error = do_cpuid(cmdarg, dev); 350e085f869SStanislav Sedov break; 351e085f869SStanislav Sedov case FLAG_M: 352e085f869SStanislav Sedov error = do_msr(cmdarg, dev); 353e085f869SStanislav Sedov break; 354e085f869SStanislav Sedov case FLAG_U: 355e085f869SStanislav Sedov error = do_update(dev); 356e085f869SStanislav Sedov break; 357e085f869SStanislav Sedov default: 358e085f869SStanislav Sedov usage(); /* Only one command can be selected. */ 359e085f869SStanislav Sedov } 360e085f869SStanislav Sedov SLIST_FREE(&datadirs, next, free); 361e085f869SStanislav Sedov return (error); 362e085f869SStanislav Sedov } 363