xref: /freebsd/usr.sbin/cpucontrol/cpucontrol.c (revision b2d758545bc5d29786ee9ab87d10de7ab52a7334)
1e085f869SStanislav Sedov /*-
2e085f869SStanislav Sedov  * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
3e085f869SStanislav Sedov  * All rights reserved.
4e085f869SStanislav Sedov  *
5e085f869SStanislav Sedov  * Redistribution and use in source and binary forms, with or without
6e085f869SStanislav Sedov  * modification, are permitted provided that the following conditions
7e085f869SStanislav Sedov  * are met:
8e085f869SStanislav Sedov  * 1. Redistributions of source code must retain the above copyright
9e085f869SStanislav Sedov  *    notice, this list of conditions and the following disclaimer.
10e085f869SStanislav Sedov  * 2. Redistributions in binary form must reproduce the above copyright
11e085f869SStanislav Sedov  *    notice, this list of conditions and the following disclaimer in the
12e085f869SStanislav Sedov  *    documentation and/or other materials provided with the distribution.
13e085f869SStanislav Sedov  *
14e085f869SStanislav Sedov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15e085f869SStanislav Sedov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16e085f869SStanislav Sedov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17e085f869SStanislav Sedov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18e085f869SStanislav Sedov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19e085f869SStanislav Sedov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20e085f869SStanislav Sedov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21e085f869SStanislav Sedov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22e085f869SStanislav Sedov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23e085f869SStanislav Sedov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24e085f869SStanislav Sedov  */
25e085f869SStanislav Sedov 
26e085f869SStanislav Sedov /*
27e085f869SStanislav Sedov  * This utility provides userland access to the cpuctl(4) pseudo-device
28e085f869SStanislav Sedov  * features.
29e085f869SStanislav Sedov  */
30e085f869SStanislav Sedov 
31e085f869SStanislav Sedov #include <sys/cdefs.h>
32e085f869SStanislav Sedov __FBSDID("$FreeBSD$");
33e085f869SStanislav Sedov 
34e085f869SStanislav Sedov #include <assert.h>
35e085f869SStanislav Sedov #include <stdio.h>
36e085f869SStanislav Sedov #include <stdlib.h>
37e085f869SStanislav Sedov #include <string.h>
38e085f869SStanislav Sedov #include <unistd.h>
39e085f869SStanislav Sedov #include <fcntl.h>
40e085f869SStanislav Sedov #include <err.h>
41e085f869SStanislav Sedov #include <sysexits.h>
42e085f869SStanislav Sedov #include <dirent.h>
43e085f869SStanislav Sedov 
44e085f869SStanislav Sedov #include <sys/queue.h>
45e085f869SStanislav Sedov #include <sys/param.h>
46e085f869SStanislav Sedov #include <sys/types.h>
47e085f869SStanislav Sedov #include <sys/stat.h>
48e085f869SStanislav Sedov #include <sys/ioctl.h>
49e085f869SStanislav Sedov #include <sys/cpuctl.h>
50e085f869SStanislav Sedov 
51e085f869SStanislav Sedov #include "cpucontrol.h"
52e085f869SStanislav Sedov #include "amd.h"
53e085f869SStanislav Sedov #include "intel.h"
54e085f869SStanislav Sedov 
55e085f869SStanislav Sedov int	verbosity_level = 0;
56e085f869SStanislav Sedov 
57e085f869SStanislav Sedov #define	DEFAULT_DATADIR	"/usr/local/share/cpucontrol"
58e085f869SStanislav Sedov 
59e085f869SStanislav Sedov #define	FLAG_I	0x01
60e085f869SStanislav Sedov #define	FLAG_M	0x02
61e085f869SStanislav Sedov #define	FLAG_U	0x04
62e085f869SStanislav Sedov 
63b2d75854SStanislav Sedov #define	OP_INVAL	0x00
64b2d75854SStanislav Sedov #define	OP_READ		0x01
65b2d75854SStanislav Sedov #define	OP_WRITE	0x02
66b2d75854SStanislav Sedov #define	OP_OR		0x04
67b2d75854SStanislav Sedov #define	OP_AND		0x08
68b2d75854SStanislav Sedov 
69e085f869SStanislav Sedov #define	HIGH(val)	(uint32_t)(((val) >> 32) & 0xffffffff)
70e085f869SStanislav Sedov #define	LOW(val)	(uint32_t)((val) & 0xffffffff)
71e085f869SStanislav Sedov 
72e085f869SStanislav Sedov /*
73e085f869SStanislav Sedov  * Macros for freeing SLISTs, probably must be in /sys/queue.h
74e085f869SStanislav Sedov  */
75e085f869SStanislav Sedov #define	SLIST_FREE(head, field, freef) do {				\
76e085f869SStanislav Sedov 		typeof(SLIST_FIRST(head)) __elm0;			\
77e085f869SStanislav Sedov 		typeof(SLIST_FIRST(head)) __elm;			\
78e085f869SStanislav Sedov 		SLIST_FOREACH_SAFE(__elm, (head), field, __elm0)	\
79e085f869SStanislav Sedov 			(void)(freef)(__elm);				\
80e085f869SStanislav Sedov } while(0);
81e085f869SStanislav Sedov 
82e085f869SStanislav Sedov struct datadir {
83e085f869SStanislav Sedov 	const char		*path;
84e085f869SStanislav Sedov 	SLIST_ENTRY(datadir)	next;
85e085f869SStanislav Sedov };
86e085f869SStanislav Sedov static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(&datadirs);
87e085f869SStanislav Sedov 
88e085f869SStanislav Sedov struct ucode_handler {
89e085f869SStanislav Sedov 	ucode_probe_t *probe;
90e085f869SStanislav Sedov 	ucode_update_t *update;
91e085f869SStanislav Sedov } handlers[] = {
92e085f869SStanislav Sedov 	{ intel_probe, intel_update },
93e085f869SStanislav Sedov 	{ amd_probe, amd_update },
94e085f869SStanislav Sedov };
95e085f869SStanislav Sedov #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
96e085f869SStanislav Sedov 
97e085f869SStanislav Sedov static void	usage(void);
98e085f869SStanislav Sedov static int	isdir(const char *path);
99e085f869SStanislav Sedov static int	do_cpuid(const char *cmdarg, const char *dev);
100e085f869SStanislav Sedov static int	do_msr(const char *cmdarg, const char *dev);
101e085f869SStanislav Sedov static int	do_update(const char *dev);
102e085f869SStanislav Sedov static void	datadir_add(const char *path);
103e085f869SStanislav Sedov 
104e085f869SStanislav Sedov static void __dead2
105e085f869SStanislav Sedov usage()
106e085f869SStanislav Sedov {
107e085f869SStanislav Sedov 	const char *name;
108e085f869SStanislav Sedov 
109e085f869SStanislav Sedov 	name = getprogname();
110e085f869SStanislav Sedov 	if (name == NULL)
111e085f869SStanislav Sedov 		name = "cpuctl";
112e085f869SStanislav Sedov 	fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
113e085f869SStanislav Sedov 	    "-i level | -u] device\n", name);
114e085f869SStanislav Sedov 	exit(EX_USAGE);
115e085f869SStanislav Sedov }
116e085f869SStanislav Sedov 
117e085f869SStanislav Sedov static int
118e085f869SStanislav Sedov isdir(const char *path)
119e085f869SStanislav Sedov {
120e085f869SStanislav Sedov 	int error;
121e085f869SStanislav Sedov 	struct stat st;
122e085f869SStanislav Sedov 
123e085f869SStanislav Sedov 	error = stat(path, &st);
124e085f869SStanislav Sedov 	if (error < 0) {
125e085f869SStanislav Sedov 		WARN(0, "stat(%s)", path);
126e085f869SStanislav Sedov 		return (error);
127e085f869SStanislav Sedov 	}
128e085f869SStanislav Sedov 	return (st.st_mode & S_IFDIR);
129e085f869SStanislav Sedov }
130e085f869SStanislav Sedov 
131e085f869SStanislav Sedov static int
132e085f869SStanislav Sedov do_cpuid(const char *cmdarg, const char *dev)
133e085f869SStanislav Sedov {
134e085f869SStanislav Sedov 	unsigned int level;
135e085f869SStanislav Sedov 	cpuctl_cpuid_args_t args;
136e085f869SStanislav Sedov 	int fd, error;
137e085f869SStanislav Sedov 	char *endptr;
138e085f869SStanislav Sedov 
139e085f869SStanislav Sedov 	assert(cmdarg != NULL);
140e085f869SStanislav Sedov 	assert(dev != NULL);
141e085f869SStanislav Sedov 
142e085f869SStanislav Sedov 	level = strtoul(cmdarg, &endptr, 16);
143e085f869SStanislav Sedov 	if (*cmdarg == '\0' || *endptr != '\0') {
144e085f869SStanislav Sedov 		WARNX(0, "incorrect operand: %s", cmdarg);
145e085f869SStanislav Sedov 		usage();
146e085f869SStanislav Sedov 		/* NOTREACHED */
147e085f869SStanislav Sedov 	}
148e085f869SStanislav Sedov 
149e085f869SStanislav Sedov 	/*
150e085f869SStanislav Sedov 	 * Fill ioctl argument structure.
151e085f869SStanislav Sedov 	 */
152e085f869SStanislav Sedov 	args.level = level;
153e085f869SStanislav Sedov 	fd = open(dev, O_RDONLY);
154e085f869SStanislav Sedov 	if (fd < 0) {
155cbcc5579SStanislav Sedov 		WARN(0, "error opening %s for reading", dev);
156e085f869SStanislav Sedov 		return (1);
157e085f869SStanislav Sedov 	}
158e085f869SStanislav Sedov 	error = ioctl(fd, CPUCTL_CPUID, &args);
159e085f869SStanislav Sedov 	if (error < 0) {
160cbcc5579SStanislav Sedov 		WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
161e085f869SStanislav Sedov 		close(fd);
162e085f869SStanislav Sedov 		return (error);
163e085f869SStanislav Sedov 	}
164e085f869SStanislav Sedov 	fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
165e085f869SStanislav Sedov 	    level, args.data[0], args.data[1], args.data[2], args.data[3]);
166e085f869SStanislav Sedov 	close(fd);
167e085f869SStanislav Sedov 	return (0);
168e085f869SStanislav Sedov }
169e085f869SStanislav Sedov 
170e085f869SStanislav Sedov static int
171e085f869SStanislav Sedov do_msr(const char *cmdarg, const char *dev)
172e085f869SStanislav Sedov {
173e085f869SStanislav Sedov 	unsigned int msr;
174e085f869SStanislav Sedov 	cpuctl_msr_args_t args;
175b2d75854SStanislav Sedov 	size_t len;
176b2d75854SStanislav Sedov 	uint64_t data = 0;
177b2d75854SStanislav Sedov 	unsigned long command;
178b2d75854SStanislav Sedov 	int do_invert = 0, op;
179e085f869SStanislav Sedov 	int fd, error;
180e085f869SStanislav Sedov 	char *endptr;
181b2d75854SStanislav Sedov 	char *p;
182e085f869SStanislav Sedov 
183e085f869SStanislav Sedov 	assert(cmdarg != NULL);
184e085f869SStanislav Sedov 	assert(dev != NULL);
185b2d75854SStanislav Sedov 	len = strlen(cmdarg);
186b2d75854SStanislav Sedov 	if (len == 0) {
187b2d75854SStanislav Sedov 		WARNX(0, "MSR register expected");
188b2d75854SStanislav Sedov 		usage();
189b2d75854SStanislav Sedov 		/* NOTREACHED */
190b2d75854SStanislav Sedov 	}
191e085f869SStanislav Sedov 
192b2d75854SStanislav Sedov 	/*
193b2d75854SStanislav Sedov 	 * Parse command string.
194b2d75854SStanislav Sedov 	 */
195b2d75854SStanislav Sedov 	msr = strtoul(cmdarg, &endptr, 16);
196b2d75854SStanislav Sedov 	switch (*endptr) {
197b2d75854SStanislav Sedov 	case '\0':
198b2d75854SStanislav Sedov 		op = OP_READ;
199b2d75854SStanislav Sedov 		break;
200b2d75854SStanislav Sedov 	case '=':
201b2d75854SStanislav Sedov 		op = OP_WRITE;
202b2d75854SStanislav Sedov 		break;
203b2d75854SStanislav Sedov 	case '&':
204b2d75854SStanislav Sedov 		op = OP_AND;
205b2d75854SStanislav Sedov 		endptr++;
206b2d75854SStanislav Sedov 		break;
207b2d75854SStanislav Sedov 	case '|':
208b2d75854SStanislav Sedov 		op = OP_OR;
209b2d75854SStanislav Sedov 		endptr++;
210b2d75854SStanislav Sedov 		break;
211b2d75854SStanislav Sedov 	default:
212b2d75854SStanislav Sedov 		op = OP_INVAL;
213b2d75854SStanislav Sedov 	}
214b2d75854SStanislav Sedov 	if (op != OP_READ) {	/* Complex operation. */
215b2d75854SStanislav Sedov 		if (*endptr != '=')
216b2d75854SStanislav Sedov 			op = OP_INVAL;
217b2d75854SStanislav Sedov 		else {
218b2d75854SStanislav Sedov 			p = ++endptr;
219b2d75854SStanislav Sedov 			if (*p == '~') {
220b2d75854SStanislav Sedov 				do_invert = 1;
221b2d75854SStanislav Sedov 				p++;
222b2d75854SStanislav Sedov 			}
223b2d75854SStanislav Sedov 			data = strtoull(p, &endptr, 16);
224e085f869SStanislav Sedov 			if (*p == '\0' || *endptr != '\0') {
225b2d75854SStanislav Sedov 				WARNX(0, "argument required: %s", cmdarg);
226e085f869SStanislav Sedov 				usage();
227e085f869SStanislav Sedov 				/* NOTREACHED */
228e085f869SStanislav Sedov 			}
229e085f869SStanislav Sedov 		}
230b2d75854SStanislav Sedov 	}
231b2d75854SStanislav Sedov 	if (op == OP_INVAL) {
232b2d75854SStanislav Sedov 		WARNX(0, "invalid operator: %s", cmdarg);
233e085f869SStanislav Sedov 		usage();
234e085f869SStanislav Sedov 		/* NOTREACHED */
235e085f869SStanislav Sedov 	}
236e085f869SStanislav Sedov 
237e085f869SStanislav Sedov 	/*
238e085f869SStanislav Sedov 	 * Fill ioctl argument structure.
239e085f869SStanislav Sedov 	 */
240e085f869SStanislav Sedov 	args.msr = msr;
241b2d75854SStanislav Sedov 	if ((do_invert != 0) ^ (op == OP_AND))
242b2d75854SStanislav Sedov 		args.data = ~data;
243b2d75854SStanislav Sedov 	else
244b2d75854SStanislav Sedov 		args.data = data;
245b2d75854SStanislav Sedov 	switch (op) {
246b2d75854SStanislav Sedov 	case OP_READ:
247b2d75854SStanislav Sedov 		command = CPUCTL_RDMSR;
248b2d75854SStanislav Sedov 		break;
249b2d75854SStanislav Sedov 	case OP_WRITE:
250b2d75854SStanislav Sedov 		command = CPUCTL_WRMSR;
251b2d75854SStanislav Sedov 		break;
252b2d75854SStanislav Sedov 	case OP_OR:
253b2d75854SStanislav Sedov 		command = CPUCTL_MSRSBIT;
254b2d75854SStanislav Sedov 		break;
255b2d75854SStanislav Sedov 	case OP_AND:
256b2d75854SStanislav Sedov 		command = CPUCTL_MSRCBIT;
257b2d75854SStanislav Sedov 		break;
258b2d75854SStanislav Sedov 	default:
259b2d75854SStanislav Sedov 		abort();
260b2d75854SStanislav Sedov 	}
261b2d75854SStanislav Sedov 	fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
262e085f869SStanislav Sedov 	if (fd < 0) {
263cbcc5579SStanislav Sedov 		WARN(0, "error opening %s for %s", dev,
264b2d75854SStanislav Sedov 		    op == OP_READ ? "reading" : "writing");
265e085f869SStanislav Sedov 		return (1);
266e085f869SStanislav Sedov 	}
267b2d75854SStanislav Sedov 	error = ioctl(fd, command, &args);
268e085f869SStanislav Sedov 	if (error < 0) {
269b2d75854SStanislav Sedov 		WARN(0, "ioctl(%s, %lu)", dev, command);
270e085f869SStanislav Sedov 		close(fd);
271e085f869SStanislav Sedov 		return (1);
272e085f869SStanislav Sedov 	}
273b2d75854SStanislav Sedov 	if (op == OP_READ)
274e085f869SStanislav Sedov 		fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
275e085f869SStanislav Sedov 		    HIGH(args.data), LOW(args.data));
276e085f869SStanislav Sedov 	close(fd);
277e085f869SStanislav Sedov 	return (0);
278e085f869SStanislav Sedov }
279e085f869SStanislav Sedov 
280e085f869SStanislav Sedov static int
281e085f869SStanislav Sedov do_update(const char *dev)
282e085f869SStanislav Sedov {
283e085f869SStanislav Sedov 	int fd;
284e085f869SStanislav Sedov 	unsigned int i;
285e085f869SStanislav Sedov 	int error;
286e085f869SStanislav Sedov 	struct ucode_handler *handler;
287e085f869SStanislav Sedov 	struct datadir *dir;
288e085f869SStanislav Sedov 	DIR *dirfd;
289e085f869SStanislav Sedov 	struct dirent *direntry;
290e085f869SStanislav Sedov 	char buf[MAXPATHLEN];
291e085f869SStanislav Sedov 
292e085f869SStanislav Sedov 	fd = open(dev, O_RDONLY);
293e085f869SStanislav Sedov 	if (fd < 0) {
294cbcc5579SStanislav Sedov 		WARN(0, "error opening %s for reading", dev);
295e085f869SStanislav Sedov 		return (1);
296e085f869SStanislav Sedov 	}
297e085f869SStanislav Sedov 
298e085f869SStanislav Sedov 	/*
299e085f869SStanislav Sedov 	 * Find the appropriate handler for device.
300e085f869SStanislav Sedov 	 */
301e085f869SStanislav Sedov 	for (i = 0; i < NHANDLERS; i++)
302e085f869SStanislav Sedov 		if (handlers[i].probe(fd) == 0)
303e085f869SStanislav Sedov 			break;
304e085f869SStanislav Sedov 	if (i < NHANDLERS)
305e085f869SStanislav Sedov 		handler = &handlers[i];
306e085f869SStanislav Sedov 	else {
307e085f869SStanislav Sedov 		WARNX(0, "cannot find the appropriate handler for device");
308e085f869SStanislav Sedov 		close(fd);
309e085f869SStanislav Sedov 		return (1);
310e085f869SStanislav Sedov 	}
311e085f869SStanislav Sedov 	close(fd);
312e085f869SStanislav Sedov 
313e085f869SStanislav Sedov 	/*
314e085f869SStanislav Sedov 	 * Process every image in specified data directories.
315e085f869SStanislav Sedov 	 */
316e085f869SStanislav Sedov 	SLIST_FOREACH(dir, &datadirs, next) {
317e085f869SStanislav Sedov 		dirfd  = opendir(dir->path);
318e085f869SStanislav Sedov 		if (dirfd == NULL) {
319e085f869SStanislav Sedov 			WARNX(1, "skipping directory %s: not accessible", dir->path);
320e085f869SStanislav Sedov 			continue;
321e085f869SStanislav Sedov 		}
322e085f869SStanislav Sedov 		while ((direntry = readdir(dirfd)) != NULL) {
323e085f869SStanislav Sedov 			if (direntry->d_namlen == 0)
324e085f869SStanislav Sedov 				continue;
325e085f869SStanislav Sedov 			error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
326e085f869SStanislav Sedov 			    direntry->d_name);
327e085f869SStanislav Sedov 			if ((unsigned)error >= sizeof(buf))
328e085f869SStanislav Sedov 				WARNX(0, "skipping %s, buffer too short",
329e085f869SStanislav Sedov 				    direntry->d_name);
330e085f869SStanislav Sedov 			if (isdir(buf) != 0) {
331e085f869SStanislav Sedov 				WARNX(2, "skipping %s: is a directory", buf);
332e085f869SStanislav Sedov 				continue;
333e085f869SStanislav Sedov 			}
334e085f869SStanislav Sedov 			handler->update(dev, buf);
335e085f869SStanislav Sedov 		}
336e085f869SStanislav Sedov 		error = closedir(dirfd);
337e085f869SStanislav Sedov 		if (error != 0)
338e085f869SStanislav Sedov 			WARN(0, "closedir(%s)", dir->path);
339e085f869SStanislav Sedov 	}
340e085f869SStanislav Sedov 	return (0);
341e085f869SStanislav Sedov }
342e085f869SStanislav Sedov 
343e085f869SStanislav Sedov /*
344e085f869SStanislav Sedov  * Add new data directory to the search list.
345e085f869SStanislav Sedov  */
346e085f869SStanislav Sedov static void
347e085f869SStanislav Sedov datadir_add(const char *path)
348e085f869SStanislav Sedov {
349e085f869SStanislav Sedov 	struct datadir *newdir;
350e085f869SStanislav Sedov 
351e085f869SStanislav Sedov 	newdir = (struct datadir *)malloc(sizeof(*newdir));
352e085f869SStanislav Sedov 	if (newdir == NULL)
353e085f869SStanislav Sedov 		err(EX_OSERR, "cannot allocate memory");
354e085f869SStanislav Sedov 	newdir->path = path;
355e085f869SStanislav Sedov 	SLIST_INSERT_HEAD(&datadirs, newdir, next);
356e085f869SStanislav Sedov }
357e085f869SStanislav Sedov 
358e085f869SStanislav Sedov int
359e085f869SStanislav Sedov main(int argc, char *argv[])
360e085f869SStanislav Sedov {
361e085f869SStanislav Sedov 	int c, flags;
362e085f869SStanislav Sedov 	const char *cmdarg;
363e085f869SStanislav Sedov 	const char *dev;
364e085f869SStanislav Sedov 	int error;
365e085f869SStanislav Sedov 
366e085f869SStanislav Sedov 	flags = 0;
367e085f869SStanislav Sedov 	error = 0;
368e085f869SStanislav Sedov 	cmdarg = "";	/* To keep gcc3 happy. */
369e085f869SStanislav Sedov 
370e085f869SStanislav Sedov 	/*
371e085f869SStanislav Sedov 	 * Add all default data dirs to the list first.
372e085f869SStanislav Sedov 	 */
373e085f869SStanislav Sedov 	datadir_add(DEFAULT_DATADIR);
374e085f869SStanislav Sedov 	while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
375e085f869SStanislav Sedov 		switch (c) {
376e085f869SStanislav Sedov 		case 'd':
377e085f869SStanislav Sedov 			datadir_add(optarg);
378e085f869SStanislav Sedov 			break;
379e085f869SStanislav Sedov 		case 'i':
380e085f869SStanislav Sedov 			flags |= FLAG_I;
381e085f869SStanislav Sedov 			cmdarg = optarg;
382e085f869SStanislav Sedov 			break;
383e085f869SStanislav Sedov 		case 'm':
384e085f869SStanislav Sedov 			flags |= FLAG_M;
385e085f869SStanislav Sedov 			cmdarg = optarg;
386e085f869SStanislav Sedov 			break;
387e085f869SStanislav Sedov 		case 'u':
388e085f869SStanislav Sedov 			flags |= FLAG_U;
389e085f869SStanislav Sedov 			break;
390e085f869SStanislav Sedov 		case 'v':
391e085f869SStanislav Sedov 			verbosity_level++;
392e085f869SStanislav Sedov 			break;
393e085f869SStanislav Sedov 		case 'h':
394e085f869SStanislav Sedov 			/* FALLTHROUGH */
395e085f869SStanislav Sedov 		default:
396e085f869SStanislav Sedov 			usage();
397e085f869SStanislav Sedov 			/* NOTREACHED */
398e085f869SStanislav Sedov 		}
399e085f869SStanislav Sedov 	}
400e085f869SStanislav Sedov 	argc -= optind;
401e085f869SStanislav Sedov 	argv += optind;
402e085f869SStanislav Sedov 	if (argc < 1) {
403e085f869SStanislav Sedov 		usage();
404e085f869SStanislav Sedov 		/* NOTREACHED */
405e085f869SStanislav Sedov 	}
406e085f869SStanislav Sedov 	dev = argv[0];
407e085f869SStanislav Sedov 	c = flags & (FLAG_I | FLAG_M | FLAG_U);
408e085f869SStanislav Sedov 	switch (c) {
409e085f869SStanislav Sedov 		case FLAG_I:
410e085f869SStanislav Sedov 			error = do_cpuid(cmdarg, dev);
411e085f869SStanislav Sedov 			break;
412e085f869SStanislav Sedov 		case FLAG_M:
413e085f869SStanislav Sedov 			error = do_msr(cmdarg, dev);
414e085f869SStanislav Sedov 			break;
415e085f869SStanislav Sedov 		case FLAG_U:
416e085f869SStanislav Sedov 			error = do_update(dev);
417e085f869SStanislav Sedov 			break;
418e085f869SStanislav Sedov 		default:
419e085f869SStanislav Sedov 			usage();	/* Only one command can be selected. */
420e085f869SStanislav Sedov 	}
421e085f869SStanislav Sedov 	SLIST_FREE(&datadirs, next, free);
422e085f869SStanislav Sedov 	return (error);
423e085f869SStanislav Sedov }
424