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