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