Deleted Added
full compact
cpucontrol.c (201145) cpucontrol.c (201227)
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>
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: head/usr.sbin/cpucontrol/cpucontrol.c 201145 2009-12-28 22:56:30Z antoine $");
32__FBSDID("$FreeBSD: head/usr.sbin/cpucontrol/cpucontrol.c 201227 2009-12-29 22:53:27Z ed $");
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
55int 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
82struct datadir {
83 const char *path;
84 SLIST_ENTRY(datadir) next;
85};
86static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
87
88struct 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
97static void usage(void);
98static int isdir(const char *path);
99static int do_cpuid(const char *cmdarg, const char *dev);
100static int do_msr(const char *cmdarg, const char *dev);
101static int do_update(const char *dev);
102static void datadir_add(const char *path);
103
104static void __dead2
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
55int 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
82struct datadir {
83 const char *path;
84 SLIST_ENTRY(datadir) next;
85};
86static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
87
88struct 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
97static void usage(void);
98static int isdir(const char *path);
99static int do_cpuid(const char *cmdarg, const char *dev);
100static int do_msr(const char *cmdarg, const char *dev);
101static int do_update(const char *dev);
102static void datadir_add(const char *path);
103
104static void __dead2
105usage()
105usage(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
117static int
118isdir(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
131static int
132do_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
170static int
171do_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
280static int
281do_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 */
346static void
347datadir_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
358int
359main(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}
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
117static int
118isdir(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
131static int
132do_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
170static int
171do_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
280static int
281do_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 */
346static void
347datadir_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
358int
359main(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}