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