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