119102Sse/*
219102Sse * Copyright 1996 Massachusetts Institute of Technology
319102Sse *
419102Sse * Permission to use, copy, modify, and distribute this software and
519102Sse * its documentation for any purpose and without fee is hereby
619102Sse * granted, provided that both the above copyright notice and this
719102Sse * permission notice appear in all copies, that both the above
819102Sse * copyright notice and this permission notice appear in all
919102Sse * supporting documentation, and that the name of M.I.T. not be used
1019102Sse * in advertising or publicity pertaining to distribution of the
1119102Sse * software without specific, written prior permission.  M.I.T. makes
1219102Sse * no representations about the suitability of this software for any
1319102Sse * purpose.  It is provided "as is" without express or implied
1419102Sse * warranty.
15149223Sdes *
1619102Sse * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1719102Sse * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1819102Sse * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1919102Sse * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2019102Sse * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2119102Sse * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2219102Sse * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2319102Sse * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2419102Sse * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2519102Sse * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2619102Sse * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2719102Sse * SUCH DAMAGE.
2819102Sse */
2919102Sse
3030172Scharnier#ifndef lint
3130172Scharnierstatic const char rcsid[] =
3250479Speter  "$FreeBSD$";
3330172Scharnier#endif /* not lint */
3430172Scharnier
3519102Sse#include <sys/types.h>
3619102Sse#include <sys/fcntl.h>
3719102Sse
38261250Sjhb#include <assert.h>
39166435Sjhb#include <ctype.h>
4019102Sse#include <err.h>
41188018Sjhb#include <inttypes.h>
4219102Sse#include <stdlib.h>
4319102Sse#include <stdio.h>
4419102Sse#include <string.h>
4519102Sse#include <unistd.h>
4654322Sken#include <sys/pciio.h>
4769700Smsmith#include <sys/queue.h>
4819102Sse
4969785Smsmith#include <dev/pci/pcireg.h>
5069700Smsmith
5119102Sse#include "pathnames.h"
52166435Sjhb#include "pciconf.h"
5319102Sse
54149223Sdesstruct pci_device_info
5569700Smsmith{
5669700Smsmith    TAILQ_ENTRY(pci_device_info)	link;
5769700Smsmith    int					id;
5869700Smsmith    char				*desc;
5969700Smsmith};
6069700Smsmith
6169700Smsmithstruct pci_vendor_info
6269700Smsmith{
6369700Smsmith    TAILQ_ENTRY(pci_vendor_info)	link;
6469700Smsmith    TAILQ_HEAD(,pci_device_info)	devs;
6569700Smsmith    int					id;
6669700Smsmith    char				*desc;
6769700Smsmith};
6869700Smsmith
6969700SmsmithTAILQ_HEAD(,pci_vendor_info)	pci_vendors;
7069700Smsmith
71261250Sjhbstatic struct pcisel getsel(const char *str);
72188018Sjhbstatic void list_bars(int fd, struct pci_conf *p);
73261250Sjhbstatic void list_devs(const char *name, int verbose, int bars, int caps,
74262134Sjhb    int errors, int vpd);
7569700Smsmithstatic void list_verbose(struct pci_conf *p);
76262134Sjhbstatic void list_vpd(int fd, struct pci_conf *p);
77166435Sjhbstatic const char *guess_class(struct pci_conf *p);
78166435Sjhbstatic const char *guess_subclass(struct pci_conf *p);
7969700Smsmithstatic int load_vendors(void);
8019102Ssestatic void readit(const char *, const char *, int);
8119102Ssestatic void writeit(const char *, const char *, const char *, int);
82212329Sjhbstatic void chkattached(const char *);
8319102Sse
8450225Speterstatic int exitstatus = 0;
8519102Sse
8619102Ssestatic void
87166435Sjhbusage(void)
8830172Scharnier{
8930172Scharnier	fprintf(stderr, "%s\n%s\n%s\n%s\n",
90262134Sjhb		"usage: pciconf -l [-bcevV] [device]",
91261250Sjhb		"       pciconf -a device",
92261250Sjhb		"       pciconf -r [-b | -h] device addr[:addr2]",
93261250Sjhb		"       pciconf -w [-b | -h] device addr value");
9419817Sse	exit (1);
9519102Sse}
9619102Sse
9719102Sseint
9819102Ssemain(int argc, char **argv)
9919102Sse{
10019102Sse	int c;
101236415Sjhb	int listmode, readmode, writemode, attachedmode;
102262134Sjhb	int bars, caps, errors, verbose, vpd;
10319102Sse	int byte, isshort;
10419102Sse
105236415Sjhb	listmode = readmode = writemode = attachedmode = 0;
106262134Sjhb	bars = caps = errors = verbose = vpd = byte = isshort = 0;
10719102Sse
108262134Sjhb	while ((c = getopt(argc, argv, "abcehlrwvV")) != -1) {
10919102Sse		switch(c) {
11021935Sse		case 'a':
11121935Sse			attachedmode = 1;
11221935Sse			break;
11321935Sse
114182707Simp		case 'b':
115188018Sjhb			bars = 1;
116182707Simp			byte = 1;
117182707Simp			break;
118182707Simp
119166435Sjhb		case 'c':
120166435Sjhb			caps = 1;
121166435Sjhb			break;
122166435Sjhb
123236415Sjhb		case 'e':
124236415Sjhb			errors = 1;
125236415Sjhb			break;
126236415Sjhb
127182707Simp		case 'h':
128182707Simp			isshort = 1;
129182707Simp			break;
130182707Simp
13119102Sse		case 'l':
13219102Sse			listmode = 1;
13319102Sse			break;
13419102Sse
13519102Sse		case 'r':
13619102Sse			readmode = 1;
13719102Sse			break;
138149223Sdes
13919102Sse		case 'w':
14019102Sse			writemode = 1;
14119102Sse			break;
14219102Sse
14368677Smsmith		case 'v':
14469700Smsmith			verbose = 1;
14568677Smsmith			break;
14668677Smsmith
147262134Sjhb		case 'V':
148262134Sjhb			vpd = 1;
149262134Sjhb			break;
150262134Sjhb
15119102Sse		default:
15230172Scharnier			usage();
15319102Sse		}
15419102Sse	}
15519102Sse
156261250Sjhb	if ((listmode && optind >= argc + 1)
15719102Sse	    || (writemode && optind + 3 != argc)
15821935Sse	    || (readmode && optind + 2 != argc)
159133271Simp	    || (attachedmode && optind + 1 != argc))
16030172Scharnier		usage();
16119102Sse
16219102Sse	if (listmode) {
163261250Sjhb		list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose,
164262134Sjhb		    bars, caps, errors, vpd);
16577513Simp	} else if (attachedmode) {
166212329Sjhb		chkattached(argv[optind]);
16777513Simp	} else if (readmode) {
168149223Sdes		readit(argv[optind], argv[optind + 1],
169182708Simp		    byte ? 1 : isshort ? 2 : 4);
17077513Simp	} else if (writemode) {
17119102Sse		writeit(argv[optind], argv[optind + 1], argv[optind + 2],
172182708Simp		    byte ? 1 : isshort ? 2 : 4);
17319102Sse	} else {
174149223Sdes		usage();
17519102Sse	}
17619102Sse
17721935Sse	return exitstatus;
17819102Sse}
17919102Sse
18019102Ssestatic void
181262134Sjhblist_devs(const char *name, int verbose, int bars, int caps, int errors,
182262134Sjhb    int vpd)
18319102Sse{
18419102Sse	int fd;
18519102Sse	struct pci_conf_io pc;
18619102Sse	struct pci_conf conf[255], *p;
187261250Sjhb	struct pci_match_conf patterns[1];
18841103Sken	int none_count = 0;
18919102Sse
19069700Smsmith	if (verbose)
19169700Smsmith		load_vendors();
19269700Smsmith
193236415Sjhb	fd = open(_PATH_DEVPCI, (caps || errors) ? O_RDWR : O_RDONLY, 0);
19419102Sse	if (fd < 0)
19519102Sse		err(1, "%s", _PATH_DEVPCI);
19619102Sse
19739231Sgibbs	bzero(&pc, sizeof(struct pci_conf_io));
19839231Sgibbs	pc.match_buf_len = sizeof(conf);
19939231Sgibbs	pc.matches = conf;
200261250Sjhb	if (name != NULL) {
201261250Sjhb		bzero(&patterns, sizeof(patterns));
202261250Sjhb		patterns[0].pc_sel = getsel(name);
203261250Sjhb		patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN |
204261250Sjhb		    PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV |
205261250Sjhb		    PCI_GETCONF_MATCH_FUNC;
206261250Sjhb		pc.num_patterns = 1;
207261250Sjhb		pc.pat_buf_len = sizeof(patterns);
208261250Sjhb		pc.patterns = patterns;
209261250Sjhb	}
21019102Sse
21139231Sgibbs	do {
21239231Sgibbs		if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
21339231Sgibbs			err(1, "ioctl(PCIOCGETCONF)");
21419102Sse
21539231Sgibbs		/*
21639231Sgibbs		 * 255 entries should be more than enough for most people,
21739231Sgibbs		 * but if someone has more devices, and then changes things
218228990Suqs		 * around between ioctls, we'll do the cheesy thing and
21939231Sgibbs		 * just bail.  The alternative would be to go back to the
22039231Sgibbs		 * beginning of the list, and print things twice, which may
221228990Suqs		 * not be desirable.
22239231Sgibbs		 */
22339231Sgibbs		if (pc.status == PCI_GETCONF_LIST_CHANGED) {
22439231Sgibbs			warnx("PCI device list changed, please try again");
22539231Sgibbs			exitstatus = 1;
22639231Sgibbs			close(fd);
22739231Sgibbs			return;
22839231Sgibbs		} else if (pc.status ==  PCI_GETCONF_ERROR) {
22953761Scharnier			warnx("error returned from PCIOCGETCONF ioctl");
23039231Sgibbs			exitstatus = 1;
23139231Sgibbs			close(fd);
23239231Sgibbs			return;
23339231Sgibbs		}
23439231Sgibbs		for (p = conf; p < &conf[pc.num_matches]; p++) {
235172394Smarius			printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x "
236182708Simp			    "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
237182708Simp			    (p->pd_name && *p->pd_name) ? p->pd_name :
238182708Simp			    "none",
239182708Simp			    (p->pd_name && *p->pd_name) ? (int)p->pd_unit :
240182708Simp			    none_count++, p->pc_sel.pc_domain,
241182708Simp			    p->pc_sel.pc_bus, p->pc_sel.pc_dev,
242182708Simp			    p->pc_sel.pc_func, (p->pc_class << 16) |
243182708Simp			    (p->pc_subclass << 8) | p->pc_progif,
244182708Simp			    (p->pc_subdevice << 16) | p->pc_subvendor,
245182708Simp			    (p->pc_device << 16) | p->pc_vendor,
246182708Simp			    p->pc_revid, p->pc_hdr);
24769700Smsmith			if (verbose)
24869700Smsmith				list_verbose(p);
249188018Sjhb			if (bars)
250188018Sjhb				list_bars(fd, p);
251166435Sjhb			if (caps)
252166435Sjhb				list_caps(fd, p);
253236415Sjhb			if (errors)
254236415Sjhb				list_errors(fd, p);
255262134Sjhb			if (vpd)
256262134Sjhb				list_vpd(fd, p);
25739231Sgibbs		}
25839231Sgibbs	} while (pc.status == PCI_GETCONF_MORE_DEVS);
25939231Sgibbs
26019102Sse	close(fd);
26119102Sse}
26219102Sse
26368677Smsmithstatic void
264188018Sjhblist_bars(int fd, struct pci_conf *p)
265188018Sjhb{
266188018Sjhb	struct pci_bar_io bar;
267188018Sjhb	uint64_t base;
268188018Sjhb	const char *type;
269188018Sjhb	int i, range, max;
270188018Sjhb
271188018Sjhb	switch (p->pc_hdr & PCIM_HDRTYPE) {
272188018Sjhb	case PCIM_HDRTYPE_NORMAL:
273188018Sjhb		max = PCIR_MAX_BAR_0;
274188018Sjhb		break;
275188018Sjhb	case PCIM_HDRTYPE_BRIDGE:
276188018Sjhb		max = PCIR_MAX_BAR_1;
277188018Sjhb		break;
278188018Sjhb	case PCIM_HDRTYPE_CARDBUS:
279188018Sjhb		max = PCIR_MAX_BAR_2;
280188018Sjhb		break;
281188018Sjhb	default:
282188018Sjhb		return;
283188018Sjhb	}
284188018Sjhb
285188018Sjhb	for (i = 0; i <= max; i++) {
286188018Sjhb		bar.pbi_sel = p->pc_sel;
287188018Sjhb		bar.pbi_reg = PCIR_BAR(i);
288188018Sjhb		if (ioctl(fd, PCIOCGETBAR, &bar) < 0)
289188018Sjhb			continue;
290188018Sjhb		if (PCI_BAR_IO(bar.pbi_base)) {
291188018Sjhb			type = "I/O Port";
292188018Sjhb			range = 32;
293188018Sjhb			base = bar.pbi_base & PCIM_BAR_IO_BASE;
294188018Sjhb		} else {
295188018Sjhb			if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
296188018Sjhb				type = "Prefetchable Memory";
297188018Sjhb			else
298188018Sjhb				type = "Memory";
299188018Sjhb			switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
300188018Sjhb			case PCIM_BAR_MEM_32:
301188018Sjhb				range = 32;
302188018Sjhb				break;
303188018Sjhb			case PCIM_BAR_MEM_1MB:
304188018Sjhb				range = 20;
305188018Sjhb				break;
306188018Sjhb			case PCIM_BAR_MEM_64:
307188018Sjhb				range = 64;
308188018Sjhb				break;
309188018Sjhb			default:
310188018Sjhb				range = -1;
311188018Sjhb			}
312188018Sjhb			base = bar.pbi_base & ~((uint64_t)0xf);
313188018Sjhb		}
314188018Sjhb		printf("    bar   [%02x] = type %s, range %2d, base %#jx, ",
315188018Sjhb		    PCIR_BAR(i), type, range, (uintmax_t)base);
316246632Sneel		printf("size %ju, %s\n", (uintmax_t)bar.pbi_length,
317188018Sjhb		    bar.pbi_enabled ? "enabled" : "disabled");
318188018Sjhb	}
319188018Sjhb}
320188018Sjhb
321188018Sjhbstatic void
32269700Smsmithlist_verbose(struct pci_conf *p)
32368677Smsmith{
32469700Smsmith	struct pci_vendor_info	*vi;
32569700Smsmith	struct pci_device_info	*di;
326166435Sjhb	const char *dp;
327149223Sdes
32869700Smsmith	TAILQ_FOREACH(vi, &pci_vendors, link) {
32969700Smsmith		if (vi->id == p->pc_vendor) {
330166435Sjhb			printf("    vendor     = '%s'\n", vi->desc);
33168677Smsmith			break;
33269700Smsmith		}
33369700Smsmith	}
33469700Smsmith	if (vi == NULL) {
33569700Smsmith		di = NULL;
33668677Smsmith	} else {
33769700Smsmith		TAILQ_FOREACH(di, &vi->devs, link) {
33869700Smsmith			if (di->id == p->pc_device) {
339166435Sjhb				printf("    device     = '%s'\n", di->desc);
34069700Smsmith				break;
34169700Smsmith			}
34269700Smsmith		}
34368677Smsmith	}
34469700Smsmith	if ((dp = guess_class(p)) != NULL)
345166435Sjhb		printf("    class      = %s\n", dp);
34669700Smsmith	if ((dp = guess_subclass(p)) != NULL)
347166435Sjhb		printf("    subclass   = %s\n", dp);
34869700Smsmith}
34969700Smsmith
350262134Sjhbstatic void
351262134Sjhblist_vpd(int fd, struct pci_conf *p)
352262134Sjhb{
353262134Sjhb	struct pci_list_vpd_io list;
354262134Sjhb	struct pci_vpd_element *vpd, *end;
355262134Sjhb
356262134Sjhb	list.plvi_sel = p->pc_sel;
357262134Sjhb	list.plvi_len = 0;
358262134Sjhb	list.plvi_data = NULL;
359262134Sjhb	if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0)
360262134Sjhb		return;
361262134Sjhb
362262134Sjhb	list.plvi_data = malloc(list.plvi_len);
363262134Sjhb	if (ioctl(fd, PCIOCLISTVPD, &list) < 0) {
364262134Sjhb		free(list.plvi_data);
365262134Sjhb		return;
366262134Sjhb	}
367262134Sjhb
368262134Sjhb	vpd = list.plvi_data;
369262134Sjhb	end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len);
370262134Sjhb	for (; vpd < end; vpd = PVE_NEXT(vpd)) {
371262134Sjhb		if (vpd->pve_flags == PVE_FLAG_IDENT) {
372262134Sjhb			printf("    VPD ident  = '%.*s'\n",
373262134Sjhb			    (int)vpd->pve_datalen, vpd->pve_data);
374262134Sjhb			continue;
375262134Sjhb		}
376262134Sjhb
377262134Sjhb		/* Ignore the checksum keyword. */
378262134Sjhb		if (!(vpd->pve_flags & PVE_FLAG_RW) &&
379262134Sjhb		    memcmp(vpd->pve_keyword, "RV", 2) == 0)
380262134Sjhb			continue;
381262134Sjhb
382262134Sjhb		/* Ignore remaining read-write space. */
383262134Sjhb		if (vpd->pve_flags & PVE_FLAG_RW &&
384262134Sjhb		    memcmp(vpd->pve_keyword, "RW", 2) == 0)
385262134Sjhb			continue;
386262134Sjhb
387262134Sjhb		/* Handle extended capability keyword. */
388262134Sjhb		if (!(vpd->pve_flags & PVE_FLAG_RW) &&
389262134Sjhb		    memcmp(vpd->pve_keyword, "CP", 2) == 0) {
390262134Sjhb			printf("    VPD ro CP  = ID %02x in map 0x%x[0x%x]\n",
391262134Sjhb			    (unsigned int)vpd->pve_data[0],
392262134Sjhb			    PCIR_BAR((unsigned int)vpd->pve_data[1]),
393262134Sjhb			    (unsigned int)vpd->pve_data[3] << 8 |
394262134Sjhb			    (unsigned int)vpd->pve_data[2]);
395262134Sjhb			continue;
396262134Sjhb		}
397262134Sjhb
398262134Sjhb		/* Remaining keywords should all have ASCII values. */
399262134Sjhb		printf("    VPD %s %c%c  = '%.*s'\n",
400262134Sjhb		    vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro",
401262134Sjhb		    vpd->pve_keyword[0], vpd->pve_keyword[1],
402262134Sjhb		    (int)vpd->pve_datalen, vpd->pve_data);
403262134Sjhb	}
404262134Sjhb	free(list.plvi_data);
405262134Sjhb}
406262134Sjhb
40769700Smsmith/*
40869700Smsmith * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
40969700Smsmith */
41069700Smsmithstatic struct
41169700Smsmith{
41269700Smsmith	int	class;
41369700Smsmith	int	subclass;
414166435Sjhb	const char *desc;
41569700Smsmith} pci_nomatch_tab[] = {
41669700Smsmith	{PCIC_OLD,		-1,			"old"},
41769700Smsmith	{PCIC_OLD,		PCIS_OLD_NONVGA,	"non-VGA display device"},
41869700Smsmith	{PCIC_OLD,		PCIS_OLD_VGA,		"VGA-compatible display device"},
41969700Smsmith	{PCIC_STORAGE,		-1,			"mass storage"},
42069700Smsmith	{PCIC_STORAGE,		PCIS_STORAGE_SCSI,	"SCSI"},
42169700Smsmith	{PCIC_STORAGE,		PCIS_STORAGE_IDE,	"ATA"},
42269700Smsmith	{PCIC_STORAGE,		PCIS_STORAGE_FLOPPY,	"floppy disk"},
42369700Smsmith	{PCIC_STORAGE,		PCIS_STORAGE_IPI,	"IPI"},
42469700Smsmith	{PCIC_STORAGE,		PCIS_STORAGE_RAID,	"RAID"},
425184936Smav	{PCIC_STORAGE,		PCIS_STORAGE_ATA_ADMA,	"ATA (ADMA)"},
426184936Smav	{PCIC_STORAGE,		PCIS_STORAGE_SATA,	"SATA"},
427184936Smav	{PCIC_STORAGE,		PCIS_STORAGE_SAS,	"SAS"},
428240739Sgavin	{PCIC_STORAGE,		PCIS_STORAGE_NVM,	"NVM"},
42969700Smsmith	{PCIC_NETWORK,		-1,			"network"},
43069700Smsmith	{PCIC_NETWORK,		PCIS_NETWORK_ETHERNET,	"ethernet"},
43169700Smsmith	{PCIC_NETWORK,		PCIS_NETWORK_TOKENRING,	"token ring"},
43269700Smsmith	{PCIC_NETWORK,		PCIS_NETWORK_FDDI,	"fddi"},
43369700Smsmith	{PCIC_NETWORK,		PCIS_NETWORK_ATM,	"ATM"},
434144156Sjmg	{PCIC_NETWORK,		PCIS_NETWORK_ISDN,	"ISDN"},
43569700Smsmith	{PCIC_DISPLAY,		-1,			"display"},
43669700Smsmith	{PCIC_DISPLAY,		PCIS_DISPLAY_VGA,	"VGA"},
43769700Smsmith	{PCIC_DISPLAY,		PCIS_DISPLAY_XGA,	"XGA"},
438144156Sjmg	{PCIC_DISPLAY,		PCIS_DISPLAY_3D,	"3D"},
43969700Smsmith	{PCIC_MULTIMEDIA,	-1,			"multimedia"},
44069700Smsmith	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_VIDEO,	"video"},
44169700Smsmith	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_AUDIO,	"audio"},
442144156Sjmg	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_TELE,	"telephony"},
443184142Smav	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_HDA,	"HDA"},
44469700Smsmith	{PCIC_MEMORY,		-1,			"memory"},
44569700Smsmith	{PCIC_MEMORY,		PCIS_MEMORY_RAM,	"RAM"},
44669700Smsmith	{PCIC_MEMORY,		PCIS_MEMORY_FLASH,	"flash"},
44769700Smsmith	{PCIC_BRIDGE,		-1,			"bridge"},
44869700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_HOST,	"HOST-PCI"},
44969700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_ISA,	"PCI-ISA"},
45069700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_EISA,	"PCI-EISA"},
45169700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_MCA,	"PCI-MCA"},
45269700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI,	"PCI-PCI"},
45369700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_PCMCIA,	"PCI-PCMCIA"},
45469700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_NUBUS,	"PCI-NuBus"},
45569700Smsmith	{PCIC_BRIDGE,		PCIS_BRIDGE_CARDBUS,	"PCI-CardBus"},
456144156Sjmg	{PCIC_BRIDGE,		PCIS_BRIDGE_RACEWAY,	"PCI-RACEway"},
45769700Smsmith	{PCIC_SIMPLECOMM,	-1,			"simple comms"},
45869700Smsmith	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_UART,	"UART"},	/* could detect 16550 */
45969700Smsmith	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_PAR,	"parallel port"},
460144156Sjmg	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MULSER,	"multiport serial"},
461144156Sjmg	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MODEM,	"generic modem"},
46269700Smsmith	{PCIC_BASEPERIPH,	-1,			"base peripheral"},
46369700Smsmith	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PIC,	"interrupt controller"},
46469700Smsmith	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_DMA,	"DMA controller"},
46569700Smsmith	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_TIMER,	"timer"},
46669700Smsmith	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RTC,	"realtime clock"},
467144156Sjmg	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PCIHOT,	"PCI hot-plug controller"},
468184140Smav	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_SDHC,	"SD host controller"},
469267002Smav	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_IOMMU,	"IOMMU"},
47069700Smsmith	{PCIC_INPUTDEV,		-1,			"input device"},
47169700Smsmith	{PCIC_INPUTDEV,		PCIS_INPUTDEV_KEYBOARD,	"keyboard"},
47269700Smsmith	{PCIC_INPUTDEV,		PCIS_INPUTDEV_DIGITIZER,"digitizer"},
47369700Smsmith	{PCIC_INPUTDEV,		PCIS_INPUTDEV_MOUSE,	"mouse"},
474144156Sjmg	{PCIC_INPUTDEV,		PCIS_INPUTDEV_SCANNER,	"scanner"},
475144156Sjmg	{PCIC_INPUTDEV,		PCIS_INPUTDEV_GAMEPORT,	"gameport"},
47669700Smsmith	{PCIC_DOCKING,		-1,			"docking station"},
47769700Smsmith	{PCIC_PROCESSOR,	-1,			"processor"},
47869700Smsmith	{PCIC_SERIALBUS,	-1,			"serial bus"},
47969700Smsmith	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FW,	"FireWire"},
480149223Sdes	{PCIC_SERIALBUS,	PCIS_SERIALBUS_ACCESS,	"AccessBus"},
48169700Smsmith	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SSA,	"SSA"},
48269700Smsmith	{PCIC_SERIALBUS,	PCIS_SERIALBUS_USB,	"USB"},
48369700Smsmith	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FC,	"Fibre Channel"},
48469700Smsmith	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SMBUS,	"SMBus"},
485144156Sjmg	{PCIC_WIRELESS,		-1,			"wireless controller"},
486144156Sjmg	{PCIC_WIRELESS,		PCIS_WIRELESS_IRDA,	"iRDA"},
487144156Sjmg	{PCIC_WIRELESS,		PCIS_WIRELESS_IR,	"IR"},
488144156Sjmg	{PCIC_WIRELESS,		PCIS_WIRELESS_RF,	"RF"},
489144156Sjmg	{PCIC_INTELLIIO,	-1,			"intelligent I/O controller"},
490144156Sjmg	{PCIC_INTELLIIO,	PCIS_INTELLIIO_I2O,	"I2O"},
491144156Sjmg	{PCIC_SATCOM,		-1,			"satellite communication"},
492144156Sjmg	{PCIC_SATCOM,		PCIS_SATCOM_TV,		"sat TV"},
493144156Sjmg	{PCIC_SATCOM,		PCIS_SATCOM_AUDIO,	"sat audio"},
494144156Sjmg	{PCIC_SATCOM,		PCIS_SATCOM_VOICE,	"sat voice"},
495144156Sjmg	{PCIC_SATCOM,		PCIS_SATCOM_DATA,	"sat data"},
496144156Sjmg	{PCIC_CRYPTO,		-1,			"encrypt/decrypt"},
497144156Sjmg	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"network/computer crypto"},
498144156Sjmg	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"entertainment crypto"},
499144156Sjmg	{PCIC_DASP,		-1,			"dasp"},
500144156Sjmg	{PCIC_DASP,		PCIS_DASP_DPIO,		"DPIO module"},
50169700Smsmith	{0, 0,		NULL}
50269700Smsmith};
50369700Smsmith
504166435Sjhbstatic const char *
50569700Smsmithguess_class(struct pci_conf *p)
50669700Smsmith{
50769700Smsmith	int	i;
50869700Smsmith
50969700Smsmith	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
51069700Smsmith		if (pci_nomatch_tab[i].class == p->pc_class)
51169700Smsmith			return(pci_nomatch_tab[i].desc);
51269700Smsmith	}
51369700Smsmith	return(NULL);
51469700Smsmith}
51569700Smsmith
516166435Sjhbstatic const char *
51769700Smsmithguess_subclass(struct pci_conf *p)
51869700Smsmith{
51969700Smsmith	int	i;
52069700Smsmith
52169700Smsmith	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
52269700Smsmith		if ((pci_nomatch_tab[i].class == p->pc_class) &&
52369700Smsmith		    (pci_nomatch_tab[i].subclass == p->pc_subclass))
52469700Smsmith			return(pci_nomatch_tab[i].desc);
52569700Smsmith	}
52669700Smsmith	return(NULL);
52769700Smsmith}
52869700Smsmith
52969700Smsmithstatic int
53069700Smsmithload_vendors(void)
53169700Smsmith{
532166435Sjhb	const char *dbf;
53369700Smsmith	FILE *db;
53469700Smsmith	struct pci_vendor_info *cv;
53569700Smsmith	struct pci_device_info *cd;
536149225Sdes	char buf[1024], str[1024];
537149225Sdes	char *ch;
53869700Smsmith	int id, error;
53969700Smsmith
54069700Smsmith	/*
54169700Smsmith	 * Locate the database and initialise.
54269700Smsmith	 */
54369700Smsmith	TAILQ_INIT(&pci_vendors);
54469700Smsmith	if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
54569700Smsmith		dbf = _PATH_PCIVDB;
54669700Smsmith	if ((db = fopen(dbf, "r")) == NULL)
54769700Smsmith		return(1);
54869700Smsmith	cv = NULL;
54969700Smsmith	cd = NULL;
55069700Smsmith	error = 0;
55169700Smsmith
55269700Smsmith	/*
55369700Smsmith	 * Scan input lines from the database
55469700Smsmith	 */
55569700Smsmith	for (;;) {
55669700Smsmith		if (fgets(buf, sizeof(buf), db) == NULL)
55768677Smsmith			break;
55869700Smsmith
559149225Sdes		if ((ch = strchr(buf, '#')) != NULL)
560149225Sdes			*ch = '\0';
561149225Sdes		ch = strchr(buf, '\0') - 1;
562149225Sdes		while (ch > buf && isspace(*ch))
563149225Sdes			*ch-- = '\0';
564149225Sdes		if (ch <= buf)
565149225Sdes			continue;
566149225Sdes
567149225Sdes		/* Can't handle subvendor / subdevice entries yet */
568149225Sdes		if (buf[0] == '\t' && buf[1] == '\t')
569149225Sdes			continue;
570149225Sdes
57169700Smsmith		/* Check for vendor entry */
572149225Sdes		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
57369700Smsmith			if ((id == 0) || (strlen(str) < 1))
57469700Smsmith				continue;
57569700Smsmith			if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
57669700Smsmith				warn("allocating vendor entry");
57769700Smsmith				error = 1;
57869700Smsmith				break;
57969700Smsmith			}
58069700Smsmith			if ((cv->desc = strdup(str)) == NULL) {
58169700Smsmith				free(cv);
58269700Smsmith				warn("allocating vendor description");
58369700Smsmith				error = 1;
58469700Smsmith				break;
58569700Smsmith			}
58669700Smsmith			cv->id = id;
58769700Smsmith			TAILQ_INIT(&cv->devs);
58869700Smsmith			TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
58969700Smsmith			continue;
59069700Smsmith		}
591149223Sdes
59269700Smsmith		/* Check for device entry */
593149225Sdes		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
59469700Smsmith			if ((id == 0) || (strlen(str) < 1))
59569700Smsmith				continue;
59669700Smsmith			if (cv == NULL) {
59769700Smsmith				warnx("device entry with no vendor!");
59869700Smsmith				continue;
59969700Smsmith			}
60069700Smsmith			if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
60169700Smsmith				warn("allocating device entry");
60269700Smsmith				error = 1;
60369700Smsmith				break;
60469700Smsmith			}
60569700Smsmith			if ((cd->desc = strdup(str)) == NULL) {
60669700Smsmith				free(cd);
60769700Smsmith				warn("allocating device description");
60869700Smsmith				error = 1;
60969700Smsmith				break;
61069700Smsmith			}
61169700Smsmith			cd->id = id;
61269700Smsmith			TAILQ_INSERT_TAIL(&cv->devs, cd, link);
61369700Smsmith			continue;
61469700Smsmith		}
61569700Smsmith
61669700Smsmith		/* It's a comment or junk, ignore it */
61768677Smsmith	}
61869700Smsmith	if (ferror(db))
61969700Smsmith		error = 1;
62069700Smsmith	fclose(db);
621149223Sdes
62269700Smsmith	return(error);
62368677Smsmith}
62468677Smsmith
625166435Sjhbuint32_t
626166435Sjhbread_config(int fd, struct pcisel *sel, long reg, int width)
627166435Sjhb{
628166435Sjhb	struct pci_io pi;
62969700Smsmith
630166435Sjhb	pi.pi_sel = *sel;
631166435Sjhb	pi.pi_reg = reg;
632166435Sjhb	pi.pi_width = width;
633166435Sjhb
634166435Sjhb	if (ioctl(fd, PCIOCREAD, &pi) < 0)
635166435Sjhb		err(1, "ioctl(PCIOCREAD)");
636166435Sjhb
637166435Sjhb	return (pi.pi_data);
638166435Sjhb}
639166435Sjhb
64019102Ssestatic struct pcisel
641261250Sjhbgetdevice(const char *name)
64219102Sse{
643261250Sjhb	struct pci_conf_io pc;
644261250Sjhb	struct pci_conf conf[1];
645261250Sjhb	struct pci_match_conf patterns[1];
646261250Sjhb	char *cp;
647261250Sjhb	int fd;
648261250Sjhb
649261250Sjhb	fd = open(_PATH_DEVPCI, O_RDONLY, 0);
650261250Sjhb	if (fd < 0)
651261250Sjhb		err(1, "%s", _PATH_DEVPCI);
652261250Sjhb
653261250Sjhb	bzero(&pc, sizeof(struct pci_conf_io));
654261250Sjhb	pc.match_buf_len = sizeof(conf);
655261250Sjhb	pc.matches = conf;
656261250Sjhb
657261250Sjhb	bzero(&patterns, sizeof(patterns));
658261250Sjhb
659261250Sjhb	/*
660261250Sjhb	 * The pattern structure requires the unit to be split out from
661261250Sjhb	 * the driver name.  Walk backwards from the end of the name to
662261250Sjhb	 * find the start of the unit.
663261250Sjhb	 */
664261250Sjhb	if (name[0] == '\0')
665261250Sjhb		err(1, "Empty device name");
666261250Sjhb	cp = strchr(name, '\0');
667261250Sjhb	assert(cp != NULL && cp != name);
668261250Sjhb	cp--;
669261250Sjhb	while (cp != name && isdigit(cp[-1]))
670261250Sjhb		cp--;
671261250Sjhb	if (cp == name)
672261250Sjhb		errx(1, "Invalid device name");
673261250Sjhb	if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name))
674261250Sjhb		errx(1, "Device name i2s too long");
675261250Sjhb	memcpy(patterns[0].pd_name, name, cp - name);
676261250Sjhb	patterns[0].pd_unit = strtol(cp, &cp, 10);
677261250Sjhb	assert(*cp == '\0');
678261250Sjhb	patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT;
679261250Sjhb	pc.num_patterns = 1;
680261250Sjhb	pc.pat_buf_len = sizeof(patterns);
681261250Sjhb	pc.patterns = patterns;
682261250Sjhb
683261250Sjhb	if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
684261250Sjhb		err(1, "ioctl(PCIOCGETCONF)");
685261250Sjhb	if (pc.status != PCI_GETCONF_LAST_DEVICE &&
686261250Sjhb	    pc.status != PCI_GETCONF_MORE_DEVS)
687261250Sjhb		errx(1, "error returned from PCIOCGETCONF ioctl");
688261250Sjhb	close(fd);
689261250Sjhb	if (pc.num_matches == 0)
690261250Sjhb		errx(1, "Device not found");
691261250Sjhb	return (conf[0].pc_sel);
692261250Sjhb}
693261250Sjhb
694261250Sjhbstatic struct pcisel
695261250Sjhbparsesel(const char *str)
696261250Sjhb{
697116640Sjmg	char *ep = strchr(str, '@');
698116640Sjmg	char *epbase;
69919102Sse	struct pcisel sel;
700172447Sse	unsigned long selarr[4];
701172447Sse	int i;
702116640Sjmg
703116640Sjmg	if (ep == NULL)
704116640Sjmg		ep = (char *)str;
705116640Sjmg	else
706116640Sjmg		ep++;
707116640Sjmg
708116640Sjmg	epbase = ep;
709116640Sjmg
71019102Sse	if (strncmp(ep, "pci", 3) == 0) {
71119102Sse		ep += 3;
712172447Sse		i = 0;
713172447Sse		do {
714172447Sse			selarr[i++] = strtoul(ep, &ep, 10);
715172448Sse		} while ((*ep == ':' || *ep == '.') && *++ep != '\0' && i < 4);
716172447Sse
717172447Sse		if (i > 2)
718172447Sse			sel.pc_func = selarr[--i];
719172447Sse		else
72019102Sse			sel.pc_func = 0;
721172447Sse		sel.pc_dev = selarr[--i];
722172447Sse		sel.pc_bus = selarr[--i];
723172447Sse		if (i > 0)
724172447Sse			sel.pc_domain = selarr[--i];
725172447Sse		else
726172447Sse			sel.pc_domain = 0;
72719102Sse	}
728116640Sjmg	if (*ep != '\x0' || ep == epbase)
72919102Sse		errx(1, "cannot parse selector %s", str);
73019102Sse	return sel;
73119102Sse}
73219102Sse
733261250Sjhbstatic struct pcisel
734261250Sjhbgetsel(const char *str)
735261250Sjhb{
736261250Sjhb
737261250Sjhb	/*
738261250Sjhb	 * No device names contain colons and selectors always contain
739261250Sjhb	 * at least one colon.
740261250Sjhb	 */
741261250Sjhb	if (strchr(str, ':') == NULL)
742261250Sjhb		return (getdevice(str));
743261250Sjhb	else
744261250Sjhb		return (parsesel(str));
745261250Sjhb}
746261250Sjhb
74719102Ssestatic void
74877533Simpreadone(int fd, struct pcisel *sel, long reg, int width)
74919102Sse{
75019102Sse
751166435Sjhb	printf("%0*x", width*2, read_config(fd, sel, reg, width));
75277533Simp}
75377533Simp
75477533Simpstatic void
75577533Simpreadit(const char *name, const char *reg, int width)
75677533Simp{
75777533Simp	long rstart;
75877533Simp	long rend;
75977533Simp	long r;
76077533Simp	char *end;
76177533Simp	int i;
76277533Simp	int fd;
76377533Simp	struct pcisel sel;
76477533Simp
76519102Sse	fd = open(_PATH_DEVPCI, O_RDWR, 0);
76619102Sse	if (fd < 0)
76719102Sse		err(1, "%s", _PATH_DEVPCI);
76819102Sse
76977533Simp	rend = rstart = strtol(reg, &end, 0);
77077533Simp	if (end && *end == ':') {
77177533Simp		end++;
77277533Simp		rend = strtol(end, (char **) 0, 0);
77377533Simp	}
77477533Simp	sel = getsel(name);
775149223Sdes	for (i = 1, r = rstart; r <= rend; i++, r += width) {
77677533Simp		readone(fd, &sel, r, width);
777182708Simp		if (i && !(i % 8))
778182708Simp			putchar(' ');
77991299Ssos		putchar(i % (16/width) ? ' ' : '\n');
78077533Simp	}
78191299Ssos	if (i % (16/width) != 1)
78277533Simp		putchar('\n');
78377533Simp	close(fd);
78419102Sse}
78519102Sse
78619102Ssestatic void
78719102Ssewriteit(const char *name, const char *reg, const char *data, int width)
78819102Sse{
78919102Sse	int fd;
79019102Sse	struct pci_io pi;
79119102Sse
79219102Sse	pi.pi_sel = getsel(name);
79319102Sse	pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
79419102Sse	pi.pi_width = width;
79519102Sse	pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
79619102Sse
79719102Sse	fd = open(_PATH_DEVPCI, O_RDWR, 0);
79819102Sse	if (fd < 0)
79919102Sse		err(1, "%s", _PATH_DEVPCI);
80019102Sse
80119102Sse	if (ioctl(fd, PCIOCWRITE, &pi) < 0)
80219102Sse		err(1, "ioctl(PCIOCWRITE)");
80319102Sse}
80421935Sse
80521935Ssestatic void
806212329Sjhbchkattached(const char *name)
80721935Sse{
80821935Sse	int fd;
80921935Sse	struct pci_io pi;
81021935Sse
81121935Sse	pi.pi_sel = getsel(name);
81221935Sse
81321935Sse	fd = open(_PATH_DEVPCI, O_RDWR, 0);
81421935Sse	if (fd < 0)
81521935Sse		err(1, "%s", _PATH_DEVPCI);
81621935Sse
81721935Sse	if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
81821935Sse		err(1, "ioctl(PCIOCATTACHED)");
81921935Sse
82021935Sse	exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
82121935Sse	printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
82221935Sse}
823