pciconf.c revision 77513
1/*
2 * Copyright 1996 Massachusetts Institute of Technology
3 *
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that both the above copyright notice and this
7 * permission notice appear in all copies, that both the above
8 * copyright notice and this permission notice appear in all
9 * supporting documentation, and that the name of M.I.T. not be used
10 * in advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission.  M.I.T. makes
12 * no representations about the suitability of this software for any
13 * purpose.  It is provided "as is" without express or implied
14 * warranty.
15 *
16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char rcsid[] =
32  "$FreeBSD: head/usr.sbin/pciconf/pciconf.c 77513 2001-05-31 05:57:49Z imp $";
33#endif /* not lint */
34
35#include <sys/types.h>
36#include <sys/fcntl.h>
37
38#include <err.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43#include <sys/pciio.h>
44#include <sys/queue.h>
45
46#include <dev/pci/pcireg.h>
47
48#include "pathnames.h"
49
50struct pci_device_info
51{
52    TAILQ_ENTRY(pci_device_info)	link;
53    int					id;
54    char				*desc;
55};
56
57struct pci_vendor_info
58{
59    TAILQ_ENTRY(pci_vendor_info)	link;
60    TAILQ_HEAD(,pci_device_info)	devs;
61    int					id;
62    char				*desc;
63};
64
65TAILQ_HEAD(,pci_vendor_info)	pci_vendors;
66
67static void list_devs(int vendors);
68static void list_verbose(struct pci_conf *p);
69static char *guess_class(struct pci_conf *p);
70static char *guess_subclass(struct pci_conf *p);
71static int load_vendors(void);
72static void readit(const char *, const char *, int);
73static void writeit(const char *, const char *, const char *, int);
74static void chkattached(const char *, int);
75
76static int exitstatus = 0;
77
78static void
79usage()
80{
81	fprintf(stderr, "%s\n%s\n%s\n%s\n",
82		"usage: pciconf -l [-v]",
83		"       pciconf -a sel",
84		"       pciconf -r [-b | -h] sel addr",
85		"       pciconf -w [-b | -h] sel addr [value]");
86	exit (1);
87}
88
89int
90main(int argc, char **argv)
91{
92	int c;
93	int listmode, readmode, writemode, attachedmode, verbose;
94	int byte, isshort;
95
96	listmode = readmode = writemode = attachedmode = verbose = byte = isshort = 0;
97
98	while ((c = getopt(argc, argv, "alrwbhv")) != -1) {
99		switch(c) {
100		case 'a':
101			attachedmode = 1;
102			break;
103
104		case 'l':
105			listmode = 1;
106			break;
107
108		case 'r':
109			readmode = 1;
110			break;
111
112		case 'w':
113			writemode = 1;
114			break;
115
116		case 'b':
117			byte = 1;
118			break;
119
120		case 'h':
121			isshort = 1;
122			break;
123
124		case 'v':
125			verbose = 1;
126			break;
127
128		default:
129			usage();
130		}
131	}
132
133	if ((listmode && optind != argc)
134	    || (writemode && optind + 3 != argc)
135	    || (readmode && optind + 2 != argc)
136	    || (attachedmode && optind + 1 != argc))
137		usage();
138
139	if (listmode) {
140		list_devs(verbose);
141	} else if (attachedmode) {
142		chkattached(argv[optind],
143		       byte ? 1 : isshort ? 2 : 4);
144	} else if (readmode) {
145		readit(argv[optind], argv[optind + 1],
146		       byte ? 1 : isshort ? 2 : 4);
147	} else if (writemode) {
148		writeit(argv[optind], argv[optind + 1], argv[optind + 2],
149		       byte ? 1 : isshort ? 2 : 4);
150	} else {
151 		usage();
152	}
153
154	return exitstatus;
155}
156
157static void
158list_devs(int verbose)
159{
160	int fd;
161	struct pci_conf_io pc;
162	struct pci_conf conf[255], *p;
163	int none_count = 0;
164
165	if (verbose)
166		load_vendors();
167
168	fd = open(_PATH_DEVPCI, O_RDWR, 0);
169	if (fd < 0)
170		err(1, "%s", _PATH_DEVPCI);
171
172	bzero(&pc, sizeof(struct pci_conf_io));
173	pc.match_buf_len = sizeof(conf);
174	pc.matches = conf;
175
176	do {
177		if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
178			err(1, "ioctl(PCIOCGETCONF)");
179
180		/*
181		 * 255 entries should be more than enough for most people,
182		 * but if someone has more devices, and then changes things
183		 * around between ioctls, we'll do the cheezy thing and
184		 * just bail.  The alternative would be to go back to the
185		 * beginning of the list, and print things twice, which may
186		 * not be desireable.
187		 */
188		if (pc.status == PCI_GETCONF_LIST_CHANGED) {
189			warnx("PCI device list changed, please try again");
190			exitstatus = 1;
191			close(fd);
192			return;
193		} else if (pc.status ==  PCI_GETCONF_ERROR) {
194			warnx("error returned from PCIOCGETCONF ioctl");
195			exitstatus = 1;
196			close(fd);
197			return;
198		}
199		for (p = conf; p < &conf[pc.num_matches]; p++) {
200
201			printf("%s%d@pci%d:%d:%d:\tclass=0x%06x card=0x%08x "
202			       "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
203			       (p->pd_name && *p->pd_name) ? p->pd_name :
204			       "none",
205			       (p->pd_name && *p->pd_name) ? (int)p->pd_unit :
206			       none_count++,
207			       p->pc_sel.pc_bus, p->pc_sel.pc_dev,
208			       p->pc_sel.pc_func, (p->pc_class << 16) |
209			       (p->pc_subclass << 8) | p->pc_progif,
210			       (p->pc_subdevice << 16) | p->pc_subvendor,
211			       (p->pc_device << 16) | p->pc_vendor,
212			       p->pc_revid, p->pc_hdr);
213			if (verbose)
214				list_verbose(p);
215		}
216	} while (pc.status == PCI_GETCONF_MORE_DEVS);
217
218	close(fd);
219}
220
221static void
222list_verbose(struct pci_conf *p)
223{
224	struct pci_vendor_info	*vi;
225	struct pci_device_info	*di;
226	char *dp;
227
228	TAILQ_FOREACH(vi, &pci_vendors, link) {
229		if (vi->id == p->pc_vendor) {
230			printf("    vendor   = '%s'\n", vi->desc);
231			break;
232		}
233	}
234	if (vi == NULL) {
235		di = NULL;
236	} else {
237		TAILQ_FOREACH(di, &vi->devs, link) {
238			if (di->id == p->pc_device) {
239				printf("    device   = '%s'\n", di->desc);
240				break;
241			}
242		}
243	}
244	if ((dp = guess_class(p)) != NULL)
245		printf("    class    = %s\n", dp);
246	if ((dp = guess_subclass(p)) != NULL)
247		printf("    subclass = %s\n", dp);
248}
249
250/*
251 * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
252 */
253static struct
254{
255	int	class;
256	int	subclass;
257	char	*desc;
258} pci_nomatch_tab[] = {
259	{PCIC_OLD,		-1,			"old"},
260	{PCIC_OLD,		PCIS_OLD_NONVGA,	"non-VGA display device"},
261	{PCIC_OLD,		PCIS_OLD_VGA,		"VGA-compatible display device"},
262	{PCIC_STORAGE,		-1,			"mass storage"},
263	{PCIC_STORAGE,		PCIS_STORAGE_SCSI,	"SCSI"},
264	{PCIC_STORAGE,		PCIS_STORAGE_IDE,	"ATA"},
265	{PCIC_STORAGE,		PCIS_STORAGE_FLOPPY,	"floppy disk"},
266	{PCIC_STORAGE,		PCIS_STORAGE_IPI,	"IPI"},
267	{PCIC_STORAGE,		PCIS_STORAGE_RAID,	"RAID"},
268	{PCIC_NETWORK,		-1,			"network"},
269	{PCIC_NETWORK,		PCIS_NETWORK_ETHERNET,	"ethernet"},
270	{PCIC_NETWORK,		PCIS_NETWORK_TOKENRING,	"token ring"},
271	{PCIC_NETWORK,		PCIS_NETWORK_FDDI,	"fddi"},
272	{PCIC_NETWORK,		PCIS_NETWORK_ATM,	"ATM"},
273	{PCIC_DISPLAY,		-1,			"display"},
274	{PCIC_DISPLAY,		PCIS_DISPLAY_VGA,	"VGA"},
275	{PCIC_DISPLAY,		PCIS_DISPLAY_XGA,	"XGA"},
276	{PCIC_MULTIMEDIA,	-1,			"multimedia"},
277	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_VIDEO,	"video"},
278	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_AUDIO,	"audio"},
279	{PCIC_MEMORY,		-1,			"memory"},
280	{PCIC_MEMORY,		PCIS_MEMORY_RAM,	"RAM"},
281	{PCIC_MEMORY,		PCIS_MEMORY_FLASH,	"flash"},
282	{PCIC_BRIDGE,		-1,			"bridge"},
283	{PCIC_BRIDGE,		PCIS_BRIDGE_HOST,	"HOST-PCI"},
284	{PCIC_BRIDGE,		PCIS_BRIDGE_ISA,	"PCI-ISA"},
285	{PCIC_BRIDGE,		PCIS_BRIDGE_EISA,	"PCI-EISA"},
286	{PCIC_BRIDGE,		PCIS_BRIDGE_MCA,	"PCI-MCA"},
287	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI,	"PCI-PCI"},
288	{PCIC_BRIDGE,		PCIS_BRIDGE_PCMCIA,	"PCI-PCMCIA"},
289	{PCIC_BRIDGE,		PCIS_BRIDGE_NUBUS,	"PCI-NuBus"},
290	{PCIC_BRIDGE,		PCIS_BRIDGE_CARDBUS,	"PCI-CardBus"},
291	{PCIC_BRIDGE,		PCIS_BRIDGE_OTHER,	"PCI-unknown"},
292	{PCIC_SIMPLECOMM,	-1,			"simple comms"},
293	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_UART,	"UART"},	/* could detect 16550 */
294	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_PAR,	"parallel port"},
295	{PCIC_BASEPERIPH,	-1,			"base peripheral"},
296	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PIC,	"interrupt controller"},
297	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_DMA,	"DMA controller"},
298	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_TIMER,	"timer"},
299	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RTC,	"realtime clock"},
300	{PCIC_INPUTDEV,		-1,			"input device"},
301	{PCIC_INPUTDEV,		PCIS_INPUTDEV_KEYBOARD,	"keyboard"},
302	{PCIC_INPUTDEV,		PCIS_INPUTDEV_DIGITIZER,"digitizer"},
303	{PCIC_INPUTDEV,		PCIS_INPUTDEV_MOUSE,	"mouse"},
304	{PCIC_DOCKING,		-1,			"docking station"},
305	{PCIC_PROCESSOR,	-1,			"processor"},
306	{PCIC_SERIALBUS,	-1,			"serial bus"},
307	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FW,	"FireWire"},
308	{PCIC_SERIALBUS,	PCIS_SERIALBUS_ACCESS,	"AccessBus"},
309	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SSA,	"SSA"},
310	{PCIC_SERIALBUS,	PCIS_SERIALBUS_USB,	"USB"},
311	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FC,	"Fibre Channel"},
312	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SMBUS,	"SMBus"},
313	{0, 0,		NULL}
314};
315
316static char *
317guess_class(struct pci_conf *p)
318{
319	int	i;
320
321	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
322		if (pci_nomatch_tab[i].class == p->pc_class)
323			return(pci_nomatch_tab[i].desc);
324	}
325	return(NULL);
326}
327
328static char *
329guess_subclass(struct pci_conf *p)
330{
331	int	i;
332
333	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
334		if ((pci_nomatch_tab[i].class == p->pc_class) &&
335		    (pci_nomatch_tab[i].subclass == p->pc_subclass))
336			return(pci_nomatch_tab[i].desc);
337	}
338	return(NULL);
339}
340
341static int
342load_vendors(void)
343{
344	char *dbf;
345	FILE *db;
346	struct pci_vendor_info *cv;
347	struct pci_device_info *cd;
348	char buf[100], str[100];
349	int id, error;
350
351	/*
352	 * Locate the database and initialise.
353	 */
354	TAILQ_INIT(&pci_vendors);
355	if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
356		dbf = _PATH_PCIVDB;
357	if ((db = fopen(dbf, "r")) == NULL)
358		return(1);
359	cv = NULL;
360	cd = NULL;
361	error = 0;
362
363	/*
364	 * Scan input lines from the database
365	 */
366	for (;;) {
367		if (fgets(buf, sizeof(buf), db) == NULL)
368			break;
369
370		/* Check for vendor entry */
371		if ((buf[0] != '\t') && (sscanf(buf, "%04x\t%[^\n]", &id, str) == 2)) {
372			if ((id == 0) || (strlen(str) < 1))
373				continue;
374			if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
375				warn("allocating vendor entry");
376				error = 1;
377				break;
378			}
379			if ((cv->desc = strdup(str)) == NULL) {
380				free(cv);
381				warn("allocating vendor description");
382				error = 1;
383				break;
384			}
385			cv->id = id;
386			TAILQ_INIT(&cv->devs);
387			TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
388			continue;
389		}
390
391		/* Check for device entry */
392		if ((buf[0] == '\t') && (sscanf(buf + 1, "%04x\t%[^\n]", &id, str) == 2)) {
393			if ((id == 0) || (strlen(str) < 1))
394				continue;
395			if (cv == NULL) {
396				warnx("device entry with no vendor!");
397				continue;
398			}
399			if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
400				warn("allocating device entry");
401				error = 1;
402				break;
403			}
404			if ((cd->desc = strdup(str)) == NULL) {
405				free(cd);
406				warn("allocating device description");
407				error = 1;
408				break;
409			}
410			cd->id = id;
411			TAILQ_INSERT_TAIL(&cv->devs, cd, link);
412			continue;
413		}
414
415		/* It's a comment or junk, ignore it */
416	}
417	if (ferror(db))
418		error = 1;
419	fclose(db);
420
421	return(error);
422}
423
424
425static struct pcisel
426getsel(const char *str)
427{
428	char *ep = (char*) str;
429	struct pcisel sel;
430
431	if (strncmp(ep, "pci", 3) == 0) {
432		ep += 3;
433		sel.pc_bus = strtoul(ep, &ep, 0);
434		if (!ep || *ep++ != ':')
435			errx(1, "cannot parse selector %s", str);
436		sel.pc_dev = strtoul(ep, &ep, 0);
437		if (!ep || *ep != ':') {
438			sel.pc_func = 0;
439		} else {
440			ep++;
441			sel.pc_func = strtoul(ep, &ep, 0);
442		}
443	}
444	if (*ep == ':')
445		ep++;
446	if (*ep || ep == str)
447		errx(1, "cannot parse selector %s", str);
448	return sel;
449}
450
451static void
452readit(const char *name, const char *reg, int width)
453{
454	int fd;
455	struct pci_io pi;
456
457	pi.pi_sel = getsel(name);
458	pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
459	pi.pi_width = width;
460
461	fd = open(_PATH_DEVPCI, O_RDWR, 0);
462	if (fd < 0)
463		err(1, "%s", _PATH_DEVPCI);
464
465	if (ioctl(fd, PCIOCREAD, &pi) < 0)
466		err(1, "ioctl(PCIOCREAD)");
467
468	printf("0x%08x\n", pi.pi_data);
469}
470
471static void
472writeit(const char *name, const char *reg, const char *data, int width)
473{
474	int fd;
475	struct pci_io pi;
476
477	pi.pi_sel = getsel(name);
478	pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
479	pi.pi_width = width;
480	pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
481
482	fd = open(_PATH_DEVPCI, O_RDWR, 0);
483	if (fd < 0)
484		err(1, "%s", _PATH_DEVPCI);
485
486	if (ioctl(fd, PCIOCWRITE, &pi) < 0)
487		err(1, "ioctl(PCIOCWRITE)");
488}
489
490static void
491chkattached (const char *name, int width)
492{
493	int fd;
494	struct pci_io pi;
495
496	pi.pi_sel = getsel(name);
497	pi.pi_reg = 0;
498	pi.pi_width = width;
499	pi.pi_data = 0;
500
501	fd = open(_PATH_DEVPCI, O_RDWR, 0);
502	if (fd < 0)
503		err(1, "%s", _PATH_DEVPCI);
504
505	if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
506		err(1, "ioctl(PCIOCATTACHED)");
507
508	exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
509	printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
510}
511