]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/cpucontrol/cpucontrol.c
Merge vendor lld/docs directory from r337145
[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 <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <err.h>
43 #include <sysexits.h>
44 #include <dirent.h>
45
46 #include <sys/queue.h>
47 #include <sys/param.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/ioctl.h>
51 #include <sys/cpuctl.h>
52
53 #include "cpucontrol.h"
54 #include "amd.h"
55 #include "intel.h"
56 #include "via.h"
57
58 int     verbosity_level = 0;
59
60 #define DEFAULT_DATADIR "/usr/local/share/cpucontrol"
61
62 #define FLAG_I  0x01
63 #define FLAG_M  0x02
64 #define FLAG_U  0x04
65 #define FLAG_N  0x08
66 #define FLAG_E  0x10
67
68 #define OP_INVAL        0x00
69 #define OP_READ         0x01
70 #define OP_WRITE        0x02
71 #define OP_OR           0x04
72 #define OP_AND          0x08
73
74 #define HIGH(val)       (uint32_t)(((val) >> 32) & 0xffffffff)
75 #define LOW(val)        (uint32_t)((val) & 0xffffffff)
76
77 /*
78  * Macros for freeing SLISTs, probably must be in /sys/queue.h
79  */
80 #define SLIST_FREE(head, field, freef) do {                             \
81                 typeof(SLIST_FIRST(head)) __elm0;                       \
82                 typeof(SLIST_FIRST(head)) __elm;                        \
83                 SLIST_FOREACH_SAFE(__elm, (head), field, __elm0)        \
84                         (void)(freef)(__elm);                           \
85 } while(0);
86
87 struct datadir {
88         const char              *path;
89         SLIST_ENTRY(datadir)    next;
90 };
91 static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
92
93 static struct ucode_handler {
94         ucode_probe_t *probe;
95         ucode_update_t *update;
96 } handlers[] = {
97         { intel_probe, intel_update },
98         { amd10h_probe, amd10h_update },
99         { amd_probe, amd_update },
100         { via_probe, via_update },
101 };
102 #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
103
104 static void     usage(void);
105 static int      isdir(const char *path);
106 static int      do_cpuid(const char *cmdarg, const char *dev);
107 static int      do_cpuid_count(const char *cmdarg, const char *dev);
108 static int      do_msr(const char *cmdarg, const char *dev);
109 static int      do_update(const char *dev);
110 static void     datadir_add(const char *path);
111
112 static void __dead2
113 usage(void)
114 {
115         const char *name;
116
117         name = getprogname();
118         if (name == NULL)
119                 name = "cpuctl";
120         fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
121             "-i level | -i level,level_type | -e | -u] device\n", name);
122         exit(EX_USAGE);
123 }
124
125 static int
126 isdir(const char *path)
127 {
128         int error;
129         struct stat st;
130
131         error = stat(path, &st);
132         if (error < 0) {
133                 WARN(0, "stat(%s)", path);
134                 return (error);
135         }
136         return (st.st_mode & S_IFDIR);
137 }
138
139 static int
140 do_cpuid(const char *cmdarg, const char *dev)
141 {
142         unsigned int level;
143         cpuctl_cpuid_args_t args;
144         int fd, error;
145         char *endptr;
146
147         assert(cmdarg != NULL);
148         assert(dev != NULL);
149
150         level = strtoul(cmdarg, &endptr, 16);
151         if (*cmdarg == '\0' || *endptr != '\0') {
152                 WARNX(0, "incorrect operand: %s", cmdarg);
153                 usage();
154                 /* NOTREACHED */
155         }
156
157         /*
158          * Fill ioctl argument structure.
159          */
160         args.level = level;
161         fd = open(dev, O_RDONLY);
162         if (fd < 0) {
163                 WARN(0, "error opening %s for reading", dev);
164                 return (1);
165         }
166         error = ioctl(fd, CPUCTL_CPUID, &args);
167         if (error < 0) {
168                 WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
169                 close(fd);
170                 return (error);
171         }
172         fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
173             level, args.data[0], args.data[1], args.data[2], args.data[3]);
174         close(fd);
175         return (0);
176 }
177
178 static int
179 do_cpuid_count(const char *cmdarg, const char *dev)
180 {
181         char *cmdarg1, *endptr, *endptr1;
182         unsigned int level, level_type;
183         cpuctl_cpuid_count_args_t args;
184         int fd, error;
185
186         assert(cmdarg != NULL);
187         assert(dev != NULL);
188
189         level = strtoul(cmdarg, &endptr, 16);
190         if (*cmdarg == '\0' || *endptr == '\0') {
191                 WARNX(0, "incorrect or missing operand: %s", cmdarg);
192                 usage();
193                 /* NOTREACHED */
194         }
195         /* Locate the comma... */
196         cmdarg1 = strstr(endptr, ",");
197         /* ... and skip past it */
198         cmdarg1 += 1;
199         level_type = strtoul(cmdarg1, &endptr1, 16);
200         if (*cmdarg1 == '\0' || *endptr1 != '\0') {
201                 WARNX(0, "incorrect or missing operand: %s", cmdarg);
202                 usage();
203                 /* NOTREACHED */
204         }
205
206         /*
207          * Fill ioctl argument structure.
208          */
209         args.level = level;
210         args.level_type = level_type;
211         fd = open(dev, O_RDONLY);
212         if (fd < 0) {
213                 WARN(0, "error opening %s for reading", dev);
214                 return (1);
215         }
216         error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
217         if (error < 0) {
218                 WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
219                 close(fd);
220                 return (error);
221         }
222         fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
223             "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
224             args.data[2], args.data[3]);
225         close(fd);
226         return (0);
227 }
228
229 static int
230 do_msr(const char *cmdarg, const char *dev)
231 {
232         unsigned int msr;
233         cpuctl_msr_args_t args;
234         size_t len;
235         uint64_t data = 0;
236         unsigned long command;
237         int do_invert = 0, op;
238         int fd, error;
239         const char *command_name;
240         char *endptr;
241         char *p;
242
243         assert(cmdarg != NULL);
244         assert(dev != NULL);
245         len = strlen(cmdarg);
246         if (len == 0) {
247                 WARNX(0, "MSR register expected");
248                 usage();
249                 /* NOTREACHED */
250         }
251
252         /*
253          * Parse command string.
254          */
255         msr = strtoul(cmdarg, &endptr, 16);
256         switch (*endptr) {
257         case '\0':
258                 op = OP_READ;
259                 break;
260         case '=':
261                 op = OP_WRITE;
262                 break;
263         case '&':
264                 op = OP_AND;
265                 endptr++;
266                 break;
267         case '|':
268                 op = OP_OR;
269                 endptr++;
270                 break;
271         default:
272                 op = OP_INVAL;
273         }
274         if (op != OP_READ) {    /* Complex operation. */
275                 if (*endptr != '=')
276                         op = OP_INVAL;
277                 else {
278                         p = ++endptr;
279                         if (*p == '~') {
280                                 do_invert = 1;
281                                 p++;
282                         }
283                         data = strtoull(p, &endptr, 16);
284                         if (*p == '\0' || *endptr != '\0') {
285                                 WARNX(0, "argument required: %s", cmdarg);
286                                 usage();
287                                 /* NOTREACHED */
288                         }
289                 }
290         }
291         if (op == OP_INVAL) {
292                 WARNX(0, "invalid operator: %s", cmdarg);
293                 usage();
294                 /* NOTREACHED */
295         }
296
297         /*
298          * Fill ioctl argument structure.
299          */
300         args.msr = msr;
301         if ((do_invert != 0) ^ (op == OP_AND))
302                 args.data = ~data;
303         else
304                 args.data = data;
305         switch (op) {
306         case OP_READ:
307                 command = CPUCTL_RDMSR;
308                 command_name = "RDMSR";
309                 break;
310         case OP_WRITE:
311                 command = CPUCTL_WRMSR;
312                 command_name = "WRMSR";
313                 break;
314         case OP_OR:
315                 command = CPUCTL_MSRSBIT;
316                 command_name = "MSRSBIT";
317                 break;
318         case OP_AND:
319                 command = CPUCTL_MSRCBIT;
320                 command_name = "MSRCBIT";
321                 break;
322         default:
323                 abort();
324         }
325         fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
326         if (fd < 0) {
327                 WARN(0, "error opening %s for %s", dev,
328                     op == OP_READ ? "reading" : "writing");
329                 return (1);
330         }
331         error = ioctl(fd, command, &args);
332         if (error < 0) {
333                 WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev, command_name, command);
334                 close(fd);
335                 return (1);
336         }
337         if (op == OP_READ)
338                 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
339                     HIGH(args.data), LOW(args.data));
340         close(fd);
341         return (0);
342 }
343
344 static int
345 do_eval_cpu_features(const char *dev)
346 {
347         int fd, error;
348
349         assert(dev != NULL);
350
351         fd = open(dev, O_RDWR);
352         if (fd < 0) {
353                 WARN(0, "error opening %s for writing", dev);
354                 return (1);
355         }
356         error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
357         if (error < 0)
358                 WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
359         close(fd);
360         return (error);
361 }
362
363 static int
364 do_update(const char *dev)
365 {
366         int fd;
367         unsigned int i;
368         int error;
369         struct ucode_handler *handler;
370         struct datadir *dir;
371         DIR *dirp;
372         struct dirent *direntry;
373         char buf[MAXPATHLEN];
374
375         fd = open(dev, O_RDONLY);
376         if (fd < 0) {
377                 WARN(0, "error opening %s for reading", dev);
378                 return (1);
379         }
380
381         /*
382          * Find the appropriate handler for device.
383          */
384         for (i = 0; i < NHANDLERS; i++)
385                 if (handlers[i].probe(fd) == 0)
386                         break;
387         if (i < NHANDLERS)
388                 handler = &handlers[i];
389         else {
390                 WARNX(0, "cannot find the appropriate handler for device");
391                 close(fd);
392                 return (1);
393         }
394         close(fd);
395
396         /*
397          * Process every image in specified data directories.
398          */
399         SLIST_FOREACH(dir, &datadirs, next) {
400                 dirp = opendir(dir->path);
401                 if (dirp == NULL) {
402                         WARNX(1, "skipping directory %s: not accessible", dir->path);
403                         continue;
404                 }
405                 while ((direntry = readdir(dirp)) != NULL) {
406                         if (direntry->d_namlen == 0)
407                                 continue;
408                         error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
409                             direntry->d_name);
410                         if ((unsigned)error >= sizeof(buf))
411                                 WARNX(0, "skipping %s, buffer too short",
412                                     direntry->d_name);
413                         if (isdir(buf) != 0) {
414                                 WARNX(2, "skipping %s: is a directory", buf);
415                                 continue;
416                         }
417                         handler->update(dev, buf);
418                 }
419                 error = closedir(dirp);
420                 if (error != 0)
421                         WARN(0, "closedir(%s)", dir->path);
422         }
423         return (0);
424 }
425
426 /*
427  * Add new data directory to the search list.
428  */
429 static void
430 datadir_add(const char *path)
431 {
432         struct datadir *newdir;
433
434         newdir = (struct datadir *)malloc(sizeof(*newdir));
435         if (newdir == NULL)
436                 err(EX_OSERR, "cannot allocate memory");
437         newdir->path = path;
438         SLIST_INSERT_HEAD(&datadirs, newdir, next);
439 }
440
441 int
442 main(int argc, char *argv[])
443 {
444         int c, flags;
445         const char *cmdarg;
446         const char *dev;
447         int error;
448
449         flags = 0;
450         error = 0;
451         cmdarg = "";    /* To keep gcc3 happy. */
452
453         while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
454                 switch (c) {
455                 case 'd':
456                         datadir_add(optarg);
457                         break;
458                 case 'e':
459                         flags |= FLAG_E;
460                         break;
461                 case 'i':
462                         flags |= FLAG_I;
463                         cmdarg = optarg;
464                         break;
465                 case 'm':
466                         flags |= FLAG_M;
467                         cmdarg = optarg;
468                         break;
469                 case 'n':
470                         flags |= FLAG_N;
471                         break;
472                 case 'u':
473                         flags |= FLAG_U;
474                         break;
475                 case 'v':
476                         verbosity_level++;
477                         break;
478                 case 'h':
479                         /* FALLTHROUGH */
480                 default:
481                         usage();
482                         /* NOTREACHED */
483                 }
484         }
485         argc -= optind;
486         argv += optind;
487         if (argc < 1) {
488                 usage();
489                 /* NOTREACHED */
490         }
491         if ((flags & FLAG_N) == 0)
492                 datadir_add(DEFAULT_DATADIR);
493         dev = argv[0];
494         c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
495         switch (c) {
496         case FLAG_I:
497                 if (strstr(cmdarg, ",") != NULL)
498                         error = do_cpuid_count(cmdarg, dev);
499                 else
500                         error = do_cpuid(cmdarg, dev);
501                 break;
502         case FLAG_M:
503                 error = do_msr(cmdarg, dev);
504                 break;
505         case FLAG_U:
506                 error = do_update(dev);
507                 break;
508         case FLAG_E:
509                 error = do_eval_cpu_features(dev);
510                 break;
511         default:
512                 usage();        /* Only one command can be selected. */
513         }
514         SLIST_FREE(&datadirs, next, free);
515         return (error == 0 ? 0 : 1);
516 }