1 /* $NetBSD: cpuctl.c,v 1.31 2020/04/21 02:56:37 msaitoh 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.31 2020/04/21 02:56:37 msaitoh 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 if (cpuset_set(id, cpuset) < 0) 243 err(EXIT_FAILURE, "cpuset_set"); 244 if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) { 245 err(EXIT_FAILURE, "_sched_setaffinity"); 246 } 247 cpuset_destroy(cpuset); 248 } 249 error = ioctl(fd, IOC_CPU_UCODE_APPLY, &uc); 250 if (error < 0) { 251 if (uc.fwname[0]) 252 err(EXIT_FAILURE, "%s", uc.fwname); 253 else 254 err(EXIT_FAILURE, "IOC_CPU_UCODE_APPLY"); 255 } 256 } 257 258 static void 259 cpu_identify(char **argv) 260 { 261 char name[32]; 262 unsigned int id, np; 263 cpuset_t *cpuset; 264 265 np = sysconf(_SC_NPROCESSORS_CONF); 266 for (; *argv; argv++) { 267 id = getcpuid(*argv); 268 snprintf(name, sizeof(name), "cpu%u", id); 269 270 if (identifycpu_bind() && np != 1) { 271 cpuset = cpuset_create(); 272 if (cpuset == NULL) 273 err(EXIT_FAILURE, "cpuset_create"); 274 cpuset_zero(cpuset); 275 if (cpuset_set(id, cpuset) < 0) 276 err(EXIT_FAILURE, "cpuset_set"); 277 if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) { 278 if (errno == EPERM) { 279 printf("Cannot bind to target CPU. Output " 280 "may not accurately describe the target.\n" 281 "Run as root to allow binding.\n\n"); 282 } else { 283 err(EXIT_FAILURE, "_sched_setaffinity"); 284 } 285 } 286 cpuset_destroy(cpuset); 287 } 288 identifycpu(fd, name); 289 } 290 } 291 292 static u_int 293 getcpuid(char *arg) 294 { 295 char *argp; 296 u_int id; 297 long np; 298 299 id = (u_int)strtoul(arg, &argp, 0); 300 if (*argp != '\0') 301 usage(); 302 303 np = sysconf(_SC_NPROCESSORS_CONF); 304 if (id >= (u_long)np) 305 errx(EXIT_FAILURE, "Invalid CPU number"); 306 307 return id; 308 } 309 310 static void 311 cpu_list(char **argv) 312 { 313 const char *state, *intr; 314 cpustate_t cs; 315 u_int cnt, i; 316 time_t lastmod; 317 char ibuf[16], *ts; 318 319 if (ioctl(fd, IOC_CPU_GETCOUNT, &cnt) < 0) 320 err(EXIT_FAILURE, "IOC_CPU_GETCOUNT"); 321 322 printf( 323 "Num HwId Unbound LWPs Interrupts Last change #Intr\n" 324 "---- ---- ------------ ---------- ------------------------ -----\n"); 325 326 for (i = 0; i < cnt; i++) { 327 cs.cs_id = i; 328 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 329 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 330 if (ioctl(fd, IOC_CPU_MAPID, &cs.cs_id) < 0) 331 err(EXIT_FAILURE, "IOC_CPU_MAPID"); 332 if (cs.cs_online) 333 state = "online"; 334 else 335 state = "offline"; 336 if (cs.cs_intr) 337 intr = "intr"; 338 else 339 intr = "nointr"; 340 if (cs.cs_intrcnt == 0) 341 strcpy(ibuf, "?"); 342 else 343 snprintf(ibuf, sizeof(ibuf), "%d", cs.cs_intrcnt - 1); 344 345 lastmod = (time_t)cs.cs_lastmod | 346 ((time_t)cs.cs_lastmodhi << 32); 347 ts = asctime(localtime(&lastmod)); 348 ts[strlen(ts) - 1] = '\0'; 349 printf("%-4d %-4x %-12s %-10s %s %-5s\n", 350 i, cs.cs_hwid, state, 351 intr, ts, ibuf); 352 } 353 } 354 355 int 356 aprint_normal(const char *fmt, ...) 357 { 358 va_list ap; 359 int rv; 360 361 va_start(ap, fmt); 362 rv = vfprintf(stdout, fmt, ap); 363 va_end(ap); 364 365 return rv; 366 } 367 __strong_alias(aprint_verbose,aprint_normal) 368 __strong_alias(aprint_error,aprint_normal) 369 370 int 371 aprint_normal_dev(const char *dev, const char *fmt, ...) 372 { 373 va_list ap; 374 int rv; 375 376 printf("%s: ", dev); 377 va_start(ap, fmt); 378 rv = vfprintf(stdout, fmt, ap); 379 va_end(ap); 380 381 return rv; 382 } 383 __strong_alias(aprint_verbose_dev,aprint_normal_dev) 384 __strong_alias(aprint_error_dev,aprint_normal_dev) 385 __strong_alias(aprint_debug_dev,aprint_normal_dev) 386