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