1 /* $NetBSD: cpuctl.c,v 1.29 2018/01/16 08:23:18 mrg Exp $ */ 2 3 /*- 4 * Copyright (c) 2007, 2008, 2009, 2012, 2015 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef lint 33 #include <sys/cdefs.h> 34 __RCSID("$NetBSD: cpuctl.c,v 1.29 2018/01/16 08:23:18 mrg Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/param.h> 38 #include <sys/ioctl.h> 39 #include <sys/uio.h> 40 #include <sys/cpuio.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <paths.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <stdarg.h> 49 #include <string.h> 50 #include <unistd.h> 51 #include <util.h> 52 #include <time.h> 53 #include <sched.h> 54 #include <stdbool.h> 55 56 #include "cpuctl.h" 57 58 static u_int getcpuid(char *); 59 __dead static void usage(void); 60 61 static void cpu_identify(char **); 62 static void cpu_list(char **); 63 static void cpu_offline(char **); 64 static void cpu_online(char **); 65 static void cpu_intr(char **); 66 static void cpu_nointr(char **); 67 static void cpu_ucode(char **); 68 69 static struct cmdtab { 70 const char *label; 71 bool takesargs; 72 bool argsoptional; 73 void (*func)(char **); 74 } const cpu_cmdtab[] = { 75 { "identify", true, false, cpu_identify }, 76 { "list", false, false, cpu_list }, 77 { "offline", true, false, cpu_offline }, 78 { "online", true, false, cpu_online }, 79 { "intr", true, false, cpu_intr }, 80 { "nointr", true, false, cpu_nointr }, 81 { "ucode", true, true, cpu_ucode }, 82 { NULL, false, false, NULL }, 83 }; 84 85 static int fd; 86 int verbose; 87 88 int 89 main(int argc, char **argv) 90 { 91 const struct cmdtab *ct; 92 int ch; 93 94 while ((ch = getopt(argc, argv, "v")) != -1) 95 switch (ch) { 96 case 'v': 97 verbose = 1; 98 break; 99 default: 100 usage(); 101 } 102 argc -= optind; 103 argv += optind; 104 if (argc < 1) 105 usage(); 106 107 if ((fd = open(_PATH_CPUCTL, O_RDWR)) < 0) 108 err(EXIT_FAILURE, _PATH_CPUCTL); 109 110 for (ct = cpu_cmdtab; ct->label != NULL; ct++) { 111 if (strcmp(argv[0], ct->label) == 0) { 112 if (!ct->argsoptional && 113 ((ct->takesargs == 0) ^ (argv[1] == NULL))) 114 { 115 usage(); 116 } 117 (*ct->func)(argv + 1); 118 break; 119 } 120 } 121 122 if (ct->label == NULL) 123 errx(EXIT_FAILURE, "unknown command ``%s''", argv[0]); 124 125 close(fd); 126 exit(EXIT_SUCCESS); 127 /* NOTREACHED */ 128 } 129 130 static void 131 usage(void) 132 { 133 const char *progname = getprogname(); 134 135 fprintf(stderr, "usage: %s identify cpuno\n", progname); 136 fprintf(stderr, " %s list\n", progname); 137 fprintf(stderr, " %s offline cpuno\n", progname); 138 fprintf(stderr, " %s online cpuno\n", progname); 139 fprintf(stderr, " %s intr cpuno\n", progname); 140 fprintf(stderr, " %s nointr cpuno\n", progname); 141 fprintf(stderr, " %s ucode [cpuno] [file]\n", progname); 142 exit(EXIT_FAILURE); 143 /* NOTREACHED */ 144 } 145 146 static void 147 cpu_online(char **argv) 148 { 149 cpustate_t cs; 150 151 for (; *argv; argv++) { 152 cs.cs_id = getcpuid(*argv); 153 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 154 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 155 cs.cs_online = true; 156 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) 157 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 158 } 159 } 160 161 static void 162 cpu_offline(char **argv) 163 { 164 cpustate_t cs; 165 166 for (; *argv; argv++) { 167 cs.cs_id = getcpuid(*argv); 168 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 169 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 170 cs.cs_online = false; 171 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) 172 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 173 } 174 } 175 176 static void 177 cpu_intr(char **argv) 178 { 179 cpustate_t cs; 180 181 for (; *argv; argv++) { 182 cs.cs_id = getcpuid(*argv); 183 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 184 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 185 cs.cs_intr = true; 186 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) 187 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 188 } 189 } 190 191 static void 192 cpu_nointr(char **argv) 193 { 194 cpustate_t cs; 195 196 for (; *argv; argv++) { 197 cs.cs_id = getcpuid(*argv); 198 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 199 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 200 cs.cs_intr = false; 201 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) { 202 if (errno == EOPNOTSUPP) { 203 warnx("interrupt control not supported on " 204 "this platform"); 205 } else 206 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 207 } 208 } 209 } 210 211 static void 212 cpu_ucode(char **argv) 213 { 214 int error; 215 struct cpu_ucode uc; 216 unsigned long id = 0; /* gcc */ 217 char *ep; 218 cpuset_t *cpuset; 219 220 uc.cpu_nr = -1; 221 if (argv[0] != NULL) { 222 id = strtoul(argv[0], &ep, 0); 223 if (id != ULONG_MAX && *ep == '\0') { 224 uc.cpu_nr = id; 225 argv++; 226 } 227 } 228 if (argv[0] != NULL) 229 strlcpy(uc.fwname, argv[0], sizeof(uc.fwname)); 230 else 231 memset(uc.fwname, '\0', sizeof(uc.fwname)); 232 233 error = ucodeupdate_check(fd, &uc); 234 if (error) 235 errx(EXIT_FAILURE, "unsupported"); 236 237 if (uc.cpu_nr == CPU_UCODE_CURRENT_CPU) { 238 cpuset = cpuset_create(); 239 if (cpuset == NULL) 240 err(EXIT_FAILURE, "cpuset_create"); 241 cpuset_zero(cpuset); 242 cpuset_set(id, cpuset); 243 if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) { 244 err(EXIT_FAILURE, "_sched_setaffinity"); 245 } 246 cpuset_destroy(cpuset); 247 } 248 error = ioctl(fd, IOC_CPU_UCODE_APPLY, &uc); 249 if (error < 0) { 250 if (uc.fwname[0]) 251 err(EXIT_FAILURE, "%s", uc.fwname); 252 else 253 err(EXIT_FAILURE, "IOC_CPU_UCODE_APPLY"); 254 } 255 } 256 257 static void 258 cpu_identify(char **argv) 259 { 260 char name[32]; 261 unsigned int id, np; 262 cpuset_t *cpuset; 263 264 np = sysconf(_SC_NPROCESSORS_CONF); 265 for (; *argv; argv++) { 266 id = getcpuid(*argv); 267 snprintf(name, sizeof(name), "cpu%u", id); 268 269 if (identifycpu_bind() && np != 1) { 270 cpuset = cpuset_create(); 271 if (cpuset == NULL) 272 err(EXIT_FAILURE, "cpuset_create"); 273 cpuset_zero(cpuset); 274 cpuset_set(id, cpuset); 275 if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) { 276 if (errno == EPERM) { 277 printf("Cannot bind to target CPU. Output " 278 "may not accurately describe the target.\n" 279 "Run as root to allow binding.\n\n"); 280 } else { 281 err(EXIT_FAILURE, "_sched_setaffinity"); 282 } 283 } 284 cpuset_destroy(cpuset); 285 } 286 identifycpu(fd, name); 287 } 288 } 289 290 static u_int 291 getcpuid(char *arg) 292 { 293 char *argp; 294 u_int id; 295 long np; 296 297 id = (u_int)strtoul(arg, &argp, 0); 298 if (*argp != '\0') 299 usage(); 300 301 np = sysconf(_SC_NPROCESSORS_CONF); 302 if (id >= (u_long)np) 303 errx(EXIT_FAILURE, "Invalid CPU number"); 304 305 return id; 306 } 307 308 static void 309 cpu_list(char **argv) 310 { 311 const char *state, *intr; 312 cpustate_t cs; 313 u_int cnt, i; 314 time_t lastmod; 315 char ibuf[16], *ts; 316 317 if (ioctl(fd, IOC_CPU_GETCOUNT, &cnt) < 0) 318 err(EXIT_FAILURE, "IOC_CPU_GETCOUNT"); 319 320 printf( 321 "Num HwId Unbound LWPs Interrupts Last change #Intr\n" 322 "---- ---- ------------ ---------- ------------------------ -----\n"); 323 324 for (i = 0; i < cnt; i++) { 325 cs.cs_id = i; 326 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 327 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 328 if (ioctl(fd, IOC_CPU_MAPID, &cs.cs_id) < 0) 329 err(EXIT_FAILURE, "IOC_CPU_MAPID"); 330 if (cs.cs_online) 331 state = "online"; 332 else 333 state = "offline"; 334 if (cs.cs_intr) 335 intr = "intr"; 336 else 337 intr = "nointr"; 338 if (cs.cs_intrcnt == 0) 339 strcpy(ibuf, "?"); 340 else 341 snprintf(ibuf, sizeof(ibuf), "%d", cs.cs_intrcnt - 1); 342 343 lastmod = (time_t)cs.cs_lastmod | 344 ((time_t)cs.cs_lastmodhi << 32); 345 ts = asctime(localtime(&lastmod)); 346 ts[strlen(ts) - 1] = '\0'; 347 printf("%-4d %-4x %-12s %-10s %s %-5s\n", 348 i, cs.cs_hwid, state, 349 intr, ts, ibuf); 350 } 351 } 352 353 int 354 aprint_normal(const char *fmt, ...) 355 { 356 va_list ap; 357 int rv; 358 359 va_start(ap, fmt); 360 rv = vfprintf(stdout, fmt, ap); 361 va_end(ap); 362 363 return rv; 364 } 365 __strong_alias(aprint_verbose,aprint_normal) 366 __strong_alias(aprint_error,aprint_normal) 367 368 int 369 aprint_normal_dev(const char *dev, const char *fmt, ...) 370 { 371 va_list ap; 372 int rv; 373 374 printf("%s: ", dev); 375 va_start(ap, fmt); 376 rv = vfprintf(stdout, fmt, ap); 377 va_end(ap); 378 379 return rv; 380 } 381 __strong_alias(aprint_verbose_dev,aprint_normal_dev) 382 __strong_alias(aprint_error_dev,aprint_normal_dev) 383