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