xref: /freebsd/usr.sbin/cpucontrol/cpucontrol.c (revision dee401e833d262fdce8df2ef0976ca057acaeb9a)
1e085f869SStanislav Sedov /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
31de7b4b8SPedro F. Giffuni  *
4f264409aSStanislav Sedov  * Copyright (c) 2008-2011 Stanislav Sedov <stas@FreeBSD.org>.
5e085f869SStanislav Sedov  * All rights reserved.
6e085f869SStanislav Sedov  *
7e085f869SStanislav Sedov  * Redistribution and use in source and binary forms, with or without
8e085f869SStanislav Sedov  * modification, are permitted provided that the following conditions
9e085f869SStanislav Sedov  * are met:
10e085f869SStanislav Sedov  * 1. Redistributions of source code must retain the above copyright
11e085f869SStanislav Sedov  *    notice, this list of conditions and the following disclaimer.
12e085f869SStanislav Sedov  * 2. Redistributions in binary form must reproduce the above copyright
13e085f869SStanislav Sedov  *    notice, this list of conditions and the following disclaimer in the
14e085f869SStanislav Sedov  *    documentation and/or other materials provided with the distribution.
15e085f869SStanislav Sedov  *
16e085f869SStanislav Sedov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17e085f869SStanislav Sedov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18e085f869SStanislav Sedov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19e085f869SStanislav Sedov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20e085f869SStanislav Sedov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21e085f869SStanislav Sedov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22e085f869SStanislav Sedov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23e085f869SStanislav Sedov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24e085f869SStanislav Sedov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25e085f869SStanislav Sedov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26e085f869SStanislav Sedov  */
27e085f869SStanislav Sedov 
28e085f869SStanislav Sedov /*
29e085f869SStanislav Sedov  * This utility provides userland access to the cpuctl(4) pseudo-device
30e085f869SStanislav Sedov  * features.
31e085f869SStanislav Sedov  */
32e085f869SStanislav Sedov 
33e085f869SStanislav Sedov #include <sys/cdefs.h>
34e085f869SStanislav Sedov __FBSDID("$FreeBSD$");
35e085f869SStanislav Sedov 
36e085f869SStanislav Sedov #include <assert.h>
37*dee401e8SConrad Meyer #include <err.h>
38*dee401e8SConrad Meyer #include <errno.h>
39*dee401e8SConrad Meyer #include <dirent.h>
40*dee401e8SConrad Meyer #include <fcntl.h>
41e085f869SStanislav Sedov #include <stdio.h>
42e085f869SStanislav Sedov #include <stdlib.h>
43e085f869SStanislav Sedov #include <string.h>
44e085f869SStanislav Sedov #include <unistd.h>
45e085f869SStanislav Sedov #include <sysexits.h>
46e085f869SStanislav Sedov 
47e085f869SStanislav Sedov #include <sys/queue.h>
48e085f869SStanislav Sedov #include <sys/param.h>
49e085f869SStanislav Sedov #include <sys/types.h>
50*dee401e8SConrad Meyer #include <sys/mman.h>
51e085f869SStanislav Sedov #include <sys/stat.h>
52e085f869SStanislav Sedov #include <sys/ioctl.h>
53e085f869SStanislav Sedov #include <sys/cpuctl.h>
54e085f869SStanislav Sedov 
55e085f869SStanislav Sedov #include "cpucontrol.h"
56e085f869SStanislav Sedov #include "amd.h"
57e085f869SStanislav Sedov #include "intel.h"
5896ff3b75SFabien Thomas #include "via.h"
59e085f869SStanislav Sedov 
60e085f869SStanislav Sedov int	verbosity_level = 0;
61e085f869SStanislav Sedov 
62e085f869SStanislav Sedov #define	DEFAULT_DATADIR	"/usr/local/share/cpucontrol"
63e085f869SStanislav Sedov 
64e085f869SStanislav Sedov #define	FLAG_I	0x01
65e085f869SStanislav Sedov #define	FLAG_M	0x02
66e085f869SStanislav Sedov #define	FLAG_U	0x04
670539c173SKonstantin Belousov #define	FLAG_N	0x08
680530a936SKonstantin Belousov #define	FLAG_E	0x10
69e085f869SStanislav Sedov 
70b2d75854SStanislav Sedov #define	OP_INVAL	0x00
71b2d75854SStanislav Sedov #define	OP_READ		0x01
72b2d75854SStanislav Sedov #define	OP_WRITE	0x02
73b2d75854SStanislav Sedov #define	OP_OR		0x04
74b2d75854SStanislav Sedov #define	OP_AND		0x08
75b2d75854SStanislav Sedov 
76e085f869SStanislav Sedov #define	HIGH(val)	(uint32_t)(((val) >> 32) & 0xffffffff)
77e085f869SStanislav Sedov #define	LOW(val)	(uint32_t)((val) & 0xffffffff)
78e085f869SStanislav Sedov 
79e085f869SStanislav Sedov struct datadir {
80e085f869SStanislav Sedov 	const char		*path;
81e085f869SStanislav Sedov 	SLIST_ENTRY(datadir)	next;
82e085f869SStanislav Sedov };
8313e403fdSAntoine Brodin static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
84e085f869SStanislav Sedov 
85bf70beceSEd Schouten static struct ucode_handler {
86e085f869SStanislav Sedov 	ucode_probe_t *probe;
87e085f869SStanislav Sedov 	ucode_update_t *update;
88e085f869SStanislav Sedov } handlers[] = {
89e085f869SStanislav Sedov 	{ intel_probe, intel_update },
900112b52bSAndriy Gapon 	{ amd10h_probe, amd10h_update },
91e085f869SStanislav Sedov 	{ amd_probe, amd_update },
9296ff3b75SFabien Thomas 	{ via_probe, via_update },
93e085f869SStanislav Sedov };
94e085f869SStanislav Sedov #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
95e085f869SStanislav Sedov 
96e085f869SStanislav Sedov static void	usage(void);
97e085f869SStanislav Sedov static int	do_cpuid(const char *cmdarg, const char *dev);
98aa1cb750SAttilio Rao static int	do_cpuid_count(const char *cmdarg, const char *dev);
99e085f869SStanislav Sedov static int	do_msr(const char *cmdarg, const char *dev);
100e085f869SStanislav Sedov static int	do_update(const char *dev);
101e085f869SStanislav Sedov static void	datadir_add(const char *path);
102e085f869SStanislav Sedov 
103e085f869SStanislav Sedov static void __dead2
10410bc3a7fSEd Schouten usage(void)
105e085f869SStanislav Sedov {
106e085f869SStanislav Sedov 	const char *name;
107e085f869SStanislav Sedov 
108e085f869SStanislav Sedov 	name = getprogname();
109e085f869SStanislav Sedov 	if (name == NULL)
110e085f869SStanislav Sedov 		name = "cpuctl";
111e085f869SStanislav Sedov 	fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
1120530a936SKonstantin Belousov 	    "-i level | -i level,level_type | -e | -u] device\n", name);
113e085f869SStanislav Sedov 	exit(EX_USAGE);
114e085f869SStanislav Sedov }
115e085f869SStanislav Sedov 
116e085f869SStanislav Sedov static int
117e085f869SStanislav Sedov do_cpuid(const char *cmdarg, const char *dev)
118e085f869SStanislav Sedov {
119e085f869SStanislav Sedov 	unsigned int level;
120e085f869SStanislav Sedov 	cpuctl_cpuid_args_t args;
121e085f869SStanislav Sedov 	int fd, error;
122e085f869SStanislav Sedov 	char *endptr;
123e085f869SStanislav Sedov 
124e085f869SStanislav Sedov 	assert(cmdarg != NULL);
125e085f869SStanislav Sedov 	assert(dev != NULL);
126e085f869SStanislav Sedov 
127e085f869SStanislav Sedov 	level = strtoul(cmdarg, &endptr, 16);
128e085f869SStanislav Sedov 	if (*cmdarg == '\0' || *endptr != '\0') {
129e085f869SStanislav Sedov 		WARNX(0, "incorrect operand: %s", cmdarg);
130e085f869SStanislav Sedov 		usage();
131e085f869SStanislav Sedov 		/* NOTREACHED */
132e085f869SStanislav Sedov 	}
133e085f869SStanislav Sedov 
134e085f869SStanislav Sedov 	/*
135e085f869SStanislav Sedov 	 * Fill ioctl argument structure.
136e085f869SStanislav Sedov 	 */
137e085f869SStanislav Sedov 	args.level = level;
138e085f869SStanislav Sedov 	fd = open(dev, O_RDONLY);
139e085f869SStanislav Sedov 	if (fd < 0) {
140cbcc5579SStanislav Sedov 		WARN(0, "error opening %s for reading", dev);
141e085f869SStanislav Sedov 		return (1);
142e085f869SStanislav Sedov 	}
143e085f869SStanislav Sedov 	error = ioctl(fd, CPUCTL_CPUID, &args);
144e085f869SStanislav Sedov 	if (error < 0) {
145cbcc5579SStanislav Sedov 		WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
146e085f869SStanislav Sedov 		close(fd);
147e085f869SStanislav Sedov 		return (error);
148e085f869SStanislav Sedov 	}
149e085f869SStanislav Sedov 	fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
150e085f869SStanislav Sedov 	    level, args.data[0], args.data[1], args.data[2], args.data[3]);
151e085f869SStanislav Sedov 	close(fd);
152e085f869SStanislav Sedov 	return (0);
153e085f869SStanislav Sedov }
154e085f869SStanislav Sedov 
155e085f869SStanislav Sedov static int
156aa1cb750SAttilio Rao do_cpuid_count(const char *cmdarg, const char *dev)
157aa1cb750SAttilio Rao {
158aa1cb750SAttilio Rao 	char *cmdarg1, *endptr, *endptr1;
159aa1cb750SAttilio Rao 	unsigned int level, level_type;
160cef789cdSKonstantin Belousov 	cpuctl_cpuid_count_args_t args;
161aa1cb750SAttilio Rao 	int fd, error;
162aa1cb750SAttilio Rao 
163aa1cb750SAttilio Rao 	assert(cmdarg != NULL);
164aa1cb750SAttilio Rao 	assert(dev != NULL);
165aa1cb750SAttilio Rao 
166aa1cb750SAttilio Rao 	level = strtoul(cmdarg, &endptr, 16);
167aa1cb750SAttilio Rao 	if (*cmdarg == '\0' || *endptr == '\0') {
168aa1cb750SAttilio Rao 		WARNX(0, "incorrect or missing operand: %s", cmdarg);
169aa1cb750SAttilio Rao 		usage();
170aa1cb750SAttilio Rao 		/* NOTREACHED */
171aa1cb750SAttilio Rao 	}
172aa1cb750SAttilio Rao 	/* Locate the comma... */
173aa1cb750SAttilio Rao 	cmdarg1 = strstr(endptr, ",");
174aa1cb750SAttilio Rao 	/* ... and skip past it */
175aa1cb750SAttilio Rao 	cmdarg1 += 1;
176aa1cb750SAttilio Rao 	level_type = strtoul(cmdarg1, &endptr1, 16);
177aa1cb750SAttilio Rao 	if (*cmdarg1 == '\0' || *endptr1 != '\0') {
178aa1cb750SAttilio Rao 		WARNX(0, "incorrect or missing operand: %s", cmdarg);
179aa1cb750SAttilio Rao 		usage();
180aa1cb750SAttilio Rao 		/* NOTREACHED */
181aa1cb750SAttilio Rao 	}
182aa1cb750SAttilio Rao 
183aa1cb750SAttilio Rao 	/*
184aa1cb750SAttilio Rao 	 * Fill ioctl argument structure.
185aa1cb750SAttilio Rao 	 */
186aa1cb750SAttilio Rao 	args.level = level;
187aa1cb750SAttilio Rao 	args.level_type = level_type;
188aa1cb750SAttilio Rao 	fd = open(dev, O_RDONLY);
189aa1cb750SAttilio Rao 	if (fd < 0) {
190aa1cb750SAttilio Rao 		WARN(0, "error opening %s for reading", dev);
191aa1cb750SAttilio Rao 		return (1);
192aa1cb750SAttilio Rao 	}
193aa1cb750SAttilio Rao 	error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
194aa1cb750SAttilio Rao 	if (error < 0) {
195aa1cb750SAttilio Rao 		WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
196aa1cb750SAttilio Rao 		close(fd);
197aa1cb750SAttilio Rao 		return (error);
198aa1cb750SAttilio Rao 	}
199aa1cb750SAttilio Rao 	fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
200aa1cb750SAttilio Rao 	    "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
201aa1cb750SAttilio Rao 	    args.data[2], args.data[3]);
202aa1cb750SAttilio Rao 	close(fd);
203aa1cb750SAttilio Rao 	return (0);
204aa1cb750SAttilio Rao }
205aa1cb750SAttilio Rao 
206aa1cb750SAttilio Rao static int
207e085f869SStanislav Sedov do_msr(const char *cmdarg, const char *dev)
208e085f869SStanislav Sedov {
209e085f869SStanislav Sedov 	unsigned int msr;
210e085f869SStanislav Sedov 	cpuctl_msr_args_t args;
211b2d75854SStanislav Sedov 	size_t len;
212b2d75854SStanislav Sedov 	uint64_t data = 0;
213b2d75854SStanislav Sedov 	unsigned long command;
214b2d75854SStanislav Sedov 	int do_invert = 0, op;
215e085f869SStanislav Sedov 	int fd, error;
216f264409aSStanislav Sedov 	const char *command_name;
217e085f869SStanislav Sedov 	char *endptr;
218b2d75854SStanislav Sedov 	char *p;
219e085f869SStanislav Sedov 
220e085f869SStanislav Sedov 	assert(cmdarg != NULL);
221e085f869SStanislav Sedov 	assert(dev != NULL);
222b2d75854SStanislav Sedov 	len = strlen(cmdarg);
223b2d75854SStanislav Sedov 	if (len == 0) {
224b2d75854SStanislav Sedov 		WARNX(0, "MSR register expected");
225b2d75854SStanislav Sedov 		usage();
226b2d75854SStanislav Sedov 		/* NOTREACHED */
227b2d75854SStanislav Sedov 	}
228e085f869SStanislav Sedov 
229b2d75854SStanislav Sedov 	/*
230b2d75854SStanislav Sedov 	 * Parse command string.
231b2d75854SStanislav Sedov 	 */
232b2d75854SStanislav Sedov 	msr = strtoul(cmdarg, &endptr, 16);
233b2d75854SStanislav Sedov 	switch (*endptr) {
234b2d75854SStanislav Sedov 	case '\0':
235b2d75854SStanislav Sedov 		op = OP_READ;
236b2d75854SStanislav Sedov 		break;
237b2d75854SStanislav Sedov 	case '=':
238b2d75854SStanislav Sedov 		op = OP_WRITE;
239b2d75854SStanislav Sedov 		break;
240b2d75854SStanislav Sedov 	case '&':
241b2d75854SStanislav Sedov 		op = OP_AND;
242b2d75854SStanislav Sedov 		endptr++;
243b2d75854SStanislav Sedov 		break;
244b2d75854SStanislav Sedov 	case '|':
245b2d75854SStanislav Sedov 		op = OP_OR;
246b2d75854SStanislav Sedov 		endptr++;
247b2d75854SStanislav Sedov 		break;
248b2d75854SStanislav Sedov 	default:
249b2d75854SStanislav Sedov 		op = OP_INVAL;
250b2d75854SStanislav Sedov 	}
251b2d75854SStanislav Sedov 	if (op != OP_READ) {	/* Complex operation. */
252b2d75854SStanislav Sedov 		if (*endptr != '=')
253b2d75854SStanislav Sedov 			op = OP_INVAL;
254b2d75854SStanislav Sedov 		else {
255b2d75854SStanislav Sedov 			p = ++endptr;
256b2d75854SStanislav Sedov 			if (*p == '~') {
257b2d75854SStanislav Sedov 				do_invert = 1;
258b2d75854SStanislav Sedov 				p++;
259b2d75854SStanislav Sedov 			}
260b2d75854SStanislav Sedov 			data = strtoull(p, &endptr, 16);
261e085f869SStanislav Sedov 			if (*p == '\0' || *endptr != '\0') {
262b2d75854SStanislav Sedov 				WARNX(0, "argument required: %s", cmdarg);
263e085f869SStanislav Sedov 				usage();
264e085f869SStanislav Sedov 				/* NOTREACHED */
265e085f869SStanislav Sedov 			}
266e085f869SStanislav Sedov 		}
267b2d75854SStanislav Sedov 	}
268b2d75854SStanislav Sedov 	if (op == OP_INVAL) {
269b2d75854SStanislav Sedov 		WARNX(0, "invalid operator: %s", cmdarg);
270e085f869SStanislav Sedov 		usage();
271e085f869SStanislav Sedov 		/* NOTREACHED */
272e085f869SStanislav Sedov 	}
273e085f869SStanislav Sedov 
274e085f869SStanislav Sedov 	/*
275e085f869SStanislav Sedov 	 * Fill ioctl argument structure.
276e085f869SStanislav Sedov 	 */
277e085f869SStanislav Sedov 	args.msr = msr;
278b2d75854SStanislav Sedov 	if ((do_invert != 0) ^ (op == OP_AND))
279b2d75854SStanislav Sedov 		args.data = ~data;
280b2d75854SStanislav Sedov 	else
281b2d75854SStanislav Sedov 		args.data = data;
282b2d75854SStanislav Sedov 	switch (op) {
283b2d75854SStanislav Sedov 	case OP_READ:
284b2d75854SStanislav Sedov 		command = CPUCTL_RDMSR;
2853b232eb6SStanislav Sedov 		command_name = "RDMSR";
286b2d75854SStanislav Sedov 		break;
287b2d75854SStanislav Sedov 	case OP_WRITE:
288b2d75854SStanislav Sedov 		command = CPUCTL_WRMSR;
2893b232eb6SStanislav Sedov 		command_name = "WRMSR";
290b2d75854SStanislav Sedov 		break;
291b2d75854SStanislav Sedov 	case OP_OR:
292b2d75854SStanislav Sedov 		command = CPUCTL_MSRSBIT;
2933b232eb6SStanislav Sedov 		command_name = "MSRSBIT";
294b2d75854SStanislav Sedov 		break;
295b2d75854SStanislav Sedov 	case OP_AND:
296b2d75854SStanislav Sedov 		command = CPUCTL_MSRCBIT;
2973b232eb6SStanislav Sedov 		command_name = "MSRCBIT";
298b2d75854SStanislav Sedov 		break;
299b2d75854SStanislav Sedov 	default:
300b2d75854SStanislav Sedov 		abort();
301b2d75854SStanislav Sedov 	}
302b2d75854SStanislav Sedov 	fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
303e085f869SStanislav Sedov 	if (fd < 0) {
304cbcc5579SStanislav Sedov 		WARN(0, "error opening %s for %s", dev,
305b2d75854SStanislav Sedov 		    op == OP_READ ? "reading" : "writing");
306e085f869SStanislav Sedov 		return (1);
307e085f869SStanislav Sedov 	}
308b2d75854SStanislav Sedov 	error = ioctl(fd, command, &args);
309e085f869SStanislav Sedov 	if (error < 0) {
3103b232eb6SStanislav Sedov 		WARN(0, "ioctl(%s, CPUCTL_%s (%lu))", dev, command_name, command);
311e085f869SStanislav Sedov 		close(fd);
312e085f869SStanislav Sedov 		return (1);
313e085f869SStanislav Sedov 	}
314b2d75854SStanislav Sedov 	if (op == OP_READ)
315e085f869SStanislav Sedov 		fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
316e085f869SStanislav Sedov 		    HIGH(args.data), LOW(args.data));
317e085f869SStanislav Sedov 	close(fd);
318e085f869SStanislav Sedov 	return (0);
319e085f869SStanislav Sedov }
320e085f869SStanislav Sedov 
321e085f869SStanislav Sedov static int
3220530a936SKonstantin Belousov do_eval_cpu_features(const char *dev)
3230530a936SKonstantin Belousov {
3240530a936SKonstantin Belousov 	int fd, error;
3250530a936SKonstantin Belousov 
3260530a936SKonstantin Belousov 	assert(dev != NULL);
3270530a936SKonstantin Belousov 
3280530a936SKonstantin Belousov 	fd = open(dev, O_RDWR);
3290530a936SKonstantin Belousov 	if (fd < 0) {
3300530a936SKonstantin Belousov 		WARN(0, "error opening %s for writing", dev);
3310530a936SKonstantin Belousov 		return (1);
3320530a936SKonstantin Belousov 	}
3330530a936SKonstantin Belousov 	error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
3340530a936SKonstantin Belousov 	if (error < 0)
3350530a936SKonstantin Belousov 		WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
3360530a936SKonstantin Belousov 	close(fd);
3370530a936SKonstantin Belousov 	return (error);
3380530a936SKonstantin Belousov }
3390530a936SKonstantin Belousov 
3400530a936SKonstantin Belousov static int
341*dee401e8SConrad Meyer try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
342*dee401e8SConrad Meyer     const char *fname, struct ucode_handler *handler)
343*dee401e8SConrad Meyer {
344*dee401e8SConrad Meyer 	struct ucode_update_params parm;
345*dee401e8SConrad Meyer 	struct stat st;
346*dee401e8SConrad Meyer 	char *fw_path;
347*dee401e8SConrad Meyer 	void *fw_map;
348*dee401e8SConrad Meyer 	int fwfd, rc;
349*dee401e8SConrad Meyer 
350*dee401e8SConrad Meyer 	rc = 0;
351*dee401e8SConrad Meyer 	fw_path = NULL;
352*dee401e8SConrad Meyer 	fw_map = MAP_FAILED;
353*dee401e8SConrad Meyer 	fwfd = openat(fwdfd, fname, O_RDONLY);
354*dee401e8SConrad Meyer 	if (fwfd < 0) {
355*dee401e8SConrad Meyer 		WARN(0, "openat(%s, %s)", dpath, fname);
356*dee401e8SConrad Meyer 		goto out;
357*dee401e8SConrad Meyer 	}
358*dee401e8SConrad Meyer 
359*dee401e8SConrad Meyer 	rc = asprintf(&fw_path, "%s/%s", dpath, fname);
360*dee401e8SConrad Meyer 	if (rc == -1) {
361*dee401e8SConrad Meyer 		WARNX(0, "out of memory");
362*dee401e8SConrad Meyer 		rc = ENOMEM;
363*dee401e8SConrad Meyer 		goto out;
364*dee401e8SConrad Meyer 	}
365*dee401e8SConrad Meyer 
366*dee401e8SConrad Meyer 	rc = fstat(fwfd, &st);
367*dee401e8SConrad Meyer 	if (rc != 0) {
368*dee401e8SConrad Meyer 		WARN(0, "fstat(%s)", fw_path);
369*dee401e8SConrad Meyer 		rc = 0;
370*dee401e8SConrad Meyer 		goto out;
371*dee401e8SConrad Meyer 	}
372*dee401e8SConrad Meyer 	if (st.st_size <= 0) {
373*dee401e8SConrad Meyer 		WARN(0, "%s: empty", fw_path);
374*dee401e8SConrad Meyer 		goto out;
375*dee401e8SConrad Meyer 	}
376*dee401e8SConrad Meyer 
377*dee401e8SConrad Meyer 	fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
378*dee401e8SConrad Meyer 	if (fw_map == MAP_FAILED) {
379*dee401e8SConrad Meyer 		WARN(0, "mmap(%s)", fw_path);
380*dee401e8SConrad Meyer 		goto out;
381*dee401e8SConrad Meyer 	}
382*dee401e8SConrad Meyer 
383*dee401e8SConrad Meyer 
384*dee401e8SConrad Meyer 	memset(&parm, 0, sizeof(parm));
385*dee401e8SConrad Meyer 	parm.devfd = devfd;
386*dee401e8SConrad Meyer 	parm.fwimage = fw_map;
387*dee401e8SConrad Meyer 	parm.fwsize = st.st_size;
388*dee401e8SConrad Meyer 	parm.dev_path = dev_path;
389*dee401e8SConrad Meyer 	parm.fw_path = fw_path;
390*dee401e8SConrad Meyer 
391*dee401e8SConrad Meyer 	handler->update(&parm);
392*dee401e8SConrad Meyer 
393*dee401e8SConrad Meyer out:
394*dee401e8SConrad Meyer 	if (fw_map != MAP_FAILED)
395*dee401e8SConrad Meyer 		munmap(fw_map, st.st_size);
396*dee401e8SConrad Meyer 	free(fw_path);
397*dee401e8SConrad Meyer 	if (fwfd >= 0)
398*dee401e8SConrad Meyer 		close(fwfd);
399*dee401e8SConrad Meyer 	return (rc);
400*dee401e8SConrad Meyer }
401*dee401e8SConrad Meyer 
402*dee401e8SConrad Meyer static int
403e085f869SStanislav Sedov do_update(const char *dev)
404e085f869SStanislav Sedov {
405*dee401e8SConrad Meyer 	int fd, fwdfd;
406e085f869SStanislav Sedov 	unsigned int i;
407e085f869SStanislav Sedov 	int error;
408e085f869SStanislav Sedov 	struct ucode_handler *handler;
409e085f869SStanislav Sedov 	struct datadir *dir;
4100bb2aabfSGleb Kurtsou 	DIR *dirp;
411e085f869SStanislav Sedov 	struct dirent *direntry;
412e085f869SStanislav Sedov 
413e085f869SStanislav Sedov 	fd = open(dev, O_RDONLY);
414e085f869SStanislav Sedov 	if (fd < 0) {
415cbcc5579SStanislav Sedov 		WARN(0, "error opening %s for reading", dev);
416e085f869SStanislav Sedov 		return (1);
417e085f869SStanislav Sedov 	}
418e085f869SStanislav Sedov 
419e085f869SStanislav Sedov 	/*
420*dee401e8SConrad Meyer 	 * Find the appropriate handler for CPU.
421e085f869SStanislav Sedov 	 */
422e085f869SStanislav Sedov 	for (i = 0; i < NHANDLERS; i++)
423e085f869SStanislav Sedov 		if (handlers[i].probe(fd) == 0)
424e085f869SStanislav Sedov 			break;
425e085f869SStanislav Sedov 	if (i < NHANDLERS)
426e085f869SStanislav Sedov 		handler = &handlers[i];
427e085f869SStanislav Sedov 	else {
428*dee401e8SConrad Meyer 		WARNX(0, "cannot find the appropriate handler for %s", dev);
429e085f869SStanislav Sedov 		close(fd);
430e085f869SStanislav Sedov 		return (1);
431e085f869SStanislav Sedov 	}
432e085f869SStanislav Sedov 	close(fd);
433e085f869SStanislav Sedov 
434*dee401e8SConrad Meyer 	fd = open(dev, O_RDWR);
435*dee401e8SConrad Meyer 	if (fd < 0) {
436*dee401e8SConrad Meyer 		WARN(0, "error opening %s for writing", dev);
437*dee401e8SConrad Meyer 		return (1);
438*dee401e8SConrad Meyer 	}
439*dee401e8SConrad Meyer 
440e085f869SStanislav Sedov 	/*
441e085f869SStanislav Sedov 	 * Process every image in specified data directories.
442e085f869SStanislav Sedov 	 */
443e085f869SStanislav Sedov 	SLIST_FOREACH(dir, &datadirs, next) {
444*dee401e8SConrad Meyer 		fwdfd = open(dir->path, O_RDONLY);
445*dee401e8SConrad Meyer 		if (fwdfd < 0) {
446*dee401e8SConrad Meyer 			WARN(1, "skipping directory %s: not accessible", dir->path);
447e085f869SStanislav Sedov 			continue;
448e085f869SStanislav Sedov 		}
449*dee401e8SConrad Meyer 		dirp = fdopendir(fwdfd);
450*dee401e8SConrad Meyer 		if (dirp == NULL) {
451*dee401e8SConrad Meyer 			WARNX(0, "out of memory");
452*dee401e8SConrad Meyer 			close(fwdfd);
453*dee401e8SConrad Meyer 			close(fd);
454*dee401e8SConrad Meyer 			return (1);
455*dee401e8SConrad Meyer 		}
456*dee401e8SConrad Meyer 
4570bb2aabfSGleb Kurtsou 		while ((direntry = readdir(dirp)) != NULL) {
458e085f869SStanislav Sedov 			if (direntry->d_namlen == 0)
459e085f869SStanislav Sedov 				continue;
460*dee401e8SConrad Meyer 			if (direntry->d_type == DT_DIR)
461e085f869SStanislav Sedov 				continue;
462*dee401e8SConrad Meyer 
463*dee401e8SConrad Meyer 			error = try_a_fw_image(dev, fd, fwdfd, dir->path,
464*dee401e8SConrad Meyer 			    direntry->d_name, handler);
465*dee401e8SConrad Meyer 			if (error != 0) {
466*dee401e8SConrad Meyer 				closedir(dirp);
467*dee401e8SConrad Meyer 				close(fd);
468*dee401e8SConrad Meyer 				return (1);
469e085f869SStanislav Sedov 			}
470e085f869SStanislav Sedov 		}
4710bb2aabfSGleb Kurtsou 		error = closedir(dirp);
472e085f869SStanislav Sedov 		if (error != 0)
473e085f869SStanislav Sedov 			WARN(0, "closedir(%s)", dir->path);
474e085f869SStanislav Sedov 	}
475*dee401e8SConrad Meyer 	close(fd);
476e085f869SStanislav Sedov 	return (0);
477e085f869SStanislav Sedov }
478e085f869SStanislav Sedov 
479e085f869SStanislav Sedov /*
480e085f869SStanislav Sedov  * Add new data directory to the search list.
481e085f869SStanislav Sedov  */
482e085f869SStanislav Sedov static void
483e085f869SStanislav Sedov datadir_add(const char *path)
484e085f869SStanislav Sedov {
485e085f869SStanislav Sedov 	struct datadir *newdir;
486e085f869SStanislav Sedov 
487e085f869SStanislav Sedov 	newdir = (struct datadir *)malloc(sizeof(*newdir));
488e085f869SStanislav Sedov 	if (newdir == NULL)
489e085f869SStanislav Sedov 		err(EX_OSERR, "cannot allocate memory");
490e085f869SStanislav Sedov 	newdir->path = path;
491e085f869SStanislav Sedov 	SLIST_INSERT_HEAD(&datadirs, newdir, next);
492e085f869SStanislav Sedov }
493e085f869SStanislav Sedov 
494e085f869SStanislav Sedov int
495e085f869SStanislav Sedov main(int argc, char *argv[])
496e085f869SStanislav Sedov {
497*dee401e8SConrad Meyer 	struct datadir *elm;
498e085f869SStanislav Sedov 	int c, flags;
499e085f869SStanislav Sedov 	const char *cmdarg;
500e085f869SStanislav Sedov 	const char *dev;
501e085f869SStanislav Sedov 	int error;
502e085f869SStanislav Sedov 
503e085f869SStanislav Sedov 	flags = 0;
504e085f869SStanislav Sedov 	error = 0;
505e085f869SStanislav Sedov 	cmdarg = "";	/* To keep gcc3 happy. */
506e085f869SStanislav Sedov 
5070530a936SKonstantin Belousov 	while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
508e085f869SStanislav Sedov 		switch (c) {
509e085f869SStanislav Sedov 		case 'd':
510e085f869SStanislav Sedov 			datadir_add(optarg);
511e085f869SStanislav Sedov 			break;
5120530a936SKonstantin Belousov 		case 'e':
5130530a936SKonstantin Belousov 			flags |= FLAG_E;
5140530a936SKonstantin Belousov 			break;
515e085f869SStanislav Sedov 		case 'i':
516e085f869SStanislav Sedov 			flags |= FLAG_I;
517e085f869SStanislav Sedov 			cmdarg = optarg;
518e085f869SStanislav Sedov 			break;
519e085f869SStanislav Sedov 		case 'm':
520e085f869SStanislav Sedov 			flags |= FLAG_M;
521e085f869SStanislav Sedov 			cmdarg = optarg;
522e085f869SStanislav Sedov 			break;
5230539c173SKonstantin Belousov 		case 'n':
5240539c173SKonstantin Belousov 			flags |= FLAG_N;
5250539c173SKonstantin Belousov 			break;
526e085f869SStanislav Sedov 		case 'u':
527e085f869SStanislav Sedov 			flags |= FLAG_U;
528e085f869SStanislav Sedov 			break;
529e085f869SStanislav Sedov 		case 'v':
530e085f869SStanislav Sedov 			verbosity_level++;
531e085f869SStanislav Sedov 			break;
532e085f869SStanislav Sedov 		case 'h':
533e085f869SStanislav Sedov 			/* FALLTHROUGH */
534e085f869SStanislav Sedov 		default:
535e085f869SStanislav Sedov 			usage();
536e085f869SStanislav Sedov 			/* NOTREACHED */
537e085f869SStanislav Sedov 		}
538e085f869SStanislav Sedov 	}
539e085f869SStanislav Sedov 	argc -= optind;
540e085f869SStanislav Sedov 	argv += optind;
541e085f869SStanislav Sedov 	if (argc < 1) {
542e085f869SStanislav Sedov 		usage();
543e085f869SStanislav Sedov 		/* NOTREACHED */
544e085f869SStanislav Sedov 	}
5450539c173SKonstantin Belousov 	if ((flags & FLAG_N) == 0)
5460539c173SKonstantin Belousov 		datadir_add(DEFAULT_DATADIR);
547e085f869SStanislav Sedov 	dev = argv[0];
5480530a936SKonstantin Belousov 	c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
549e085f869SStanislav Sedov 	switch (c) {
550e085f869SStanislav Sedov 	case FLAG_I:
551aa1cb750SAttilio Rao 		if (strstr(cmdarg, ",") != NULL)
552aa1cb750SAttilio Rao 			error = do_cpuid_count(cmdarg, dev);
553aa1cb750SAttilio Rao 		else
554e085f869SStanislav Sedov 			error = do_cpuid(cmdarg, dev);
555e085f869SStanislav Sedov 		break;
556e085f869SStanislav Sedov 	case FLAG_M:
557e085f869SStanislav Sedov 		error = do_msr(cmdarg, dev);
558e085f869SStanislav Sedov 		break;
559e085f869SStanislav Sedov 	case FLAG_U:
560e085f869SStanislav Sedov 		error = do_update(dev);
561e085f869SStanislav Sedov 		break;
5620530a936SKonstantin Belousov 	case FLAG_E:
5630530a936SKonstantin Belousov 		error = do_eval_cpu_features(dev);
5640530a936SKonstantin Belousov 		break;
565e085f869SStanislav Sedov 	default:
566e085f869SStanislav Sedov 		usage();	/* Only one command can be selected. */
567e085f869SStanislav Sedov 	}
568*dee401e8SConrad Meyer 	while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
569*dee401e8SConrad Meyer 		SLIST_REMOVE_HEAD(&datadirs, next);
570*dee401e8SConrad Meyer 		free(elm);
571*dee401e8SConrad Meyer 	}
57278a6a430SKonstantin Belousov 	return (error == 0 ? 0 : 1);
573e085f869SStanislav Sedov }
574