1/*	$NetBSD: cpuctl.c,v 1.19 2011/09/27 11:24:21 jruoho Exp $	*/
2
3/*-
4 * Copyright (c) 2007, 2008, 2009, 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#ifndef lint
33#include <sys/cdefs.h>
34__RCSID("$NetBSD: cpuctl.c,v 1.19 2011/09/27 11:24:21 jruoho Exp $");
35#endif /* not lint */
36
37#include <sys/param.h>
38#include <sys/ioctl.h>
39#include <sys/uio.h>
40#include <sys/cpuio.h>
41
42#include <err.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <paths.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <stdarg.h>
49#include <string.h>
50#include <unistd.h>
51#include <util.h>
52#include <time.h>
53#include <sched.h>
54
55#include "cpuctl.h"
56
57static u_int	getcpuid(char **);
58__dead static void	usage(void);
59
60static void	cpu_identify(char **);
61static void	cpu_list(char **);
62static void	cpu_offline(char **);
63static void	cpu_online(char **);
64static void	cpu_intr(char **);
65static void	cpu_nointr(char **);
66static void	cpu_ucode(char **);
67
68static struct cmdtab {
69	const char	*label;
70	int	takesargs;
71	int	argsoptional;
72	void	(*func)(char **);
73} const cpu_cmdtab[] = {
74	{ "identify", 1, 0, cpu_identify },
75	{ "list", 0, 0, cpu_list },
76	{ "offline", 1, 0, cpu_offline },
77	{ "online", 1, 0, cpu_online },
78	{ "intr", 1, 0, cpu_intr },
79	{ "nointr", 1, 0, cpu_nointr },
80	{ "ucode", 1, 1, cpu_ucode },
81	{ NULL, 0, 0, NULL },
82};
83
84static int	fd;
85
86int
87main(int argc, char **argv)
88{
89	const struct cmdtab *ct;
90
91	if (argc < 2)
92		usage();
93
94	if ((fd = open(_PATH_CPUCTL, O_RDWR)) < 0)
95		err(EXIT_FAILURE, _PATH_CPUCTL);
96
97	for (ct = cpu_cmdtab; ct->label != NULL; ct++) {
98		if (strcmp(argv[1], ct->label) == 0) {
99			if (!ct->argsoptional &&
100			    ((ct->takesargs == 0) ^ (argv[2] == NULL)))
101			{
102				usage();
103			}
104			(*ct->func)(argv + 2);
105			break;
106		}
107	}
108
109	if (ct->label == NULL)
110		errx(EXIT_FAILURE, "unknown command ``%s''", argv[optind]);
111
112	close(fd);
113	exit(EXIT_SUCCESS);
114	/* NOTREACHED */
115}
116
117static void
118usage(void)
119{
120	const char *progname = getprogname();
121
122	fprintf(stderr, "usage: %s identify cpuno\n", progname);
123	fprintf(stderr, "       %s list\n", progname);
124	fprintf(stderr, "       %s offline cpuno\n", progname);
125	fprintf(stderr, "       %s online cpuno\n", progname);
126	fprintf(stderr, "       %s intr cpuno\n", progname);
127	fprintf(stderr, "       %s nointr cpuno\n", progname);
128	fprintf(stderr, "       %s ucode [file]\n", progname);
129	exit(EXIT_FAILURE);
130	/* NOTREACHED */
131}
132
133static void
134cpu_online(char **argv)
135{
136	cpustate_t cs;
137
138	cs.cs_id = getcpuid(argv);
139	if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0)
140		err(EXIT_FAILURE, "IOC_CPU_GETSTATE");
141	cs.cs_online = true;
142	if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0)
143		err(EXIT_FAILURE, "IOC_CPU_SETSTATE");
144}
145
146static void
147cpu_offline(char **argv)
148{
149	cpustate_t cs;
150
151	cs.cs_id = getcpuid(argv);
152	if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0)
153		err(EXIT_FAILURE, "IOC_CPU_GETSTATE");
154	cs.cs_online = false;
155	if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0)
156		err(EXIT_FAILURE, "IOC_CPU_SETSTATE");
157}
158
159static void
160cpu_intr(char **argv)
161{
162	cpustate_t cs;
163
164	cs.cs_id = getcpuid(argv);
165	if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0)
166		err(EXIT_FAILURE, "IOC_CPU_GETSTATE");
167	cs.cs_intr = true;
168	if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0)
169		err(EXIT_FAILURE, "IOC_CPU_SETSTATE");
170}
171
172static void
173cpu_nointr(char **argv)
174{
175	cpustate_t cs;
176
177	cs.cs_id = getcpuid(argv);
178	if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0)
179		err(EXIT_FAILURE, "IOC_CPU_GETSTATE");
180	cs.cs_intr = false;
181	if (ioctl(fd, IOC_CPU_SETSTATE, &cs) < 0) {
182		if (errno == EOPNOTSUPP) {
183			warnx("interrupt control not supported on "
184			    "this platform");
185		} else
186			err(EXIT_FAILURE, "IOC_CPU_SETSTATE");
187	}
188}
189
190static void
191cpu_ucode(char **argv)
192{
193	int error;
194	struct cpu_ucode uc;
195
196	if (argv[0] != NULL)
197		strlcpy(uc.fwname, argv[0], sizeof(uc.fwname));
198	else
199		memset(uc.fwname, '\0', sizeof(uc.fwname));
200
201	error = ioctl(fd, IOC_CPU_UCODE_APPLY, &uc);
202	if (error < 0) {
203		if (uc.fwname[0])
204			err(EXIT_FAILURE, "%s", uc.fwname);
205		else
206			err(EXIT_FAILURE, "IOC_CPU_UCODE_APPLY");
207	}
208}
209
210
211static void
212cpu_identify(char **argv)
213{
214	char name[32];
215	unsigned int id, np;
216	cpuset_t *cpuset;
217	struct cpu_ucode ucode;
218	char ucbuf[16];
219
220	np = sysconf(_SC_NPROCESSORS_CONF);
221	id = getcpuid(argv);
222	snprintf(name, sizeof(name), "cpu%u", id);
223
224	if (np != 0) {
225		cpuset = cpuset_create();
226		if (cpuset == NULL)
227			err(EXIT_FAILURE, "cpuset_create");
228		cpuset_zero(cpuset);
229		cpuset_set(id, cpuset);
230		if (_sched_setaffinity(0, 0, cpuset_size(cpuset), cpuset) < 0) {
231			if (errno == EPERM) {
232				printf("Cannot bind to target CPU.  Output "
233				    "may not accurately describe the target.\n"
234				    "Run as root to allow binding.\n\n");
235			} else {
236				err(EXIT_FAILURE, "_sched_setaffinity");
237			}
238		}
239		cpuset_destroy(cpuset);
240	}
241	identifycpu(name);
242
243	if (ioctl(fd, IOC_CPU_UCODE_GET_VERSION, &ucode) < 0)
244		ucode.version = (uint64_t)-1;
245	if (ucode.version == (uint64_t)-1)
246		strcpy(ucbuf, "?");
247	else
248		snprintf(ucbuf, sizeof(ucbuf), "0x%"PRIx64,
249		    ucode.version);
250
251	printf("%s: UCode version: %s\n", name, ucbuf);
252}
253
254static u_int
255getcpuid(char **argv)
256{
257	char *argp;
258	u_int id;
259	long np;
260
261	id = (u_int)strtoul(argv[0], &argp, 0);
262	if (*argp != '\0')
263		usage();
264
265	np = sysconf(_SC_NPROCESSORS_CONF);
266	if (id >= (u_long)np)
267		errx(EXIT_FAILURE, "Invalid CPU number");
268
269	return id;
270}
271
272static void
273cpu_list(char **argv)
274{
275	const char *state, *intr;
276	cpustate_t cs;
277	u_int cnt, i;
278	time_t lastmod;
279	char ibuf[16], *ts;
280
281	if (ioctl(fd, IOC_CPU_GETCOUNT, &cnt) < 0)
282		err(EXIT_FAILURE, "IOC_CPU_GETCOUNT");
283
284	printf(
285"Num  HwId Unbound LWPs Interrupts Last change              #Intr\n"
286"---- ---- ------------ ---------- ------------------------ -----\n");
287
288	for (i = 0; i < cnt; i++) {
289		cs.cs_id = i;
290		if (ioctl(fd, IOC_CPU_GETSTATE, &cs) < 0)
291			err(EXIT_FAILURE, "IOC_CPU_GETSTATE");
292		if (ioctl(fd, IOC_CPU_MAPID, &cs.cs_id) < 0)
293			err(EXIT_FAILURE, "IOC_CPU_MAPID");
294		if (cs.cs_online)
295			state = "online";
296		else
297			state = "offline";
298		if (cs.cs_intr)
299			intr = "intr";
300		else
301			intr = "nointr";
302		if (cs.cs_intrcnt == 0)
303			strcpy(ibuf, "?");
304		else
305			snprintf(ibuf, sizeof(ibuf), "%d", cs.cs_intrcnt - 1);
306
307		lastmod = (time_t)cs.cs_lastmod |
308		    ((time_t)cs.cs_lastmodhi << 32);
309		ts = asctime(localtime(&lastmod));
310		ts[strlen(ts) - 1] = '\0';
311		printf("%-4d %-4x %-12s %-10s %s %-5s\n",
312		   i, cs.cs_hwid, state,
313		   intr, ts, ibuf);
314	}
315}
316
317int
318aprint_normal(const char *fmt, ...)
319{
320	va_list ap;
321	int rv;
322
323	va_start(ap, fmt);
324	rv = vfprintf(stdout, fmt, ap);
325	va_end(ap);
326
327	return rv;
328}
329__strong_alias(aprint_verbose,aprint_normal)
330__strong_alias(aprint_error,aprint_normal)
331
332int
333aprint_normal_dev(const char *dev, const char *fmt, ...)
334{
335	va_list ap;
336	int rv;
337
338	printf("%s: ", dev);
339	va_start(ap, fmt);
340	rv = vfprintf(stdout, fmt, ap);
341	va_end(ap);
342
343	return rv;
344}
345__strong_alias(aprint_verbose_dev,aprint_normal_dev)
346__strong_alias(aprint_error_dev,aprint_normal_dev)
347