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