xref: /netbsd-src/usr.sbin/pcictl/pcictl.c (revision 62ee7dc27baa40ad66a6b4799630c1b418add616)
1 /*	$NetBSD: pcictl.c,v 1.22 2016/09/24 23:12:54 mrg Exp $	*/
2 
3 /*
4  * Copyright 2001 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed for the NetBSD Project by
20  *	Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*
39  * pcictl(8) -- a program to manipulate the PCI bus
40  */
41 
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <paths.h>
48 #include <pci.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <util.h>
54 
55 #include <dev/pci/pcireg.h>
56 #include <dev/pci/pcidevs.h>
57 #include <dev/pci/pciio.h>
58 
59 struct command {
60 	const char *cmd_name;
61 	const char *arg_names;
62 	void (*cmd_func)(int, char *[]);
63 	int open_flags;
64 };
65 
66 __dead static void	usage(void);
67 
68 static int	pcifd;
69 
70 static struct pciio_businfo pci_businfo;
71 
72 static const	char *dvname;
73 static char	dvname_store[MAXPATHLEN];
74 static const	char *cmdname;
75 static int	print_numbers = 0;
76 static int	print_names = 0;
77 
78 static void	cmd_list(int, char *[]);
79 static void	cmd_dump(int, char *[]);
80 static void	cmd_read(int, char *[]);
81 static void	cmd_write(int, char *[]);
82 
83 static const struct command commands[] = {
84 	{ "list",
85 	  "[-Nn] [-b bus] [-d device] [-f function]",
86 	  cmd_list,
87 	  O_RDONLY },
88 
89 	{ "dump",
90 	  "[-b bus] -d device [-f function]",
91 	  cmd_dump,
92 	  O_RDONLY },
93 
94 	{ "read",
95 	  "[-b bus] -d device [-f function] reg",
96 	  cmd_read,
97 	  O_RDONLY },
98 
99 	{ "write",
100 	  "[-b bus] -d device [-f function] reg value",
101 	  cmd_write,
102 	  O_WRONLY },
103 
104 	{ 0, 0, 0, 0 },
105 };
106 
107 static int	parse_bdf(const char *);
108 static u_int	parse_reg(const char *);
109 
110 static void	scan_pci(int, int, int, void (*)(u_int, u_int, u_int));
111 
112 static void	scan_pci_list(u_int, u_int, u_int);
113 static void	scan_pci_dump(u_int, u_int, u_int);
114 
115 int
main(int argc,char * argv[])116 main(int argc, char *argv[])
117 {
118 	int i;
119 
120 	/* Must have at least: device command */
121 	if (argc < 3)
122 		usage();
123 
124 	/* Skip program name, get and skip device name, get command. */
125 	dvname = argv[1];
126 	cmdname = argv[2];
127 	argv += 2;
128 	argc -= 2;
129 
130 	/* Look up and call the command. */
131 	for (i = 0; commands[i].cmd_name != NULL; i++)
132 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
133 			break;
134 	if (commands[i].cmd_name == NULL)
135 		errx(EXIT_FAILURE, "unknown command: %s", cmdname);
136 
137 	/* Open the device. */
138 	if ((strchr(dvname, '/') == NULL) &&
139 	    (snprintf(dvname_store, sizeof(dvname_store), _PATH_DEV "%s",
140 	     dvname) < (int)sizeof(dvname_store)))
141 		dvname = dvname_store;
142 	pcifd = open(dvname, commands[i].open_flags);
143 	if (pcifd < 0)
144 		err(EXIT_FAILURE, "%s", dvname);
145 
146 	/* Make sure the device is a PCI bus. */
147 	if (ioctl(pcifd, PCI_IOC_BUSINFO, &pci_businfo) != 0)
148 		errx(EXIT_FAILURE, "%s: not a PCI bus device", dvname);
149 
150 	(*commands[i].cmd_func)(argc, argv);
151 	exit(EXIT_SUCCESS);
152 }
153 
154 static void
usage(void)155 usage(void)
156 {
157 	int i;
158 
159 	fprintf(stderr, "usage: %s device command [arg [...]]\n",
160 	    getprogname());
161 
162 	fprintf(stderr, "   Available commands:\n");
163 	for (i = 0; commands[i].cmd_name != NULL; i++)
164 		fprintf(stderr, "\t%s %s\n", commands[i].cmd_name,
165 		    commands[i].arg_names);
166 
167 	exit(EXIT_FAILURE);
168 }
169 
170 static void
cmd_list(int argc,char * argv[])171 cmd_list(int argc, char *argv[])
172 {
173 	int bus, dev, func;
174 	int ch;
175 
176 	bus = -1;
177 	dev = func = -1;
178 
179 	while ((ch = getopt(argc, argv, "b:d:f:Nn")) != -1) {
180 		switch (ch) {
181 		case 'b':
182 			bus = parse_bdf(optarg);
183 			break;
184 		case 'd':
185 			dev = parse_bdf(optarg);
186 			break;
187 		case 'f':
188 			func = parse_bdf(optarg);
189 			break;
190 		case 'n':
191 			print_numbers = 1;
192 			break;
193 		case 'N':
194 			print_names = 1;
195 			break;
196 		default:
197 			usage();
198 		}
199 	}
200 	argv += optind;
201 	argc -= optind;
202 
203 	if (argc != 0)
204 		usage();
205 
206 	scan_pci(bus, dev, func, scan_pci_list);
207 }
208 
209 static void
cmd_dump(int argc,char * argv[])210 cmd_dump(int argc, char *argv[])
211 {
212 	int bus, dev, func;
213 	int ch;
214 
215 	bus = pci_businfo.busno;
216 	func = 0;
217 	dev = -1;
218 
219 	while ((ch = getopt(argc, argv, "b:d:f:")) != -1) {
220 		switch (ch) {
221 		case 'b':
222 			bus = parse_bdf(optarg);
223 			break;
224 		case 'd':
225 			dev = parse_bdf(optarg);
226 			break;
227 		case 'f':
228 			func = parse_bdf(optarg);
229 			break;
230 		default:
231 			usage();
232 		}
233 	}
234 	argv += optind;
235 	argc -= optind;
236 
237 	if (argc != 0)
238 		usage();
239 
240 	if (bus == -1)
241 		errx(EXIT_FAILURE, "dump: wildcard bus number not permitted");
242 	if (dev == -1)
243 		errx(EXIT_FAILURE, "dump: must specify a device number");
244 	if (func == -1)
245 		errx(EXIT_FAILURE, "dump: wildcard function number not permitted");
246 
247 	scan_pci(bus, dev, func, scan_pci_dump);
248 }
249 
250 static void
cmd_read(int argc,char * argv[])251 cmd_read(int argc, char *argv[])
252 {
253 	int bus, dev, func;
254 	u_int reg;
255 	pcireg_t value;
256 	int ch;
257 
258 	bus = pci_businfo.busno;
259 	func = 0;
260 	dev = -1;
261 
262 	while ((ch = getopt(argc, argv, "b:d:f:")) != -1) {
263 		switch (ch) {
264 		case 'b':
265 			bus = parse_bdf(optarg);
266 			break;
267 		case 'd':
268 			dev = parse_bdf(optarg);
269 			break;
270 		case 'f':
271 			func = parse_bdf(optarg);
272 			break;
273 		default:
274 			usage();
275 		}
276 	}
277 	argv += optind;
278 	argc -= optind;
279 
280 	if (argc != 1)
281 		usage();
282 	reg = parse_reg(argv[0]);
283 	if (pcibus_conf_read(pcifd, bus, dev, func, reg, &value) == -1)
284 		err(EXIT_FAILURE, "pcibus_conf_read"
285 		    "(bus %d dev %d func %d reg %u)", bus, dev, func, reg);
286 	if (printf("%08x\n", value) < 0)
287 		err(EXIT_FAILURE, "printf");
288 }
289 
290 static void
cmd_write(int argc,char * argv[])291 cmd_write(int argc, char *argv[])
292 {
293 	int bus, dev, func;
294 	u_int reg;
295 	pcireg_t value;
296 	int ch;
297 
298 	bus = pci_businfo.busno;
299 	func = 0;
300 	dev = -1;
301 
302 	while ((ch = getopt(argc, argv, "b:d:f:")) != -1) {
303 		switch (ch) {
304 		case 'b':
305 			bus = parse_bdf(optarg);
306 			break;
307 		case 'd':
308 			dev = parse_bdf(optarg);
309 			break;
310 		case 'f':
311 			func = parse_bdf(optarg);
312 			break;
313 		default:
314 			usage();
315 		}
316 	}
317 	argv += optind;
318 	argc -= optind;
319 
320 	if (argc != 2)
321 		usage();
322 	reg = parse_reg(argv[0]);
323 	__CTASSERT(sizeof(value) == sizeof(u_int));
324 	value = parse_reg(argv[1]);
325 	if (pcibus_conf_write(pcifd, bus, dev, func, reg, value) == -1)
326 		err(EXIT_FAILURE, "pcibus_conf_write"
327 		    "(bus %d dev %d func %d reg %u value 0x%x)",
328 		    bus, dev, func, reg, value);
329 }
330 
331 static int
parse_bdf(const char * str)332 parse_bdf(const char *str)
333 {
334 	long value;
335 	char *end;
336 
337 	if (strcmp(str, "all") == 0 ||
338 	    strcmp(str, "any") == 0)
339 		return (-1);
340 
341 	errno = 0;
342 	value = strtol(str, &end, 0);
343 	if ((str[0] == '\0') || (*end != '\0'))
344 		errx(EXIT_FAILURE, "\"%s\" is not a number", str);
345 	if ((errno == ERANGE) && ((value == LONG_MIN) || (value == LONG_MAX)))
346 		errx(EXIT_FAILURE, "out of range: %s", str);
347 	if ((value < INT_MIN) || (INT_MAX < value))
348 		errx(EXIT_FAILURE, "out of range: %lu", value);
349 
350 	return value;
351 }
352 
353 static u_int
parse_reg(const char * str)354 parse_reg(const char *str)
355 {
356 	unsigned long value;
357 	char *end;
358 
359 	errno = 0;
360 	value = strtoul(str, &end, 0);
361 	if (*end != '\0')
362 		errx(EXIT_FAILURE, "\"%s\" is not a number", str);
363 	if ((errno == ERANGE) && (value == ULONG_MAX))
364 		errx(EXIT_FAILURE, "out of range: %s", str);
365 	if (UINT_MAX < value)
366 		errx(EXIT_FAILURE, "out of range: %lu", value);
367 
368 	return value;
369 }
370 
371 static void
scan_pci(int busarg,int devarg,int funcarg,void (* cb)(u_int,u_int,u_int))372 scan_pci(int busarg, int devarg, int funcarg, void (*cb)(u_int, u_int, u_int))
373 {
374 	u_int busmin, busmax;
375 	u_int devmin, devmax;
376 	u_int funcmin, funcmax;
377 	u_int bus, dev, func;
378 	pcireg_t id, bhlcr;
379 
380 	if (busarg == -1) {
381 		busmin = 0;
382 		busmax = 255;
383 	} else
384 		busmin = busmax = busarg;
385 
386 	if (devarg == -1) {
387 		devmin = 0;
388 		if (pci_businfo.maxdevs <= 0)
389 			devmax = 0;
390 		else
391 			devmax = pci_businfo.maxdevs - 1;
392 	} else
393 		devmin = devmax = devarg;
394 
395 	for (bus = busmin; bus <= busmax; bus++) {
396 		for (dev = devmin; dev <= devmax; dev++) {
397 			if (pcibus_conf_read(pcifd, bus, dev, 0,
398 			    PCI_BHLC_REG, &bhlcr) != 0)
399 				continue;
400 			if (funcarg == -1) {
401 				funcmin = 0;
402 				if (PCI_HDRTYPE_MULTIFN(bhlcr))
403 					funcmax = 7;
404 				else
405 					funcmax = 0;
406 			} else
407 				funcmin = funcmax = funcarg;
408 			for (func = funcmin; func <= funcmax; func++) {
409 				if (pcibus_conf_read(pcifd, bus, dev,
410 				    func, PCI_ID_REG, &id) != 0)
411 					continue;
412 
413 				/* Invalid vendor ID value? */
414 				if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
415 					continue;
416 				/*
417 				 * XXX Not invalid, but we've done this
418 				 * ~forever.
419 				 */
420 				if (PCI_VENDOR(id) == 0)
421 					continue;
422 
423 				(*cb)(bus, dev, func);
424 			}
425 		}
426 	}
427 }
428 
429 static void
scan_pci_list(u_int bus,u_int dev,u_int func)430 scan_pci_list(u_int bus, u_int dev, u_int func)
431 {
432 	pcireg_t id, class;
433 	char devinfo[256];
434 
435 	if (pcibus_conf_read(pcifd, bus, dev, func, PCI_ID_REG, &id) != 0)
436 		return;
437 	if (pcibus_conf_read(pcifd, bus, dev, func, PCI_CLASS_REG, &class) != 0)
438 		return;
439 
440 	printf("%03u:%02u:%01u: ", bus, dev, func);
441 	if (print_numbers) {
442 		printf("0x%08x (0x%08x)", id, class);
443 	} else {
444 		pci_devinfo(id, class, 1, devinfo, sizeof(devinfo));
445 		printf("%s", devinfo);
446 	}
447 	if (print_names) {
448 		char drvname[16];
449 		if (pci_drvnameonbus(pcifd, bus, dev, func, drvname,
450 				     sizeof drvname) == 0)
451 			printf(" [%s]", drvname);
452 	}
453 	printf("\n");
454 }
455 
456 static void
scan_pci_dump(u_int bus,u_int dev,u_int func)457 scan_pci_dump(u_int bus, u_int dev, u_int func)
458 {
459 
460 	pci_conf_print(pcifd, bus, dev, func);
461 }
462