pciconf.c revision 188018
1193240Ssam/*
2193240Ssam * Copyright 1996 Massachusetts Institute of Technology
3193240Ssam *
4193240Ssam * Permission to use, copy, modify, and distribute this software and
5193240Ssam * its documentation for any purpose and without fee is hereby
6193240Ssam * granted, provided that both the above copyright notice and this
7193240Ssam * permission notice appear in all copies, that both the above
8193240Ssam * copyright notice and this permission notice appear in all
9193240Ssam * supporting documentation, and that the name of M.I.T. not be used
10193240Ssam * in advertising or publicity pertaining to distribution of the
11193240Ssam * software without specific, written prior permission.  M.I.T. makes
12193240Ssam * no representations about the suitability of this software for any
13193240Ssam * purpose.  It is provided "as is" without express or implied
14193240Ssam * warranty.
15193240Ssam *
16193240Ssam * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17193240Ssam * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18193240Ssam * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19193240Ssam * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20193240Ssam * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21193240Ssam * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22193240Ssam * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23193240Ssam * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24193240Ssam * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25193240Ssam * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26193240Ssam * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27193240Ssam * SUCH DAMAGE.
28193240Ssam */
29193240Ssam
30193240Ssam#ifndef lint
31193240Ssamstatic const char rcsid[] =
32193240Ssam  "$FreeBSD: head/usr.sbin/pciconf/pciconf.c 188018 2009-02-02 19:54:16Z jhb $";
33193240Ssam#endif /* not lint */
34193240Ssam
35193240Ssam#include <sys/types.h>
36193240Ssam#include <sys/fcntl.h>
37193240Ssam
38193240Ssam#include <ctype.h>
39193240Ssam#include <err.h>
40193240Ssam#include <inttypes.h>
41193240Ssam#include <stdlib.h>
42193240Ssam#include <stdio.h>
43193240Ssam#include <string.h>
44193240Ssam#include <unistd.h>
45193240Ssam#include <sys/pciio.h>
46193240Ssam#include <sys/queue.h>
47193240Ssam
48193240Ssam#include <dev/pci/pcireg.h>
49193240Ssam
50193240Ssam#include "pathnames.h"
51193240Ssam#include "pciconf.h"
52193240Ssam
53193240Ssamstruct pci_device_info
54193240Ssam{
55193240Ssam    TAILQ_ENTRY(pci_device_info)	link;
56193240Ssam    int					id;
57193240Ssam    char				*desc;
58193240Ssam};
59193240Ssam
60193240Ssamstruct pci_vendor_info
61193240Ssam{
62193240Ssam    TAILQ_ENTRY(pci_vendor_info)	link;
63193240Ssam    TAILQ_HEAD(,pci_device_info)	devs;
64193240Ssam    int					id;
65193240Ssam    char				*desc;
66193240Ssam};
67193240Ssam
68193240SsamTAILQ_HEAD(,pci_vendor_info)	pci_vendors;
69193240Ssam
70193240Ssamstatic void list_bars(int fd, struct pci_conf *p);
71193240Ssamstatic void list_devs(int verbose, int bars, int caps);
72193240Ssamstatic void list_verbose(struct pci_conf *p);
73193240Ssamstatic const char *guess_class(struct pci_conf *p);
74193240Ssamstatic const char *guess_subclass(struct pci_conf *p);
75193240Ssamstatic int load_vendors(void);
76193240Ssamstatic void readit(const char *, const char *, int);
77193240Ssamstatic void writeit(const char *, const char *, const char *, int);
78193240Ssamstatic void chkattached(const char *, int);
79193240Ssam
80193240Ssamstatic int exitstatus = 0;
81193240Ssam
82193240Ssamstatic void
83193240Ssamusage(void)
84193240Ssam{
85193240Ssam	fprintf(stderr, "%s\n%s\n%s\n%s\n",
86193240Ssam		"usage: pciconf -l [-bcv]",
87193240Ssam		"       pciconf -a selector",
88193240Ssam		"       pciconf -r [-b | -h] selector addr[:addr2]",
89193240Ssam		"       pciconf -w [-b | -h] selector addr value");
90193240Ssam	exit (1);
91193240Ssam}
92193240Ssam
93193240Ssamint
94193240Ssammain(int argc, char **argv)
95193240Ssam{
96193240Ssam	int c;
97193240Ssam	int listmode, readmode, writemode, attachedmode, bars, caps, verbose;
98193240Ssam	int byte, isshort;
99193240Ssam
100193240Ssam	listmode = readmode = writemode = attachedmode = bars = caps = verbose = byte = isshort = 0;
101193240Ssam
102193240Ssam	while ((c = getopt(argc, argv, "abchlrwv")) != -1) {
103193240Ssam		switch(c) {
104193240Ssam		case 'a':
105193240Ssam			attachedmode = 1;
106193240Ssam			break;
107193240Ssam
108193240Ssam		case 'b':
109193240Ssam			bars = 1;
110193240Ssam			byte = 1;
111193240Ssam			break;
112193240Ssam
113193240Ssam		case 'c':
114193240Ssam			caps = 1;
115193240Ssam			break;
116193240Ssam
117193240Ssam		case 'h':
118195171Ssam			isshort = 1;
119193240Ssam			break;
120193240Ssam
121193240Ssam		case 'l':
122193240Ssam			listmode = 1;
123193240Ssam			break;
124193240Ssam
125193240Ssam		case 'r':
126193240Ssam			readmode = 1;
127193240Ssam			break;
128193240Ssam
129193240Ssam		case 'w':
130193240Ssam			writemode = 1;
131193240Ssam			break;
132193240Ssam
133193240Ssam		case 'v':
134193240Ssam			verbose = 1;
135193240Ssam			break;
136193240Ssam
137193240Ssam		default:
138193240Ssam			usage();
139193240Ssam		}
140193240Ssam	}
141193240Ssam
142193240Ssam	if ((listmode && optind != argc)
143193240Ssam	    || (writemode && optind + 3 != argc)
144193240Ssam	    || (readmode && optind + 2 != argc)
145193240Ssam	    || (attachedmode && optind + 1 != argc))
146193240Ssam		usage();
147193240Ssam
148193240Ssam	if (listmode) {
149193240Ssam		list_devs(verbose, bars, caps);
150193240Ssam	} else if (attachedmode) {
151193240Ssam		chkattached(argv[optind],
152193240Ssam		    byte ? 1 : isshort ? 2 : 4);
153193240Ssam	} else if (readmode) {
154193240Ssam		readit(argv[optind], argv[optind + 1],
155193240Ssam		    byte ? 1 : isshort ? 2 : 4);
156193240Ssam	} else if (writemode) {
157193240Ssam		writeit(argv[optind], argv[optind + 1], argv[optind + 2],
158193240Ssam		    byte ? 1 : isshort ? 2 : 4);
159193240Ssam	} else {
160193240Ssam		usage();
161193240Ssam	}
162193240Ssam
163193240Ssam	return exitstatus;
164193240Ssam}
165193240Ssam
166193240Ssamstatic void
167193240Ssamlist_devs(int verbose, int bars, int caps)
168193240Ssam{
169193240Ssam	int fd;
170193240Ssam	struct pci_conf_io pc;
171193240Ssam	struct pci_conf conf[255], *p;
172193240Ssam	int none_count = 0;
173193240Ssam
174193240Ssam	if (verbose)
175193240Ssam		load_vendors();
176193240Ssam
177193240Ssam	fd = open(_PATH_DEVPCI, caps ? O_RDWR : O_RDONLY, 0);
178193240Ssam	if (fd < 0)
179193240Ssam		err(1, "%s", _PATH_DEVPCI);
180193240Ssam
181193240Ssam	bzero(&pc, sizeof(struct pci_conf_io));
182193240Ssam	pc.match_buf_len = sizeof(conf);
183193240Ssam	pc.matches = conf;
184193240Ssam
185193240Ssam	do {
186193240Ssam		if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
187193240Ssam			err(1, "ioctl(PCIOCGETCONF)");
188193240Ssam
189193240Ssam		/*
190193240Ssam		 * 255 entries should be more than enough for most people,
191193240Ssam		 * but if someone has more devices, and then changes things
192193240Ssam		 * around between ioctls, we'll do the cheezy thing and
193227309Sed		 * just bail.  The alternative would be to go back to the
194227309Sed		 * beginning of the list, and print things twice, which may
195193240Ssam		 * not be desireable.
196193240Ssam		 */
197193240Ssam		if (pc.status == PCI_GETCONF_LIST_CHANGED) {
198193240Ssam			warnx("PCI device list changed, please try again");
199193240Ssam			exitstatus = 1;
200193240Ssam			close(fd);
201193240Ssam			return;
202193240Ssam		} else if (pc.status ==  PCI_GETCONF_ERROR) {
203193240Ssam			warnx("error returned from PCIOCGETCONF ioctl");
204193240Ssam			exitstatus = 1;
205193240Ssam			close(fd);
206193240Ssam			return;
207193240Ssam		}
208193240Ssam		for (p = conf; p < &conf[pc.num_matches]; p++) {
209193240Ssam			printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x "
210193240Ssam			    "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
211193240Ssam			    (p->pd_name && *p->pd_name) ? p->pd_name :
212193240Ssam			    "none",
213193240Ssam			    (p->pd_name && *p->pd_name) ? (int)p->pd_unit :
214193240Ssam			    none_count++, p->pc_sel.pc_domain,
215193240Ssam			    p->pc_sel.pc_bus, p->pc_sel.pc_dev,
216193240Ssam			    p->pc_sel.pc_func, (p->pc_class << 16) |
217193240Ssam			    (p->pc_subclass << 8) | p->pc_progif,
218193240Ssam			    (p->pc_subdevice << 16) | p->pc_subvendor,
219193240Ssam			    (p->pc_device << 16) | p->pc_vendor,
220193240Ssam			    p->pc_revid, p->pc_hdr);
221193240Ssam			if (verbose)
222193240Ssam				list_verbose(p);
223193240Ssam			if (bars)
224193240Ssam				list_bars(fd, p);
225193240Ssam			if (caps)
226193240Ssam				list_caps(fd, p);
227193240Ssam		}
228193240Ssam	} while (pc.status == PCI_GETCONF_MORE_DEVS);
229193240Ssam
230193240Ssam	close(fd);
231193240Ssam}
232193240Ssam
233193240Ssamstatic void
234193240Ssamlist_bars(int fd, struct pci_conf *p)
235193240Ssam{
236193240Ssam	struct pci_bar_io bar;
237193240Ssam	uint64_t base;
238193240Ssam	const char *type;
239193240Ssam	int i, range, max;
240193240Ssam
241193240Ssam	switch (p->pc_hdr & PCIM_HDRTYPE) {
242193240Ssam	case PCIM_HDRTYPE_NORMAL:
243193240Ssam		max = PCIR_MAX_BAR_0;
244193240Ssam		break;
245193240Ssam	case PCIM_HDRTYPE_BRIDGE:
246193240Ssam		max = PCIR_MAX_BAR_1;
247193240Ssam		break;
248193240Ssam	case PCIM_HDRTYPE_CARDBUS:
249193240Ssam		max = PCIR_MAX_BAR_2;
250193240Ssam		break;
251193240Ssam	default:
252193240Ssam		return;
253193240Ssam	}
254193240Ssam
255193240Ssam	for (i = 0; i <= max; i++) {
256193240Ssam		bar.pbi_sel = p->pc_sel;
257193240Ssam		bar.pbi_reg = PCIR_BAR(i);
258193240Ssam		if (ioctl(fd, PCIOCGETBAR, &bar) < 0)
259193240Ssam			continue;
260193240Ssam		if (PCI_BAR_IO(bar.pbi_base)) {
261193240Ssam			type = "I/O Port";
262193240Ssam			range = 32;
263193240Ssam			base = bar.pbi_base & PCIM_BAR_IO_BASE;
264193240Ssam		} else {
265193240Ssam			if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
266193240Ssam				type = "Prefetchable Memory";
267193240Ssam			else
268193240Ssam				type = "Memory";
269193240Ssam			switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
270193240Ssam			case PCIM_BAR_MEM_32:
271193240Ssam				range = 32;
272193240Ssam				break;
273193240Ssam			case PCIM_BAR_MEM_1MB:
274193240Ssam				range = 20;
275193240Ssam				break;
276193240Ssam			case PCIM_BAR_MEM_64:
277193240Ssam				range = 64;
278193240Ssam				break;
279193240Ssam			default:
280193240Ssam				range = -1;
281193240Ssam			}
282193240Ssam			base = bar.pbi_base & ~((uint64_t)0xf);
283216835Sbschmidt		}
284193240Ssam		printf("    bar   [%02x] = type %s, range %2d, base %#jx, ",
285193240Ssam		    PCIR_BAR(i), type, range, (uintmax_t)base);
286193240Ssam		printf("size %2d, %s\n", (int)bar.pbi_length,
287193240Ssam		    bar.pbi_enabled ? "enabled" : "disabled");
288193240Ssam	}
289193240Ssam}
290193240Ssam
291193240Ssamstatic void
292193240Ssamlist_verbose(struct pci_conf *p)
293193240Ssam{
294193240Ssam	struct pci_vendor_info	*vi;
295193240Ssam	struct pci_device_info	*di;
296193240Ssam	const char *dp;
297193240Ssam
298193240Ssam	TAILQ_FOREACH(vi, &pci_vendors, link) {
299193240Ssam		if (vi->id == p->pc_vendor) {
300193240Ssam			printf("    vendor     = '%s'\n", vi->desc);
301193240Ssam			break;
302193240Ssam		}
303193240Ssam	}
304193240Ssam	if (vi == NULL) {
305193240Ssam		di = NULL;
306193240Ssam	} else {
307193240Ssam		TAILQ_FOREACH(di, &vi->devs, link) {
308193240Ssam			if (di->id == p->pc_device) {
309193240Ssam				printf("    device     = '%s'\n", di->desc);
310193240Ssam				break;
311193240Ssam			}
312193240Ssam		}
313193240Ssam	}
314193240Ssam	if ((dp = guess_class(p)) != NULL)
315193240Ssam		printf("    class      = %s\n", dp);
316193240Ssam	if ((dp = guess_subclass(p)) != NULL)
317193240Ssam		printf("    subclass   = %s\n", dp);
318193240Ssam}
319193240Ssam
320193240Ssam/*
321193240Ssam * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
322193240Ssam */
323193240Ssamstatic struct
324193240Ssam{
325193240Ssam	int	class;
326193240Ssam	int	subclass;
327193240Ssam	const char *desc;
328193240Ssam} pci_nomatch_tab[] = {
329193240Ssam	{PCIC_OLD,		-1,			"old"},
330193240Ssam	{PCIC_OLD,		PCIS_OLD_NONVGA,	"non-VGA display device"},
331193240Ssam	{PCIC_OLD,		PCIS_OLD_VGA,		"VGA-compatible display device"},
332193240Ssam	{PCIC_STORAGE,		-1,			"mass storage"},
333193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_SCSI,	"SCSI"},
334193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_IDE,	"ATA"},
335193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_FLOPPY,	"floppy disk"},
336193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_IPI,	"IPI"},
337193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_RAID,	"RAID"},
338193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_ATA_ADMA,	"ATA (ADMA)"},
339193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_SATA,	"SATA"},
340193240Ssam	{PCIC_STORAGE,		PCIS_STORAGE_SAS,	"SAS"},
341193240Ssam	{PCIC_NETWORK,		-1,			"network"},
342193240Ssam	{PCIC_NETWORK,		PCIS_NETWORK_ETHERNET,	"ethernet"},
343193240Ssam	{PCIC_NETWORK,		PCIS_NETWORK_TOKENRING,	"token ring"},
344193240Ssam	{PCIC_NETWORK,		PCIS_NETWORK_FDDI,	"fddi"},
345193240Ssam	{PCIC_NETWORK,		PCIS_NETWORK_ATM,	"ATM"},
346193240Ssam	{PCIC_NETWORK,		PCIS_NETWORK_ISDN,	"ISDN"},
347193240Ssam	{PCIC_DISPLAY,		-1,			"display"},
348193240Ssam	{PCIC_DISPLAY,		PCIS_DISPLAY_VGA,	"VGA"},
349193240Ssam	{PCIC_DISPLAY,		PCIS_DISPLAY_XGA,	"XGA"},
350193240Ssam	{PCIC_DISPLAY,		PCIS_DISPLAY_3D,	"3D"},
351193240Ssam	{PCIC_MULTIMEDIA,	-1,			"multimedia"},
352193240Ssam	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_VIDEO,	"video"},
353193240Ssam	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_AUDIO,	"audio"},
354193240Ssam	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_TELE,	"telephony"},
355193240Ssam	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_HDA,	"HDA"},
356193240Ssam	{PCIC_MEMORY,		-1,			"memory"},
357193240Ssam	{PCIC_MEMORY,		PCIS_MEMORY_RAM,	"RAM"},
358193240Ssam	{PCIC_MEMORY,		PCIS_MEMORY_FLASH,	"flash"},
359193240Ssam	{PCIC_BRIDGE,		-1,			"bridge"},
360193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_HOST,	"HOST-PCI"},
361193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_ISA,	"PCI-ISA"},
362193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_EISA,	"PCI-EISA"},
363193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_MCA,	"PCI-MCA"},
364193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI,	"PCI-PCI"},
365193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_PCMCIA,	"PCI-PCMCIA"},
366193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_NUBUS,	"PCI-NuBus"},
367193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_CARDBUS,	"PCI-CardBus"},
368193240Ssam	{PCIC_BRIDGE,		PCIS_BRIDGE_RACEWAY,	"PCI-RACEway"},
369193240Ssam	{PCIC_SIMPLECOMM,	-1,			"simple comms"},
370193240Ssam	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_UART,	"UART"},	/* could detect 16550 */
371193240Ssam	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_PAR,	"parallel port"},
372193240Ssam	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MULSER,	"multiport serial"},
373193240Ssam	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MODEM,	"generic modem"},
374193240Ssam	{PCIC_BASEPERIPH,	-1,			"base peripheral"},
375193240Ssam	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PIC,	"interrupt controller"},
376193240Ssam	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_DMA,	"DMA controller"},
377193240Ssam	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_TIMER,	"timer"},
378193240Ssam	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RTC,	"realtime clock"},
379193240Ssam	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PCIHOT,	"PCI hot-plug controller"},
380193240Ssam	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_SDHC,	"SD host controller"},
381193240Ssam	{PCIC_INPUTDEV,		-1,			"input device"},
382193240Ssam	{PCIC_INPUTDEV,		PCIS_INPUTDEV_KEYBOARD,	"keyboard"},
383193240Ssam	{PCIC_INPUTDEV,		PCIS_INPUTDEV_DIGITIZER,"digitizer"},
384193240Ssam	{PCIC_INPUTDEV,		PCIS_INPUTDEV_MOUSE,	"mouse"},
385193240Ssam	{PCIC_INPUTDEV,		PCIS_INPUTDEV_SCANNER,	"scanner"},
386193240Ssam	{PCIC_INPUTDEV,		PCIS_INPUTDEV_GAMEPORT,	"gameport"},
387193240Ssam	{PCIC_DOCKING,		-1,			"docking station"},
388193240Ssam	{PCIC_PROCESSOR,	-1,			"processor"},
389193240Ssam	{PCIC_SERIALBUS,	-1,			"serial bus"},
390193240Ssam	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FW,	"FireWire"},
391193240Ssam	{PCIC_SERIALBUS,	PCIS_SERIALBUS_ACCESS,	"AccessBus"},
392193240Ssam	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SSA,	"SSA"},
393193240Ssam	{PCIC_SERIALBUS,	PCIS_SERIALBUS_USB,	"USB"},
394193240Ssam	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FC,	"Fibre Channel"},
395193240Ssam	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SMBUS,	"SMBus"},
396193240Ssam	{PCIC_WIRELESS,		-1,			"wireless controller"},
397193240Ssam	{PCIC_WIRELESS,		PCIS_WIRELESS_IRDA,	"iRDA"},
398193240Ssam	{PCIC_WIRELESS,		PCIS_WIRELESS_IR,	"IR"},
399193240Ssam	{PCIC_WIRELESS,		PCIS_WIRELESS_RF,	"RF"},
400193240Ssam	{PCIC_INTELLIIO,	-1,			"intelligent I/O controller"},
401193240Ssam	{PCIC_INTELLIIO,	PCIS_INTELLIIO_I2O,	"I2O"},
402193240Ssam	{PCIC_SATCOM,		-1,			"satellite communication"},
403193240Ssam	{PCIC_SATCOM,		PCIS_SATCOM_TV,		"sat TV"},
404193240Ssam	{PCIC_SATCOM,		PCIS_SATCOM_AUDIO,	"sat audio"},
405193240Ssam	{PCIC_SATCOM,		PCIS_SATCOM_VOICE,	"sat voice"},
406193240Ssam	{PCIC_SATCOM,		PCIS_SATCOM_DATA,	"sat data"},
407193240Ssam	{PCIC_CRYPTO,		-1,			"encrypt/decrypt"},
408193240Ssam	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"network/computer crypto"},
409193240Ssam	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"entertainment crypto"},
410193240Ssam	{PCIC_DASP,		-1,			"dasp"},
411193240Ssam	{PCIC_DASP,		PCIS_DASP_DPIO,		"DPIO module"},
412193240Ssam	{0, 0,		NULL}
413193240Ssam};
414193240Ssam
415193240Ssamstatic const char *
416193240Ssamguess_class(struct pci_conf *p)
417193240Ssam{
418193240Ssam	int	i;
419193240Ssam
420193240Ssam	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
421193240Ssam		if (pci_nomatch_tab[i].class == p->pc_class)
422193240Ssam			return(pci_nomatch_tab[i].desc);
423193240Ssam	}
424193240Ssam	return(NULL);
425193240Ssam}
426193240Ssam
427193240Ssamstatic const char *
428193240Ssamguess_subclass(struct pci_conf *p)
429193240Ssam{
430193240Ssam	int	i;
431193240Ssam
432193240Ssam	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
433193240Ssam		if ((pci_nomatch_tab[i].class == p->pc_class) &&
434193240Ssam		    (pci_nomatch_tab[i].subclass == p->pc_subclass))
435193240Ssam			return(pci_nomatch_tab[i].desc);
436193240Ssam	}
437193240Ssam	return(NULL);
438193240Ssam}
439193240Ssam
440193240Ssamstatic int
441193240Ssamload_vendors(void)
442193240Ssam{
443193240Ssam	const char *dbf;
444193240Ssam	FILE *db;
445193240Ssam	struct pci_vendor_info *cv;
446193240Ssam	struct pci_device_info *cd;
447193240Ssam	char buf[1024], str[1024];
448193240Ssam	char *ch;
449193240Ssam	int id, error;
450193240Ssam
451193240Ssam	/*
452193240Ssam	 * Locate the database and initialise.
453193240Ssam	 */
454193240Ssam	TAILQ_INIT(&pci_vendors);
455193240Ssam	if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
456193240Ssam		dbf = _PATH_PCIVDB;
457193240Ssam	if ((db = fopen(dbf, "r")) == NULL)
458193240Ssam		return(1);
459193240Ssam	cv = NULL;
460193240Ssam	cd = NULL;
461193240Ssam	error = 0;
462193240Ssam
463193240Ssam	/*
464193240Ssam	 * Scan input lines from the database
465193240Ssam	 */
466193240Ssam	for (;;) {
467193240Ssam		if (fgets(buf, sizeof(buf), db) == NULL)
468193240Ssam			break;
469193240Ssam
470193240Ssam		if ((ch = strchr(buf, '#')) != NULL)
471193240Ssam			*ch = '\0';
472193240Ssam		ch = strchr(buf, '\0') - 1;
473193240Ssam		while (ch > buf && isspace(*ch))
474193240Ssam			*ch-- = '\0';
475193240Ssam		if (ch <= buf)
476193240Ssam			continue;
477193240Ssam
478193240Ssam		/* Can't handle subvendor / subdevice entries yet */
479193240Ssam		if (buf[0] == '\t' && buf[1] == '\t')
480193240Ssam			continue;
481193240Ssam
482193240Ssam		/* Check for vendor entry */
483193240Ssam		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
484193240Ssam			if ((id == 0) || (strlen(str) < 1))
485193240Ssam				continue;
486193240Ssam			if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
487193240Ssam				warn("allocating vendor entry");
488193240Ssam				error = 1;
489193240Ssam				break;
490193240Ssam			}
491193240Ssam			if ((cv->desc = strdup(str)) == NULL) {
492193240Ssam				free(cv);
493193240Ssam				warn("allocating vendor description");
494193240Ssam				error = 1;
495193240Ssam				break;
496193240Ssam			}
497193240Ssam			cv->id = id;
498193240Ssam			TAILQ_INIT(&cv->devs);
499193240Ssam			TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
500193240Ssam			continue;
501193240Ssam		}
502193240Ssam
503193240Ssam		/* Check for device entry */
504193240Ssam		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
505193240Ssam			if ((id == 0) || (strlen(str) < 1))
506193240Ssam				continue;
507193240Ssam			if (cv == NULL) {
508193240Ssam				warnx("device entry with no vendor!");
509193240Ssam				continue;
510193240Ssam			}
511193240Ssam			if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
512193240Ssam				warn("allocating device entry");
513193240Ssam				error = 1;
514193240Ssam				break;
515193240Ssam			}
516193240Ssam			if ((cd->desc = strdup(str)) == NULL) {
517193240Ssam				free(cd);
518193240Ssam				warn("allocating device description");
519193240Ssam				error = 1;
520193240Ssam				break;
521193240Ssam			}
522193240Ssam			cd->id = id;
523193240Ssam			TAILQ_INSERT_TAIL(&cv->devs, cd, link);
524193240Ssam			continue;
525193240Ssam		}
526193240Ssam
527193240Ssam		/* It's a comment or junk, ignore it */
528193240Ssam	}
529193240Ssam	if (ferror(db))
530193240Ssam		error = 1;
531193240Ssam	fclose(db);
532193240Ssam
533193240Ssam	return(error);
534193240Ssam}
535193240Ssam
536193240Ssamuint32_t
537193240Ssamread_config(int fd, struct pcisel *sel, long reg, int width)
538193240Ssam{
539193240Ssam	struct pci_io pi;
540193240Ssam
541193240Ssam	pi.pi_sel = *sel;
542193240Ssam	pi.pi_reg = reg;
543193240Ssam	pi.pi_width = width;
544193240Ssam
545193240Ssam	if (ioctl(fd, PCIOCREAD, &pi) < 0)
546193240Ssam		err(1, "ioctl(PCIOCREAD)");
547193240Ssam
548193240Ssam	return (pi.pi_data);
549193240Ssam}
550193240Ssam
551193240Ssamstatic struct pcisel
552193240Ssamgetsel(const char *str)
553193240Ssam{
554193240Ssam	char *ep = strchr(str, '@');
555193240Ssam	char *epbase;
556193240Ssam	struct pcisel sel;
557193240Ssam	unsigned long selarr[4];
558193240Ssam	int i;
559193240Ssam
560193240Ssam	if (ep == NULL)
561193240Ssam		ep = (char *)str;
562193240Ssam	else
563193240Ssam		ep++;
564193240Ssam
565193240Ssam	epbase = ep;
566193240Ssam
567193240Ssam	if (strncmp(ep, "pci", 3) == 0) {
568193240Ssam		ep += 3;
569193240Ssam		i = 0;
570193240Ssam		do {
571193240Ssam			selarr[i++] = strtoul(ep, &ep, 10);
572193240Ssam		} while ((*ep == ':' || *ep == '.') && *++ep != '\0' && i < 4);
573193240Ssam
574193240Ssam		if (i > 2)
575193240Ssam			sel.pc_func = selarr[--i];
576193240Ssam		else
577193240Ssam			sel.pc_func = 0;
578193240Ssam		sel.pc_dev = selarr[--i];
579193240Ssam		sel.pc_bus = selarr[--i];
580193240Ssam		if (i > 0)
581193240Ssam			sel.pc_domain = selarr[--i];
582195171Ssam		else
583195171Ssam			sel.pc_domain = 0;
584195171Ssam	}
585193240Ssam	if (*ep != '\x0' || ep == epbase)
586193240Ssam		errx(1, "cannot parse selector %s", str);
587193240Ssam	return sel;
588193240Ssam}
589193240Ssam
590193240Ssamstatic void
591193240Ssamreadone(int fd, struct pcisel *sel, long reg, int width)
592193240Ssam{
593193240Ssam
594193240Ssam	printf("%0*x", width*2, read_config(fd, sel, reg, width));
595193240Ssam}
596193240Ssam
597193240Ssamstatic void
598193240Ssamreadit(const char *name, const char *reg, int width)
599193240Ssam{
600193240Ssam	long rstart;
601193240Ssam	long rend;
602193240Ssam	long r;
603193240Ssam	char *end;
604193240Ssam	int i;
605193240Ssam	int fd;
606193240Ssam	struct pcisel sel;
607193240Ssam
608193240Ssam	fd = open(_PATH_DEVPCI, O_RDWR, 0);
609193240Ssam	if (fd < 0)
610193240Ssam		err(1, "%s", _PATH_DEVPCI);
611193240Ssam
612193240Ssam	rend = rstart = strtol(reg, &end, 0);
613193240Ssam	if (end && *end == ':') {
614193240Ssam		end++;
615193240Ssam		rend = strtol(end, (char **) 0, 0);
616193240Ssam	}
617193240Ssam	sel = getsel(name);
618193240Ssam	for (i = 1, r = rstart; r <= rend; i++, r += width) {
619193240Ssam		readone(fd, &sel, r, width);
620193240Ssam		if (i && !(i % 8))
621193240Ssam			putchar(' ');
622193240Ssam		putchar(i % (16/width) ? ' ' : '\n');
623193240Ssam	}
624193240Ssam	if (i % (16/width) != 1)
625193240Ssam		putchar('\n');
626193240Ssam	close(fd);
627193240Ssam}
628193240Ssam
629193240Ssamstatic void
630193240Ssamwriteit(const char *name, const char *reg, const char *data, int width)
631193240Ssam{
632193240Ssam	int fd;
633193240Ssam	struct pci_io pi;
634193240Ssam
635193240Ssam	pi.pi_sel = getsel(name);
636193240Ssam	pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
637193240Ssam	pi.pi_width = width;
638193240Ssam	pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
639193240Ssam
640193240Ssam	fd = open(_PATH_DEVPCI, O_RDWR, 0);
641193240Ssam	if (fd < 0)
642193240Ssam		err(1, "%s", _PATH_DEVPCI);
643193240Ssam
644193240Ssam	if (ioctl(fd, PCIOCWRITE, &pi) < 0)
645193240Ssam		err(1, "ioctl(PCIOCWRITE)");
646193240Ssam}
647193240Ssam
648193240Ssamstatic void
649193240Ssamchkattached(const char *name, int width)
650193240Ssam{
651193240Ssam	int fd;
652193240Ssam	struct pci_io pi;
653193240Ssam
654193240Ssam	pi.pi_sel = getsel(name);
655193240Ssam	pi.pi_reg = 0;
656193240Ssam	pi.pi_width = width;
657193240Ssam	pi.pi_data = 0;
658193240Ssam
659193240Ssam	fd = open(_PATH_DEVPCI, O_RDWR, 0);
660193240Ssam	if (fd < 0)
661193240Ssam		err(1, "%s", _PATH_DEVPCI);
662193240Ssam
663193240Ssam	if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
664193240Ssam		err(1, "ioctl(PCIOCATTACHED)");
665193240Ssam
666193240Ssam	exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
667193240Ssam	printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
668193240Ssam}
669193240Ssam