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