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