1e085f869SStanislav Sedov /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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 #include <assert.h>
35dee401e8SConrad Meyer #include <err.h>
36dee401e8SConrad Meyer #include <errno.h>
37dee401e8SConrad Meyer #include <dirent.h>
38dee401e8SConrad Meyer #include <fcntl.h>
391f474190SStefan Eßer #include <paths.h>
40e085f869SStanislav Sedov #include <stdio.h>
41e085f869SStanislav Sedov #include <stdlib.h>
42e085f869SStanislav Sedov #include <string.h>
43e085f869SStanislav Sedov #include <unistd.h>
44e085f869SStanislav Sedov #include <sysexits.h>
45e085f869SStanislav Sedov
46e085f869SStanislav Sedov #include <sys/queue.h>
47e085f869SStanislav Sedov #include <sys/param.h>
48e085f869SStanislav Sedov #include <sys/types.h>
49dee401e8SConrad Meyer #include <sys/mman.h>
50e085f869SStanislav Sedov #include <sys/stat.h>
51e085f869SStanislav Sedov #include <sys/ioctl.h>
52e085f869SStanislav Sedov #include <sys/cpuctl.h>
53e085f869SStanislav Sedov
54e085f869SStanislav Sedov #include "cpucontrol.h"
55e085f869SStanislav Sedov #include "amd.h"
56e085f869SStanislav Sedov #include "intel.h"
5796ff3b75SFabien Thomas #include "via.h"
58e085f869SStanislav Sedov
59e085f869SStanislav Sedov int verbosity_level = 0;
60e085f869SStanislav Sedov
611f474190SStefan Eßer #define DEFAULT_DATADIR _PATH_LOCALBASE "/share/cpucontrol"
62e085f869SStanislav Sedov
63e085f869SStanislav Sedov #define FLAG_I 0x01
64e085f869SStanislav Sedov #define FLAG_M 0x02
65e085f869SStanislav Sedov #define FLAG_U 0x04
660539c173SKonstantin Belousov #define FLAG_N 0x08
670530a936SKonstantin Belousov #define FLAG_E 0x10
68e085f869SStanislav Sedov
69b2d75854SStanislav Sedov #define OP_INVAL 0x00
70b2d75854SStanislav Sedov #define OP_READ 0x01
71b2d75854SStanislav Sedov #define OP_WRITE 0x02
72b2d75854SStanislav Sedov #define OP_OR 0x04
73b2d75854SStanislav Sedov #define OP_AND 0x08
74b2d75854SStanislav Sedov
75e085f869SStanislav Sedov #define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
76e085f869SStanislav Sedov #define LOW(val) (uint32_t)((val) & 0xffffffff)
77e085f869SStanislav Sedov
78e085f869SStanislav Sedov struct datadir {
79e085f869SStanislav Sedov const char *path;
80e085f869SStanislav Sedov SLIST_ENTRY(datadir) next;
81e085f869SStanislav Sedov };
8213e403fdSAntoine Brodin static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
83e085f869SStanislav Sedov
84bf70beceSEd Schouten static struct ucode_handler {
85e085f869SStanislav Sedov ucode_probe_t *probe;
86e085f869SStanislav Sedov ucode_update_t *update;
87e085f869SStanislav Sedov } handlers[] = {
88e085f869SStanislav Sedov { intel_probe, intel_update },
890112b52bSAndriy Gapon { amd10h_probe, amd10h_update },
90e085f869SStanislav Sedov { amd_probe, amd_update },
9196ff3b75SFabien Thomas { via_probe, via_update },
92e085f869SStanislav Sedov };
93e085f869SStanislav Sedov #define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
94e085f869SStanislav Sedov
95e085f869SStanislav Sedov static void usage(void);
96e085f869SStanislav Sedov static int do_cpuid(const char *cmdarg, const char *dev);
97aa1cb750SAttilio Rao static int do_cpuid_count(const char *cmdarg, const char *dev);
98e085f869SStanislav Sedov static int do_msr(const char *cmdarg, const char *dev);
99e085f869SStanislav Sedov static int do_update(const char *dev);
100e085f869SStanislav Sedov static void datadir_add(const char *path);
101e085f869SStanislav Sedov
102e085f869SStanislav Sedov static void __dead2
usage(void)10310bc3a7fSEd Schouten usage(void)
104e085f869SStanislav Sedov {
105e085f869SStanislav Sedov const char *name;
106e085f869SStanislav Sedov
107e085f869SStanislav Sedov name = getprogname();
108e085f869SStanislav Sedov if (name == NULL)
109e085f869SStanislav Sedov name = "cpuctl";
110e085f869SStanislav Sedov fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
1110530a936SKonstantin Belousov "-i level | -i level,level_type | -e | -u] device\n", name);
112e085f869SStanislav Sedov exit(EX_USAGE);
113e085f869SStanislav Sedov }
114e085f869SStanislav Sedov
115e085f869SStanislav Sedov static int
do_cpuid(const char * cmdarg,const char * dev)116e085f869SStanislav Sedov do_cpuid(const char *cmdarg, const char *dev)
117e085f869SStanislav Sedov {
118e085f869SStanislav Sedov unsigned int level;
119e085f869SStanislav Sedov cpuctl_cpuid_args_t args;
120e085f869SStanislav Sedov int fd, error;
121e085f869SStanislav Sedov char *endptr;
122e085f869SStanislav Sedov
123e085f869SStanislav Sedov assert(cmdarg != NULL);
124e085f869SStanislav Sedov assert(dev != NULL);
125e085f869SStanislav Sedov
126e085f869SStanislav Sedov level = strtoul(cmdarg, &endptr, 16);
127e085f869SStanislav Sedov if (*cmdarg == '\0' || *endptr != '\0') {
128e085f869SStanislav Sedov WARNX(0, "incorrect operand: %s", cmdarg);
129e085f869SStanislav Sedov usage();
130e085f869SStanislav Sedov /* NOTREACHED */
131e085f869SStanislav Sedov }
132e085f869SStanislav Sedov
133e085f869SStanislav Sedov /*
134e085f869SStanislav Sedov * Fill ioctl argument structure.
135e085f869SStanislav Sedov */
136e085f869SStanislav Sedov args.level = level;
137e085f869SStanislav Sedov fd = open(dev, O_RDONLY);
138e085f869SStanislav Sedov if (fd < 0) {
139cbcc5579SStanislav Sedov WARN(0, "error opening %s for reading", dev);
140e085f869SStanislav Sedov return (1);
141e085f869SStanislav Sedov }
142e085f869SStanislav Sedov error = ioctl(fd, CPUCTL_CPUID, &args);
143e085f869SStanislav Sedov if (error < 0) {
144cbcc5579SStanislav Sedov WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
145e085f869SStanislav Sedov close(fd);
146e085f869SStanislav Sedov return (error);
147e085f869SStanislav Sedov }
148e085f869SStanislav Sedov fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
149e085f869SStanislav Sedov level, args.data[0], args.data[1], args.data[2], args.data[3]);
150e085f869SStanislav Sedov close(fd);
151e085f869SStanislav Sedov return (0);
152e085f869SStanislav Sedov }
153e085f869SStanislav Sedov
154e085f869SStanislav Sedov static int
do_cpuid_count(const char * cmdarg,const char * dev)155aa1cb750SAttilio Rao do_cpuid_count(const char *cmdarg, const char *dev)
156aa1cb750SAttilio Rao {
157aa1cb750SAttilio Rao char *cmdarg1, *endptr, *endptr1;
158aa1cb750SAttilio Rao unsigned int level, level_type;
159cef789cdSKonstantin Belousov cpuctl_cpuid_count_args_t args;
160aa1cb750SAttilio Rao int fd, error;
161aa1cb750SAttilio Rao
162aa1cb750SAttilio Rao assert(cmdarg != NULL);
163aa1cb750SAttilio Rao assert(dev != NULL);
164aa1cb750SAttilio Rao
165aa1cb750SAttilio Rao level = strtoul(cmdarg, &endptr, 16);
166aa1cb750SAttilio Rao if (*cmdarg == '\0' || *endptr == '\0') {
167aa1cb750SAttilio Rao WARNX(0, "incorrect or missing operand: %s", cmdarg);
168aa1cb750SAttilio Rao usage();
169aa1cb750SAttilio Rao /* NOTREACHED */
170aa1cb750SAttilio Rao }
171aa1cb750SAttilio Rao /* Locate the comma... */
172aa1cb750SAttilio Rao cmdarg1 = strstr(endptr, ",");
173aa1cb750SAttilio Rao /* ... and skip past it */
174aa1cb750SAttilio Rao cmdarg1 += 1;
175aa1cb750SAttilio Rao level_type = strtoul(cmdarg1, &endptr1, 16);
176aa1cb750SAttilio Rao if (*cmdarg1 == '\0' || *endptr1 != '\0') {
177aa1cb750SAttilio Rao WARNX(0, "incorrect or missing operand: %s", cmdarg);
178aa1cb750SAttilio Rao usage();
179aa1cb750SAttilio Rao /* NOTREACHED */
180aa1cb750SAttilio Rao }
181aa1cb750SAttilio Rao
182aa1cb750SAttilio Rao /*
183aa1cb750SAttilio Rao * Fill ioctl argument structure.
184aa1cb750SAttilio Rao */
185aa1cb750SAttilio Rao args.level = level;
186aa1cb750SAttilio Rao args.level_type = level_type;
187aa1cb750SAttilio Rao fd = open(dev, O_RDONLY);
188aa1cb750SAttilio Rao if (fd < 0) {
189aa1cb750SAttilio Rao WARN(0, "error opening %s for reading", dev);
190aa1cb750SAttilio Rao return (1);
191aa1cb750SAttilio Rao }
192aa1cb750SAttilio Rao error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
193aa1cb750SAttilio Rao if (error < 0) {
194aa1cb750SAttilio Rao WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
195aa1cb750SAttilio Rao close(fd);
196aa1cb750SAttilio Rao return (error);
197aa1cb750SAttilio Rao }
198aa1cb750SAttilio Rao fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
199aa1cb750SAttilio Rao "0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
200aa1cb750SAttilio Rao args.data[2], args.data[3]);
201aa1cb750SAttilio Rao close(fd);
202aa1cb750SAttilio Rao return (0);
203aa1cb750SAttilio Rao }
204aa1cb750SAttilio Rao
205aa1cb750SAttilio Rao static int
do_msr(const char * cmdarg,const char * dev)206e085f869SStanislav Sedov do_msr(const char *cmdarg, const char *dev)
207e085f869SStanislav Sedov {
208e085f869SStanislav Sedov unsigned int msr;
209e085f869SStanislav Sedov cpuctl_msr_args_t args;
210b2d75854SStanislav Sedov size_t len;
211b2d75854SStanislav Sedov uint64_t data = 0;
212b2d75854SStanislav Sedov unsigned long command;
213b2d75854SStanislav Sedov int do_invert = 0, op;
214e085f869SStanislav Sedov int fd, error;
215f264409aSStanislav Sedov const char *command_name;
216e085f869SStanislav Sedov char *endptr;
217b2d75854SStanislav Sedov char *p;
218e085f869SStanislav Sedov
219e085f869SStanislav Sedov assert(cmdarg != NULL);
220e085f869SStanislav Sedov assert(dev != NULL);
221b2d75854SStanislav Sedov len = strlen(cmdarg);
222b2d75854SStanislav Sedov if (len == 0) {
223b2d75854SStanislav Sedov WARNX(0, "MSR register expected");
224b2d75854SStanislav Sedov usage();
225b2d75854SStanislav Sedov /* NOTREACHED */
226b2d75854SStanislav Sedov }
227e085f869SStanislav Sedov
228b2d75854SStanislav Sedov /*
229b2d75854SStanislav Sedov * Parse command string.
230b2d75854SStanislav Sedov */
231b2d75854SStanislav Sedov msr = strtoul(cmdarg, &endptr, 16);
232b2d75854SStanislav Sedov switch (*endptr) {
233b2d75854SStanislav Sedov case '\0':
234b2d75854SStanislav Sedov op = OP_READ;
235b2d75854SStanislav Sedov break;
236b2d75854SStanislav Sedov case '=':
237b2d75854SStanislav Sedov op = OP_WRITE;
238b2d75854SStanislav Sedov break;
239b2d75854SStanislav Sedov case '&':
240b2d75854SStanislav Sedov op = OP_AND;
241b2d75854SStanislav Sedov endptr++;
242b2d75854SStanislav Sedov break;
243b2d75854SStanislav Sedov case '|':
244b2d75854SStanislav Sedov op = OP_OR;
245b2d75854SStanislav Sedov endptr++;
246b2d75854SStanislav Sedov break;
247b2d75854SStanislav Sedov default:
248b2d75854SStanislav Sedov op = OP_INVAL;
249b2d75854SStanislav Sedov }
250b2d75854SStanislav Sedov if (op != OP_READ) { /* Complex operation. */
251b2d75854SStanislav Sedov if (*endptr != '=')
252b2d75854SStanislav Sedov op = OP_INVAL;
253b2d75854SStanislav Sedov else {
254b2d75854SStanislav Sedov p = ++endptr;
255b2d75854SStanislav Sedov if (*p == '~') {
256b2d75854SStanislav Sedov do_invert = 1;
257b2d75854SStanislav Sedov p++;
258b2d75854SStanislav Sedov }
259b2d75854SStanislav Sedov data = strtoull(p, &endptr, 16);
260e085f869SStanislav Sedov if (*p == '\0' || *endptr != '\0') {
261b2d75854SStanislav Sedov WARNX(0, "argument required: %s", cmdarg);
262e085f869SStanislav Sedov usage();
263e085f869SStanislav Sedov /* NOTREACHED */
264e085f869SStanislav Sedov }
265e085f869SStanislav Sedov }
266b2d75854SStanislav Sedov }
267b2d75854SStanislav Sedov if (op == OP_INVAL) {
268b2d75854SStanislav Sedov WARNX(0, "invalid operator: %s", cmdarg);
269e085f869SStanislav Sedov usage();
270e085f869SStanislav Sedov /* NOTREACHED */
271e085f869SStanislav Sedov }
272e085f869SStanislav Sedov
273e085f869SStanislav Sedov /*
274e085f869SStanislav Sedov * Fill ioctl argument structure.
275e085f869SStanislav Sedov */
276e085f869SStanislav Sedov args.msr = msr;
277b2d75854SStanislav Sedov if ((do_invert != 0) ^ (op == OP_AND))
278b2d75854SStanislav Sedov args.data = ~data;
279b2d75854SStanislav Sedov else
280b2d75854SStanislav Sedov args.data = data;
281b2d75854SStanislav Sedov switch (op) {
282b2d75854SStanislav Sedov case OP_READ:
283b2d75854SStanislav Sedov command = CPUCTL_RDMSR;
2843b232eb6SStanislav Sedov command_name = "RDMSR";
285b2d75854SStanislav Sedov break;
286b2d75854SStanislav Sedov case OP_WRITE:
287b2d75854SStanislav Sedov command = CPUCTL_WRMSR;
2883b232eb6SStanislav Sedov command_name = "WRMSR";
289b2d75854SStanislav Sedov break;
290b2d75854SStanislav Sedov case OP_OR:
291b2d75854SStanislav Sedov command = CPUCTL_MSRSBIT;
2923b232eb6SStanislav Sedov command_name = "MSRSBIT";
293b2d75854SStanislav Sedov break;
294b2d75854SStanislav Sedov case OP_AND:
295b2d75854SStanislav Sedov command = CPUCTL_MSRCBIT;
2963b232eb6SStanislav Sedov command_name = "MSRCBIT";
297b2d75854SStanislav Sedov break;
298b2d75854SStanislav Sedov default:
299b2d75854SStanislav Sedov abort();
300b2d75854SStanislav Sedov }
301b2d75854SStanislav Sedov fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
302e085f869SStanislav Sedov if (fd < 0) {
303cbcc5579SStanislav Sedov WARN(0, "error opening %s for %s", dev,
304b2d75854SStanislav Sedov op == OP_READ ? "reading" : "writing");
305e085f869SStanislav Sedov return (1);
306e085f869SStanislav Sedov }
307b2d75854SStanislav Sedov error = ioctl(fd, command, &args);
308e085f869SStanislav Sedov if (error < 0) {
3097672c254SKonstantin Belousov WARN(0, "ioctl(%s, CPUCTL_%s (%#x))", dev, command_name, msr);
310e085f869SStanislav Sedov close(fd);
311e085f869SStanislav Sedov return (1);
312e085f869SStanislav Sedov }
313b2d75854SStanislav Sedov if (op == OP_READ)
314e085f869SStanislav Sedov fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
315e085f869SStanislav Sedov HIGH(args.data), LOW(args.data));
316e085f869SStanislav Sedov close(fd);
317e085f869SStanislav Sedov return (0);
318e085f869SStanislav Sedov }
319e085f869SStanislav Sedov
320e085f869SStanislav Sedov static int
do_eval_cpu_features(const char * dev)3210530a936SKonstantin Belousov do_eval_cpu_features(const char *dev)
3220530a936SKonstantin Belousov {
3230530a936SKonstantin Belousov int fd, error;
3240530a936SKonstantin Belousov
3250530a936SKonstantin Belousov assert(dev != NULL);
3260530a936SKonstantin Belousov
3270530a936SKonstantin Belousov fd = open(dev, O_RDWR);
3280530a936SKonstantin Belousov if (fd < 0) {
3290530a936SKonstantin Belousov WARN(0, "error opening %s for writing", dev);
3300530a936SKonstantin Belousov return (1);
3310530a936SKonstantin Belousov }
3320530a936SKonstantin Belousov error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
3330530a936SKonstantin Belousov if (error < 0)
3340530a936SKonstantin Belousov WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
3350530a936SKonstantin Belousov close(fd);
3360530a936SKonstantin Belousov return (error);
3370530a936SKonstantin Belousov }
3380530a936SKonstantin Belousov
3390530a936SKonstantin Belousov static int
try_a_fw_image(const char * dev_path,int devfd,int fwdfd,const char * dpath,const char * fname,struct ucode_handler * handler)340dee401e8SConrad Meyer try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
341dee401e8SConrad Meyer const char *fname, struct ucode_handler *handler)
342dee401e8SConrad Meyer {
343dee401e8SConrad Meyer struct ucode_update_params parm;
344dee401e8SConrad Meyer struct stat st;
345dee401e8SConrad Meyer char *fw_path;
346dee401e8SConrad Meyer void *fw_map;
347dee401e8SConrad Meyer int fwfd, rc;
348dee401e8SConrad Meyer
349dee401e8SConrad Meyer rc = 0;
350dee401e8SConrad Meyer fw_path = NULL;
351dee401e8SConrad Meyer fw_map = MAP_FAILED;
352dee401e8SConrad Meyer fwfd = openat(fwdfd, fname, O_RDONLY);
353dee401e8SConrad Meyer if (fwfd < 0) {
354dee401e8SConrad Meyer WARN(0, "openat(%s, %s)", dpath, fname);
355dee401e8SConrad Meyer goto out;
356dee401e8SConrad Meyer }
357dee401e8SConrad Meyer
358dee401e8SConrad Meyer rc = asprintf(&fw_path, "%s/%s", dpath, fname);
359dee401e8SConrad Meyer if (rc == -1) {
360dee401e8SConrad Meyer WARNX(0, "out of memory");
361dee401e8SConrad Meyer rc = ENOMEM;
362dee401e8SConrad Meyer goto out;
363dee401e8SConrad Meyer }
364dee401e8SConrad Meyer
365dee401e8SConrad Meyer rc = fstat(fwfd, &st);
366dee401e8SConrad Meyer if (rc != 0) {
367dee401e8SConrad Meyer WARN(0, "fstat(%s)", fw_path);
368dee401e8SConrad Meyer rc = 0;
369dee401e8SConrad Meyer goto out;
370dee401e8SConrad Meyer }
371e4e0dad4SKonstantin Belousov if (!S_ISREG(st.st_mode))
372e4e0dad4SKonstantin Belousov goto out;
373dee401e8SConrad Meyer if (st.st_size <= 0) {
374dee401e8SConrad Meyer WARN(0, "%s: empty", fw_path);
375dee401e8SConrad Meyer goto out;
376dee401e8SConrad Meyer }
377dee401e8SConrad Meyer
378dee401e8SConrad Meyer fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
379dee401e8SConrad Meyer if (fw_map == MAP_FAILED) {
380dee401e8SConrad Meyer WARN(0, "mmap(%s)", fw_path);
381dee401e8SConrad Meyer goto out;
382dee401e8SConrad Meyer }
383dee401e8SConrad Meyer
384dee401e8SConrad Meyer
385dee401e8SConrad Meyer memset(&parm, 0, sizeof(parm));
386dee401e8SConrad Meyer parm.devfd = devfd;
387dee401e8SConrad Meyer parm.fwimage = fw_map;
388dee401e8SConrad Meyer parm.fwsize = st.st_size;
389dee401e8SConrad Meyer parm.dev_path = dev_path;
390dee401e8SConrad Meyer parm.fw_path = fw_path;
391dee401e8SConrad Meyer
392dee401e8SConrad Meyer handler->update(&parm);
393dee401e8SConrad Meyer
394dee401e8SConrad Meyer out:
395dee401e8SConrad Meyer if (fw_map != MAP_FAILED)
396dee401e8SConrad Meyer munmap(fw_map, st.st_size);
397dee401e8SConrad Meyer free(fw_path);
398dee401e8SConrad Meyer if (fwfd >= 0)
399dee401e8SConrad Meyer close(fwfd);
400dee401e8SConrad Meyer return (rc);
401dee401e8SConrad Meyer }
402dee401e8SConrad Meyer
403dee401e8SConrad Meyer static int
do_update(const char * dev)404e085f869SStanislav Sedov do_update(const char *dev)
405e085f869SStanislav Sedov {
406dee401e8SConrad Meyer int fd, fwdfd;
407e085f869SStanislav Sedov unsigned int i;
408e085f869SStanislav Sedov int error;
409e085f869SStanislav Sedov struct ucode_handler *handler;
410e085f869SStanislav Sedov struct datadir *dir;
4110bb2aabfSGleb Kurtsou DIR *dirp;
412e085f869SStanislav Sedov struct dirent *direntry;
413e085f869SStanislav Sedov
414e085f869SStanislav Sedov fd = open(dev, O_RDONLY);
415e085f869SStanislav Sedov if (fd < 0) {
416cbcc5579SStanislav Sedov WARN(0, "error opening %s for reading", dev);
417e085f869SStanislav Sedov return (1);
418e085f869SStanislav Sedov }
419e085f869SStanislav Sedov
420e085f869SStanislav Sedov /*
421dee401e8SConrad Meyer * Find the appropriate handler for CPU.
422e085f869SStanislav Sedov */
423e085f869SStanislav Sedov for (i = 0; i < NHANDLERS; i++)
424e085f869SStanislav Sedov if (handlers[i].probe(fd) == 0)
425e085f869SStanislav Sedov break;
426e085f869SStanislav Sedov if (i < NHANDLERS)
427e085f869SStanislav Sedov handler = &handlers[i];
428e085f869SStanislav Sedov else {
429dee401e8SConrad Meyer WARNX(0, "cannot find the appropriate handler for %s", dev);
430e085f869SStanislav Sedov close(fd);
431e085f869SStanislav Sedov return (1);
432e085f869SStanislav Sedov }
433e085f869SStanislav Sedov close(fd);
434e085f869SStanislav Sedov
435dee401e8SConrad Meyer fd = open(dev, O_RDWR);
436dee401e8SConrad Meyer if (fd < 0) {
437dee401e8SConrad Meyer WARN(0, "error opening %s for writing", dev);
438dee401e8SConrad Meyer return (1);
439dee401e8SConrad Meyer }
440dee401e8SConrad Meyer
441e085f869SStanislav Sedov /*
442e085f869SStanislav Sedov * Process every image in specified data directories.
443e085f869SStanislav Sedov */
444e085f869SStanislav Sedov SLIST_FOREACH(dir, &datadirs, next) {
445dee401e8SConrad Meyer fwdfd = open(dir->path, O_RDONLY);
446dee401e8SConrad Meyer if (fwdfd < 0) {
447dee401e8SConrad Meyer WARN(1, "skipping directory %s: not accessible", dir->path);
448e085f869SStanislav Sedov continue;
449e085f869SStanislav Sedov }
450dee401e8SConrad Meyer dirp = fdopendir(fwdfd);
451dee401e8SConrad Meyer if (dirp == NULL) {
452dee401e8SConrad Meyer WARNX(0, "out of memory");
453dee401e8SConrad Meyer close(fwdfd);
454dee401e8SConrad Meyer close(fd);
455dee401e8SConrad Meyer return (1);
456dee401e8SConrad Meyer }
457dee401e8SConrad Meyer
4580bb2aabfSGleb Kurtsou while ((direntry = readdir(dirp)) != NULL) {
459e085f869SStanislav Sedov if (direntry->d_namlen == 0)
460e085f869SStanislav Sedov continue;
461dee401e8SConrad Meyer if (direntry->d_type == DT_DIR)
462e085f869SStanislav Sedov continue;
463dee401e8SConrad Meyer
464dee401e8SConrad Meyer error = try_a_fw_image(dev, fd, fwdfd, dir->path,
465dee401e8SConrad Meyer direntry->d_name, handler);
466dee401e8SConrad Meyer if (error != 0) {
467dee401e8SConrad Meyer closedir(dirp);
468dee401e8SConrad Meyer close(fd);
469dee401e8SConrad Meyer return (1);
470e085f869SStanislav Sedov }
471e085f869SStanislav Sedov }
4720bb2aabfSGleb Kurtsou error = closedir(dirp);
473e085f869SStanislav Sedov if (error != 0)
474e085f869SStanislav Sedov WARN(0, "closedir(%s)", dir->path);
475e085f869SStanislav Sedov }
476dee401e8SConrad Meyer close(fd);
477e085f869SStanislav Sedov return (0);
478e085f869SStanislav Sedov }
479e085f869SStanislav Sedov
480e085f869SStanislav Sedov /*
481e085f869SStanislav Sedov * Add new data directory to the search list.
482e085f869SStanislav Sedov */
483e085f869SStanislav Sedov static void
datadir_add(const char * path)484e085f869SStanislav Sedov datadir_add(const char *path)
485e085f869SStanislav Sedov {
486e085f869SStanislav Sedov struct datadir *newdir;
487e085f869SStanislav Sedov
488e085f869SStanislav Sedov newdir = (struct datadir *)malloc(sizeof(*newdir));
489e085f869SStanislav Sedov if (newdir == NULL)
490e085f869SStanislav Sedov err(EX_OSERR, "cannot allocate memory");
491e085f869SStanislav Sedov newdir->path = path;
492e085f869SStanislav Sedov SLIST_INSERT_HEAD(&datadirs, newdir, next);
493e085f869SStanislav Sedov }
494e085f869SStanislav Sedov
495e085f869SStanislav Sedov int
main(int argc,char * argv[])496e085f869SStanislav Sedov main(int argc, char *argv[])
497e085f869SStanislav Sedov {
498dee401e8SConrad Meyer struct datadir *elm;
499e085f869SStanislav Sedov int c, flags;
500e085f869SStanislav Sedov const char *cmdarg;
501e085f869SStanislav Sedov const char *dev;
502e085f869SStanislav Sedov int error;
503e085f869SStanislav Sedov
504e085f869SStanislav Sedov flags = 0;
505e085f869SStanislav Sedov error = 0;
506e085f869SStanislav Sedov cmdarg = ""; /* To keep gcc3 happy. */
507e085f869SStanislav Sedov
5080530a936SKonstantin Belousov while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
509e085f869SStanislav Sedov switch (c) {
510e085f869SStanislav Sedov case 'd':
511e085f869SStanislav Sedov datadir_add(optarg);
512e085f869SStanislav Sedov break;
5130530a936SKonstantin Belousov case 'e':
5140530a936SKonstantin Belousov flags |= FLAG_E;
5150530a936SKonstantin Belousov break;
516e085f869SStanislav Sedov case 'i':
517e085f869SStanislav Sedov flags |= FLAG_I;
518e085f869SStanislav Sedov cmdarg = optarg;
519e085f869SStanislav Sedov break;
520e085f869SStanislav Sedov case 'm':
521e085f869SStanislav Sedov flags |= FLAG_M;
522e085f869SStanislav Sedov cmdarg = optarg;
523e085f869SStanislav Sedov break;
5240539c173SKonstantin Belousov case 'n':
5250539c173SKonstantin Belousov flags |= FLAG_N;
5260539c173SKonstantin Belousov break;
527e085f869SStanislav Sedov case 'u':
528e085f869SStanislav Sedov flags |= FLAG_U;
529e085f869SStanislav Sedov break;
530e085f869SStanislav Sedov case 'v':
531e085f869SStanislav Sedov verbosity_level++;
532e085f869SStanislav Sedov break;
533e085f869SStanislav Sedov case 'h':
534e085f869SStanislav Sedov /* FALLTHROUGH */
535e085f869SStanislav Sedov default:
536e085f869SStanislav Sedov usage();
537e085f869SStanislav Sedov /* NOTREACHED */
538e085f869SStanislav Sedov }
539e085f869SStanislav Sedov }
540e085f869SStanislav Sedov argc -= optind;
541e085f869SStanislav Sedov argv += optind;
542e085f869SStanislav Sedov if (argc < 1) {
543e085f869SStanislav Sedov usage();
544e085f869SStanislav Sedov /* NOTREACHED */
545e085f869SStanislav Sedov }
5460539c173SKonstantin Belousov if ((flags & FLAG_N) == 0)
5470539c173SKonstantin Belousov datadir_add(DEFAULT_DATADIR);
548e085f869SStanislav Sedov dev = argv[0];
5490530a936SKonstantin Belousov c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
550e085f869SStanislav Sedov switch (c) {
551e085f869SStanislav Sedov case FLAG_I:
552aa1cb750SAttilio Rao if (strstr(cmdarg, ",") != NULL)
553aa1cb750SAttilio Rao error = do_cpuid_count(cmdarg, dev);
554aa1cb750SAttilio Rao else
555e085f869SStanislav Sedov error = do_cpuid(cmdarg, dev);
556e085f869SStanislav Sedov break;
557e085f869SStanislav Sedov case FLAG_M:
558e085f869SStanislav Sedov error = do_msr(cmdarg, dev);
559e085f869SStanislav Sedov break;
560e085f869SStanislav Sedov case FLAG_U:
561e085f869SStanislav Sedov error = do_update(dev);
562e085f869SStanislav Sedov break;
5630530a936SKonstantin Belousov case FLAG_E:
5640530a936SKonstantin Belousov error = do_eval_cpu_features(dev);
5650530a936SKonstantin Belousov break;
566e085f869SStanislav Sedov default:
567e085f869SStanislav Sedov usage(); /* Only one command can be selected. */
568e085f869SStanislav Sedov }
569dee401e8SConrad Meyer while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
570dee401e8SConrad Meyer SLIST_REMOVE_HEAD(&datadirs, next);
571dee401e8SConrad Meyer free(elm);
572dee401e8SConrad Meyer }
57378a6a430SKonstantin Belousov return (error == 0 ? 0 : 1);
574e085f869SStanislav Sedov }
575