]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/cpucontrol/cpucontrol.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / cpucontrol / cpucontrol.c
1 /*-
2  * Copyright (c) 2008 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()
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         char *endptr;
181         char *p;
182
183         assert(cmdarg != NULL);
184         assert(dev != NULL);
185         len = strlen(cmdarg);
186         if (len == 0) {
187                 WARNX(0, "MSR register expected");
188                 usage();
189                 /* NOTREACHED */
190         }
191
192         /*
193          * Parse command string.
194          */
195         msr = strtoul(cmdarg, &endptr, 16);
196         switch (*endptr) {
197         case '\0':
198                 op = OP_READ;
199                 break;
200         case '=':
201                 op = OP_WRITE;
202                 break;
203         case '&':
204                 op = OP_AND;
205                 endptr++;
206                 break;
207         case '|':
208                 op = OP_OR;
209                 endptr++;
210                 break;
211         default:
212                 op = OP_INVAL;
213         }
214         if (op != OP_READ) {    /* Complex operation. */
215                 if (*endptr != '=')
216                         op = OP_INVAL;
217                 else {
218                         p = ++endptr;
219                         if (*p == '~') {
220                                 do_invert = 1;
221                                 p++;
222                         }
223                         data = strtoull(p, &endptr, 16);
224                         if (*p == '\0' || *endptr != '\0') {
225                                 WARNX(0, "argument required: %s", cmdarg);
226                                 usage();
227                                 /* NOTREACHED */
228                         }
229                 }
230         }
231         if (op == OP_INVAL) {
232                 WARNX(0, "invalid operator: %s", cmdarg);
233                 usage();
234                 /* NOTREACHED */
235         }
236
237         /*
238          * Fill ioctl argument structure.
239          */
240         args.msr = msr;
241         if ((do_invert != 0) ^ (op == OP_AND))
242                 args.data = ~data;
243         else
244                 args.data = data;
245         switch (op) {
246         case OP_READ:
247                 command = CPUCTL_RDMSR;
248                 break;
249         case OP_WRITE:
250                 command = CPUCTL_WRMSR;
251                 break;
252         case OP_OR:
253                 command = CPUCTL_MSRSBIT;
254                 break;
255         case OP_AND:
256                 command = CPUCTL_MSRCBIT;
257                 break;
258         default:
259                 abort();
260         }
261         fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
262         if (fd < 0) {
263                 WARN(0, "error opening %s for %s", dev,
264                     op == OP_READ ? "reading" : "writing");
265                 return (1);
266         }
267         error = ioctl(fd, command, &args);
268         if (error < 0) {
269                 WARN(0, "ioctl(%s, %lu)", dev, command);
270                 close(fd);
271                 return (1);
272         }
273         if (op == OP_READ)
274                 fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
275                     HIGH(args.data), LOW(args.data));
276         close(fd);
277         return (0);
278 }
279
280 static int
281 do_update(const char *dev)
282 {
283         int fd;
284         unsigned int i;
285         int error;
286         struct ucode_handler *handler;
287         struct datadir *dir;
288         DIR *dirfd;
289         struct dirent *direntry;
290         char buf[MAXPATHLEN];
291
292         fd = open(dev, O_RDONLY);
293         if (fd < 0) {
294                 WARN(0, "error opening %s for reading", dev);
295                 return (1);
296         }
297
298         /*
299          * Find the appropriate handler for device.
300          */
301         for (i = 0; i < NHANDLERS; i++)
302                 if (handlers[i].probe(fd) == 0)
303                         break;
304         if (i < NHANDLERS)
305                 handler = &handlers[i];
306         else {
307                 WARNX(0, "cannot find the appropriate handler for device");
308                 close(fd);
309                 return (1);
310         }
311         close(fd);
312
313         /*
314          * Process every image in specified data directories.
315          */
316         SLIST_FOREACH(dir, &datadirs, next) {
317                 dirfd  = opendir(dir->path);
318                 if (dirfd == NULL) {
319                         WARNX(1, "skipping directory %s: not accessible", dir->path);
320                         continue;
321                 }
322                 while ((direntry = readdir(dirfd)) != NULL) {
323                         if (direntry->d_namlen == 0)
324                                 continue;
325                         error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
326                             direntry->d_name);
327                         if ((unsigned)error >= sizeof(buf))
328                                 WARNX(0, "skipping %s, buffer too short",
329                                     direntry->d_name);
330                         if (isdir(buf) != 0) {
331                                 WARNX(2, "skipping %s: is a directory", buf);
332                                 continue;
333                         }
334                         handler->update(dev, buf);
335                 }
336                 error = closedir(dirfd);
337                 if (error != 0)
338                         WARN(0, "closedir(%s)", dir->path);
339         }
340         return (0);
341 }
342
343 /*
344  * Add new data directory to the search list.
345  */
346 static void
347 datadir_add(const char *path)
348 {
349         struct datadir *newdir;
350
351         newdir = (struct datadir *)malloc(sizeof(*newdir));
352         if (newdir == NULL)
353                 err(EX_OSERR, "cannot allocate memory");
354         newdir->path = path;
355         SLIST_INSERT_HEAD(&datadirs, newdir, next);
356 }
357
358 int
359 main(int argc, char *argv[])
360 {
361         int c, flags;
362         const char *cmdarg;
363         const char *dev;
364         int error;
365
366         flags = 0;
367         error = 0;
368         cmdarg = "";    /* To keep gcc3 happy. */
369
370         /*
371          * Add all default data dirs to the list first.
372          */
373         datadir_add(DEFAULT_DATADIR);
374         while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
375                 switch (c) {
376                 case 'd':
377                         datadir_add(optarg);
378                         break;
379                 case 'i':
380                         flags |= FLAG_I;
381                         cmdarg = optarg;
382                         break;
383                 case 'm':
384                         flags |= FLAG_M;
385                         cmdarg = optarg;
386                         break;
387                 case 'u':
388                         flags |= FLAG_U;
389                         break;
390                 case 'v':
391                         verbosity_level++;
392                         break;
393                 case 'h':
394                         /* FALLTHROUGH */
395                 default:
396                         usage();
397                         /* NOTREACHED */
398                 }
399         }
400         argc -= optind;
401         argv += optind;
402         if (argc < 1) {
403                 usage();
404                 /* NOTREACHED */
405         }
406         dev = argv[0];
407         c = flags & (FLAG_I | FLAG_M | FLAG_U);
408         switch (c) {
409                 case FLAG_I:
410                         error = do_cpuid(cmdarg, dev);
411                         break;
412                 case FLAG_M:
413                         error = do_msr(cmdarg, dev);
414                         break;
415                 case FLAG_U:
416                         error = do_update(dev);
417                         break;
418                 default:
419                         usage();        /* Only one command can be selected. */
420         }
421         SLIST_FREE(&datadirs, next, free);
422         return (error);
423 }