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