]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/cpucontrol/cpucontrol.c
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / usr.sbin / cpucontrol / cpucontrol.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 /*
29  * This utility provides userland access to the cpuctl(4) pseudo-device
30  * features.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <assert.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <dirent.h>
40 #include <fcntl.h>
41 #include <paths.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <sysexits.h>
47
48 #include <sys/queue.h>
49 #include <sys/param.h>
50 #include <sys/types.h>
51 #include <sys/mman.h>
52 #include <sys/stat.h>
53 #include <sys/ioctl.h>
54 #include <sys/cpuctl.h>
55
56 #include "cpucontrol.h"
57 #include "amd.h"
58 #include "intel.h"
59 #include "via.h"
60
61 int     verbosity_level = 0;
62
63 #define DEFAULT_DATADIR _PATH_LOCALBASE "/share/cpucontrol"
64
65 #define FLAG_I  0x01
66 #define FLAG_M  0x02
67 #define FLAG_U  0x04
68 #define FLAG_N  0x08
69 #define FLAG_E  0x10
70
71 #define OP_INVAL        0x00
72 #define OP_READ         0x01
73 #define OP_WRITE        0x02
74 #define OP_OR           0x04
75 #define OP_AND          0x08
76
77 #define HIGH(val)       (uint32_t)(((val) >> 32) & 0xffffffff)
78 #define LOW(val)        (uint32_t)((val) & 0xffffffff)
79
80 struct datadir {
81         const char              *path;
82         SLIST_ENTRY(datadir)    next;
83 };
84 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
85
86 static struct ucode_handler {
87         ucode_probe_t *probe;
88         ucode_update_t *update;
89 } handlers[] = {
90         { intel_probe, intel_update },
91         { amd10h_probe, amd10h_update },
92         { amd_probe, amd_update },
93         { via_probe, via_update },
94 };
95 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
96
97 static void     usage(void);
98 static int      do_cpuid(const char *cmdarg, const char *dev);
99 static int      do_cpuid_count(const char *cmdarg, const char *dev);
100 static int      do_msr(const char *cmdarg, const char *dev);
101 static int      do_update(const char *dev);
102 static void     datadir_add(const char *path);
103
104 static void __dead2
105 usage(void)
106 {
107         const char *name;
108
109         name = getprogname();
110         if (name == NULL)
111                 name = "cpuctl";
112         fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
113             "-i level | -i level,level_type | -e | -u] device\n", name);
114         exit(EX_USAGE);
115 }
116
117 static int
118 do_cpuid(const char *cmdarg, const char *dev)
119 {
120         unsigned int level;
121         cpuctl_cpuid_args_t args;
122         int fd, error;
123         char *endptr;
124
125         assert(cmdarg != NULL);
126         assert(dev != NULL);
127
128         level = strtoul(cmdarg, &endptr, 16);
129         if (*cmdarg == '\0' || *endptr != '\0') {
130                 WARNX(0, "incorrect operand: %s", cmdarg);
131                 usage();
132                 /* NOTREACHED */
133         }
134
135         /*
136          * Fill ioctl argument structure.
137          */
138         args.level = level;
139         fd = open(dev, O_RDONLY);
140         if (fd < 0) {
141                 WARN(0, "error opening %s for reading", dev);
142                 return (1);
143         }
144         error = ioctl(fd, CPUCTL_CPUID, &args);
145         if (error < 0) {
146                 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
147                 close(fd);
148                 return (error);
149         }
150         fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
151             level, args.data[0], args.data[1], args.data[2], args.data[3]);
152         close(fd);
153         return (0);
154 }
155
156 static int
157 do_cpuid_count(const char *cmdarg, const char *dev)
158 {
159         char *cmdarg1, *endptr, *endptr1;
160         unsigned int level, level_type;
161         cpuctl_cpuid_count_args_t args;
162         int fd, error;
163
164         assert(cmdarg != NULL);
165         assert(dev != NULL);
166
167         level = strtoul(cmdarg, &endptr, 16);
168         if (*cmdarg == '\0' || *endptr == '\0') {
169                 WARNX(0, "incorrect or missing operand: %s", cmdarg);
170                 usage();
171                 /* NOTREACHED */
172         }
173         /* Locate the comma... */
174         cmdarg1 = strstr(endptr, ",");
175         /* ... and skip past it */
176         cmdarg1 += 1;
177         level_type = strtoul(cmdarg1, &endptr1, 16);
178         if (*cmdarg1 == '\0' || *endptr1 != '\0') {
179                 WARNX(0, "incorrect or missing operand: %s", cmdarg);
180                 usage();
181                 /* NOTREACHED */
182         }
183
184         /*
185          * Fill ioctl argument structure.
186          */
187         args.level = level;
188         args.level_type = level_type;
189         fd = open(dev, O_RDONLY);
190         if (fd < 0) {
191                 WARN(0, "error opening %s for reading", dev);
192                 return (1);
193         }
194         error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
195         if (error < 0) {
196                 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
197                 close(fd);
198                 return (error);
199         }
200         fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
201             "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
202             args.data[2], args.data[3]);
203         close(fd);
204         return (0);
205 }
206
207 static int
208 do_msr(const char *cmdarg, const char *dev)
209 {
210         unsigned int msr;
211         cpuctl_msr_args_t args;
212         size_t len;
213         uint64_t data = 0;
214         unsigned long command;
215         int do_invert = 0, op;
216         int fd, error;
217         const char *command_name;
218         char *endptr;
219         char *p;
220
221         assert(cmdarg != NULL);
222         assert(dev != NULL);
223         len = strlen(cmdarg);
224         if (len == 0) {
225                 WARNX(0, "MSR register expected");
226                 usage();
227                 /* NOTREACHED */
228         }
229
230         /*
231          * Parse command string.
232          */
233         msr = strtoul(cmdarg, &endptr, 16);
234         switch (*endptr) {
235         case '\0':
236                 op = OP_READ;
237                 break;
238         case '=':
239                 op = OP_WRITE;
240                 break;
241         case '&':
242                 op = OP_AND;
243                 endptr++;
244                 break;
245         case '|':
246                 op = OP_OR;
247                 endptr++;
248                 break;
249         default:
250                 op = OP_INVAL;
251         }
252         if (op != OP_READ) {    /* Complex operation. */
253                 if (*endptr != '=')
254                         op = OP_INVAL;
255                 else {
256                         p = ++endptr;
257                         if (*p == '~') {
258                                 do_invert = 1;
259                                 p++;
260                         }
261                         data = strtoull(p, &endptr, 16);
262                         if (*p == '\0' || *endptr != '\0') {
263                                 WARNX(0, "argument required: %s", cmdarg);
264                                 usage();
265                                 /* NOTREACHED */
266                         }
267                 }
268         }
269         if (op == OP_INVAL) {
270                 WARNX(0, "invalid operator: %s", cmdarg);
271                 usage();
272                 /* NOTREACHED */
273         }
274
275         /*
276          * Fill ioctl argument structure.
277          */
278         args.msr = msr;
279         if ((do_invert != 0) ^ (op == OP_AND))
280                 args.data = ~data;
281         else
282                 args.data = data;
283         switch (op) {
284         case OP_READ:
285                 command = CPUCTL_RDMSR;
286                 command_name = "RDMSR";
287                 break;
288         case OP_WRITE:
289                 command = CPUCTL_WRMSR;
290                 command_name = "WRMSR";
291                 break;
292         case OP_OR:
293                 command = CPUCTL_MSRSBIT;
294                 command_name = "MSRSBIT";
295                 break;
296         case OP_AND:
297                 command = CPUCTL_MSRCBIT;
298                 command_name = "MSRCBIT";
299                 break;
300         default:
301                 abort();
302         }
303         fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
304         if (fd < 0) {
305                 WARN(0, "error opening %s for %s", dev,
306                     op == OP_READ ? "reading" : "writing");
307                 return (1);
308         }
309         error = ioctl(fd, command, &args);
310         if (error < 0) {
311                 WARN(0, "ioctl(%s, CPUCTL_%s (%#x))", dev, command_name, msr);
312                 close(fd);
313                 return (1);
314         }
315         if (op == OP_READ)
316                 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
317                     HIGH(args.data), LOW(args.data));
318         close(fd);
319         return (0);
320 }
321
322 static int
323 do_eval_cpu_features(const char *dev)
324 {
325         int fd, error;
326
327         assert(dev != NULL);
328
329         fd = open(dev, O_RDWR);
330         if (fd < 0) {
331                 WARN(0, "error opening %s for writing", dev);
332                 return (1);
333         }
334         error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
335         if (error < 0)
336                 WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
337         close(fd);
338         return (error);
339 }
340
341 static int
342 try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
343     const char *fname, struct ucode_handler *handler)
344 {
345         struct ucode_update_params parm;
346         struct stat st;
347         char *fw_path;
348         void *fw_map;
349         int fwfd, rc;
350
351         rc = 0;
352         fw_path = NULL;
353         fw_map = MAP_FAILED;
354         fwfd = openat(fwdfd, fname, O_RDONLY);
355         if (fwfd < 0) {
356                 WARN(0, "openat(%s, %s)", dpath, fname);
357                 goto out;
358         }
359
360         rc = asprintf(&fw_path, "%s/%s", dpath, fname);
361         if (rc == -1) {
362                 WARNX(0, "out of memory");
363                 rc = ENOMEM;
364                 goto out;
365         }
366
367         rc = fstat(fwfd, &st);
368         if (rc != 0) {
369                 WARN(0, "fstat(%s)", fw_path);
370                 rc = 0;
371                 goto out;
372         }
373         if (!S_ISREG(st.st_mode))
374                 goto out;
375         if (st.st_size <= 0) {
376                 WARN(0, "%s: empty", fw_path);
377                 goto out;
378         }
379
380         fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
381         if (fw_map == MAP_FAILED) {
382                 WARN(0, "mmap(%s)", fw_path);
383                 goto out;
384         }
385
386
387         memset(&parm, 0, sizeof(parm));
388         parm.devfd = devfd;
389         parm.fwimage = fw_map;
390         parm.fwsize = st.st_size;
391         parm.dev_path = dev_path;
392         parm.fw_path = fw_path;
393
394         handler->update(&parm);
395
396 out:
397         if (fw_map != MAP_FAILED)
398                 munmap(fw_map, st.st_size);
399         free(fw_path);
400         if (fwfd >= 0)
401                 close(fwfd);
402         return (rc);
403 }
404
405 static int
406 do_update(const char *dev)
407 {
408         int fd, fwdfd;
409         unsigned int i;
410         int error;
411         struct ucode_handler *handler;
412         struct datadir *dir;
413         DIR *dirp;
414         struct dirent *direntry;
415
416         fd = open(dev, O_RDONLY);
417         if (fd < 0) {
418                 WARN(0, "error opening %s for reading", dev);
419                 return (1);
420         }
421
422         /*
423          * Find the appropriate handler for CPU.
424          */
425         for (i = 0; i < NHANDLERS; i++)
426                 if (handlers[i].probe(fd) == 0)
427                         break;
428         if (i < NHANDLERS)
429                 handler = &handlers[i];
430         else {
431                 WARNX(0, "cannot find the appropriate handler for %s", dev);
432                 close(fd);
433                 return (1);
434         }
435         close(fd);
436
437         fd = open(dev, O_RDWR);
438         if (fd < 0) {
439                 WARN(0, "error opening %s for writing", dev);
440                 return (1);
441         }
442
443         /*
444          * Process every image in specified data directories.
445          */
446         SLIST_FOREACH(dir, &datadirs, next) {
447                 fwdfd = open(dir->path, O_RDONLY);
448                 if (fwdfd < 0) {
449                         WARN(1, "skipping directory %s: not accessible", dir->path);
450                         continue;
451                 }
452                 dirp = fdopendir(fwdfd);
453                 if (dirp == NULL) {
454                         WARNX(0, "out of memory");
455                         close(fwdfd);
456                         close(fd);
457                         return (1);
458                 }
459
460                 while ((direntry = readdir(dirp)) != NULL) {
461                         if (direntry->d_namlen == 0)
462                                 continue;
463                         if (direntry->d_type == DT_DIR)
464                                 continue;
465
466                         error = try_a_fw_image(dev, fd, fwdfd, dir->path,
467                             direntry->d_name, handler);
468                         if (error != 0) {
469                                 closedir(dirp);
470                                 close(fd);
471                                 return (1);
472                         }
473                 }
474                 error = closedir(dirp);
475                 if (error != 0)
476                         WARN(0, "closedir(%s)", dir->path);
477         }
478         close(fd);
479         return (0);
480 }
481
482 /*
483  * Add new data directory to the search list.
484  */
485 static void
486 datadir_add(const char *path)
487 {
488         struct datadir *newdir;
489
490         newdir = (struct datadir *)malloc(sizeof(*newdir));
491         if (newdir == NULL)
492                 err(EX_OSERR, "cannot allocate memory");
493         newdir->path = path;
494         SLIST_INSERT_HEAD(&datadirs, newdir, next);
495 }
496
497 int
498 main(int argc, char *argv[])
499 {
500         struct datadir *elm;
501         int c, flags;
502         const char *cmdarg;
503         const char *dev;
504         int error;
505
506         flags = 0;
507         error = 0;
508         cmdarg = "";    /* To keep gcc3 happy. */
509
510         while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
511                 switch (c) {
512                 case 'd':
513                         datadir_add(optarg);
514                         break;
515                 case 'e':
516                         flags |= FLAG_E;
517                         break;
518                 case 'i':
519                         flags |= FLAG_I;
520                         cmdarg = optarg;
521                         break;
522                 case 'm':
523                         flags |= FLAG_M;
524                         cmdarg = optarg;
525                         break;
526                 case 'n':
527                         flags |= FLAG_N;
528                         break;
529                 case 'u':
530                         flags |= FLAG_U;
531                         break;
532                 case 'v':
533                         verbosity_level++;
534                         break;
535                 case 'h':
536                         /* FALLTHROUGH */
537                 default:
538                         usage();
539                         /* NOTREACHED */
540                 }
541         }
542         argc -= optind;
543         argv += optind;
544         if (argc < 1) {
545                 usage();
546                 /* NOTREACHED */
547         }
548         if ((flags & FLAG_N) == 0)
549                 datadir_add(DEFAULT_DATADIR);
550         dev = argv[0];
551         c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
552         switch (c) {
553         case FLAG_I:
554                 if (strstr(cmdarg, ",") != NULL)
555                         error = do_cpuid_count(cmdarg, dev);
556                 else
557                         error = do_cpuid(cmdarg, dev);
558                 break;
559         case FLAG_M:
560                 error = do_msr(cmdarg, dev);
561                 break;
562         case FLAG_U:
563                 error = do_update(dev);
564                 break;
565         case FLAG_E:
566                 error = do_eval_cpu_features(dev);
567                 break;
568         default:
569                 usage();        /* Only one command can be selected. */
570         }
571         while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
572                 SLIST_REMOVE_HEAD(&datadirs, next);
573                 free(elm);
574         }
575         return (error == 0 ? 0 : 1);
576 }