1 /* $NetBSD: cpuctl.c,v 1.22 2013/01/31 19:47:59 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 2007, 2008, 2009, 2012 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.22 2013/01/31 19:47:59 matt 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 55 #include "cpuctl.h" 56 57 static u_int getcpuid(char **); 58 __dead static void usage(void); 59 60 static void cpu_identify(char **); 61 static void cpu_list(char **); 62 static void cpu_offline(char **); 63 static void cpu_online(char **); 64 static void cpu_intr(char **); 65 static void cpu_nointr(char **); 66 static void cpu_ucode(char **); 67 68 static struct cmdtab { 69 const char *label; 70 int takesargs; 71 int argsoptional; 72 void (*func)(char **); 73 } const cpu_cmdtab[] = { 74 { "identify", 1, 0, cpu_identify }, 75 { "list", 0, 0, cpu_list }, 76 { "offline", 1, 0, cpu_offline }, 77 { "online", 1, 0, cpu_online }, 78 { "intr", 1, 0, cpu_intr }, 79 { "nointr", 1, 0, cpu_nointr }, 80 { "ucode", 1, 1, cpu_ucode }, 81 { NULL, 0, 0, NULL }, 82 }; 83 84 static int fd; 85 86 int 87 main(int argc, char **argv) 88 { 89 const struct cmdtab *ct; 90 91 if (argc < 2) 92 usage(); 93 94 if ((fd = open(_PATH_CPUCTL, O_RDWR)) < 0) 95 err(EXIT_FAILURE, _PATH_CPUCTL); 96 97 for (ct = cpu_cmdtab; ct->label != NULL; ct++) { 98 if (strcmp(argv[1], ct->label) == 0) { 99 if (!ct->argsoptional && 100 ((ct->takesargs == 0) ^ (argv[2] == NULL))) 101 { 102 usage(); 103 } 104 (*ct->func)(argv + 2); 105 break; 106 } 107 } 108 109 if (ct->label == NULL) 110 errx(EXIT_FAILURE, "unknown command ``%s''", argv[optind]); 111 112 close(fd); 113 exit(EXIT_SUCCESS); 114 /* NOTREACHED */ 115 } 116 117 static void 118 usage(void) 119 { 120 const char *progname = getprogname(); 121 122 fprintf(stderr, "usage: %s identify cpuno\n", progname); 123 fprintf(stderr, " %s list\n", progname); 124 fprintf(stderr, " %s offline cpuno\n", progname); 125 fprintf(stderr, " %s online cpuno\n", progname); 126 fprintf(stderr, " %s intr cpuno\n", progname); 127 fprintf(stderr, " %s nointr cpuno\n", progname); 128 fprintf(stderr, " %s ucode [file]\n", progname); 129 exit(EXIT_FAILURE); 130 /* NOTREACHED */ 131 } 132 133 static void 134 cpu_online(char **argv) 135 { 136 cpustate_t cs; 137 138 cs.cs_id = getcpuid(argv); 139 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 140 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 141 cs.cs_online = true; 142 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) 143 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 144 } 145 146 static void 147 cpu_offline(char **argv) 148 { 149 cpustate_t cs; 150 151 cs.cs_id = getcpuid(argv); 152 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 153 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 154 cs.cs_online = false; 155 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) 156 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 157 } 158 159 static void 160 cpu_intr(char **argv) 161 { 162 cpustate_t cs; 163 164 cs.cs_id = getcpuid(argv); 165 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 166 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 167 cs.cs_intr = true; 168 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) 169 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 170 } 171 172 static void 173 cpu_nointr(char **argv) 174 { 175 cpustate_t cs; 176 177 cs.cs_id = getcpuid(argv); 178 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 179 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 180 cs.cs_intr = false; 181 if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) { 182 if (errno == EOPNOTSUPP) { 183 warnx("interrupt control not supported on " 184 "this platform"); 185 } else 186 err(EXIT_FAILURE, "IOC_CPU_SETSTATE"); 187 } 188 } 189 190 static void 191 cpu_ucode(char **argv) 192 { 193 int error; 194 struct cpu_ucode uc; 195 unsigned long id = 0; /* gcc */ 196 char *ep; 197 cpuset_t *cpuset; 198 199 uc.cpu_nr = -1; 200 if (argv[0] != NULL) { 201 id = strtoul(argv[0], &ep, 0); 202 if (id != ULONG_MAX && *ep == '\0') { 203 uc.cpu_nr = id; 204 argv++; 205 } 206 } 207 if (argv[0] != NULL) 208 strlcpy(uc.fwname, argv[0], sizeof(uc.fwname)); 209 else 210 memset(uc.fwname, '\0', sizeof(uc.fwname)); 211 212 error = ucodeupdate_check(fd, &uc); 213 if (error) 214 errx(EXIT_FAILURE, "unsupported"); 215 216 if (uc.cpu_nr == CPU_UCODE_CURRENT_CPU) { 217 cpuset = cpuset_create(); 218 if (cpuset == NULL) 219 err(EXIT_FAILURE, "cpuset_create"); 220 cpuset_zero(cpuset); 221 cpuset_set(id, cpuset); 222 if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) { 223 err(EXIT_FAILURE, "_sched_setaffinity"); 224 } 225 cpuset_destroy(cpuset); 226 } 227 error = ioctl(fd, IOC_CPU_UCODE_APPLY, &uc); 228 if (error < 0) { 229 if (uc.fwname[0]) 230 err(EXIT_FAILURE, "%s", uc.fwname); 231 else 232 err(EXIT_FAILURE, "IOC_CPU_UCODE_APPLY"); 233 } 234 } 235 236 237 static void 238 cpu_identify(char **argv) 239 { 240 char name[32]; 241 unsigned int id, np; 242 cpuset_t *cpuset; 243 244 np = sysconf(_SC_NPROCESSORS_CONF); 245 id = getcpuid(argv); 246 snprintf(name, sizeof(name), "cpu%u", id); 247 248 if (np != 1) { 249 cpuset = cpuset_create(); 250 if (cpuset == NULL) 251 err(EXIT_FAILURE, "cpuset_create"); 252 cpuset_zero(cpuset); 253 cpuset_set(id, cpuset); 254 if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) { 255 if (errno == EPERM) { 256 printf("Cannot bind to target CPU. Output " 257 "may not accurately describe the target.\n" 258 "Run as root to allow binding.\n\n"); 259 } else { 260 err(EXIT_FAILURE, "_sched_setaffinity"); 261 } 262 } 263 cpuset_destroy(cpuset); 264 } 265 identifycpu(fd, name); 266 } 267 268 static u_int 269 getcpuid(char **argv) 270 { 271 char *argp; 272 u_int id; 273 long np; 274 275 id = (u_int)strtoul(argv[0], &argp, 0); 276 if (*argp != '\0') 277 usage(); 278 279 np = sysconf(_SC_NPROCESSORS_CONF); 280 if (id >= (u_long)np) 281 errx(EXIT_FAILURE, "Invalid CPU number"); 282 283 return id; 284 } 285 286 static void 287 cpu_list(char **argv) 288 { 289 const char *state, *intr; 290 cpustate_t cs; 291 u_int cnt, i; 292 time_t lastmod; 293 char ibuf[16], *ts; 294 295 if (ioctl(fd, IOC_CPU_GETCOUNT, &cnt) < 0) 296 err(EXIT_FAILURE, "IOC_CPU_GETCOUNT"); 297 298 printf( 299 "Num HwId Unbound LWPs Interrupts Last change #Intr\n" 300 "---- ---- ------------ ---------- ------------------------ -----\n"); 301 302 for (i = 0; i < cnt; i++) { 303 cs.cs_id = i; 304 if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0) 305 err(EXIT_FAILURE, "IOC_CPU_GETSTATE"); 306 if (ioctl(fd, IOC_CPU_MAPID, &cs.cs_id) < 0) 307 err(EXIT_FAILURE, "IOC_CPU_MAPID"); 308 if (cs.cs_online) 309 state = "online"; 310 else 311 state = "offline"; 312 if (cs.cs_intr) 313 intr = "intr"; 314 else 315 intr = "nointr"; 316 if (cs.cs_intrcnt == 0) 317 strcpy(ibuf, "?"); 318 else 319 snprintf(ibuf, sizeof(ibuf), "%d", cs.cs_intrcnt - 1); 320 321 lastmod = (time_t)cs.cs_lastmod | 322 ((time_t)cs.cs_lastmodhi << 32); 323 ts = asctime(localtime(&lastmod)); 324 ts[strlen(ts) - 1] = '\0'; 325 printf("%-4d %-4x %-12s %-10s %s %-5s\n", 326 i, cs.cs_hwid, state, 327 intr, ts, ibuf); 328 } 329 } 330 331 int 332 aprint_normal(const char *fmt, ...) 333 { 334 va_list ap; 335 int rv; 336 337 va_start(ap, fmt); 338 rv = vfprintf(stdout, fmt, ap); 339 va_end(ap); 340 341 return rv; 342 } 343 __strong_alias(aprint_verbose,aprint_normal) 344 __strong_alias(aprint_error,aprint_normal) 345 346 int 347 aprint_normal_dev(const char *dev, const char *fmt, ...) 348 { 349 va_list ap; 350 int rv; 351 352 printf("%s: ", dev); 353 va_start(ap, fmt); 354 rv = vfprintf(stdout, fmt, ap); 355 va_end(ap); 356 357 return rv; 358 } 359 __strong_alias(aprint_verbose_dev,aprint_normal_dev) 360 __strong_alias(aprint_error_dev,aprint_normal_dev) 361