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#include <sys/types.h>
31#include <sys/fcntl.h>
32#include <sys/mman.h>
33#include <sys/pciio.h>
34#include <sys/queue.h>
35
36#include <vm/vm.h>
37
38#include <dev/pci/pcireg.h>
39
40#include <assert.h>
41#include <ctype.h>
42#include <err.h>
43#include <inttypes.h>
44#include <stdbool.h>
45#include <stdlib.h>
46#include <stdio.h>
47#include <string.h>
48#include <unistd.h>
49
50#include "pathnames.h"
51#include "pciconf.h"
52
53struct pci_device_info
54{
55    TAILQ_ENTRY(pci_device_info)	link;
56    int					id;
57    char				*desc;
58};
59
60struct pci_vendor_info
61{
62    TAILQ_ENTRY(pci_vendor_info)	link;
63    TAILQ_HEAD(,pci_device_info)	devs;
64    int					id;
65    char				*desc;
66};
67
68static TAILQ_HEAD(,pci_vendor_info)	pci_vendors;
69
70static struct pcisel getsel(const char *str);
71static void list_bridge(int fd, struct pci_conf *p);
72static void list_bars(int fd, struct pci_conf *p);
73static void list_devs(const char *name, int verbose, int bars, int bridge,
74    int caps, int errors, int vpd, int listmode);
75static void list_verbose(struct pci_conf *p);
76static void list_vpd(int fd, struct pci_conf *p);
77static const char *guess_class(struct pci_conf *p);
78static const char *guess_subclass(struct pci_conf *p);
79static int load_vendors(void);
80static void readit(const char *, const char *, int);
81static void writeit(const char *, const char *, const char *, int);
82static void chkattached(const char *);
83static void dump_bar(const char *name, const char *reg, const char *bar_start,
84    const char *bar_count, int width, int verbose);
85
86static int exitstatus = 0;
87
88static void
89usage(void)
90{
91
92	fprintf(stderr, "%s",
93		"usage: pciconf -l [-BbcevV] [device]\n"
94		"       pciconf -a device\n"
95		"       pciconf -r [-b | -h] device addr[:addr2]\n"
96		"       pciconf -w [-b | -h] device addr value\n"
97		"       pciconf -D [-b | -h | -x] device bar [start [count]]"
98		"\n");
99	exit(1);
100}
101
102int
103main(int argc, char **argv)
104{
105	int c, width;
106	int listmode, readmode, writemode, attachedmode, dumpbarmode;
107	int bars, bridge, caps, errors, verbose, vpd;
108
109	listmode = readmode = writemode = attachedmode = dumpbarmode = 0;
110	bars = bridge = caps = errors = verbose = vpd= 0;
111	width = 4;
112
113	while ((c = getopt(argc, argv, "aBbcDehlrwVv")) != -1) {
114		switch(c) {
115		case 'a':
116			attachedmode = 1;
117			break;
118
119		case 'B':
120			bridge = 1;
121			break;
122
123		case 'b':
124			bars = 1;
125			width = 1;
126			break;
127
128		case 'c':
129			caps++;
130			break;
131
132		case 'D':
133			dumpbarmode = 1;
134			break;
135
136		case 'e':
137			errors = 1;
138			break;
139
140		case 'h':
141			width = 2;
142			break;
143
144		case 'l':
145			listmode++;
146			break;
147
148		case 'r':
149			readmode = 1;
150			break;
151
152		case 'w':
153			writemode = 1;
154			break;
155
156		case 'v':
157			verbose = 1;
158			break;
159
160		case 'V':
161			vpd = 1;
162			break;
163
164		case 'x':
165			width = 8;
166			break;
167
168		default:
169			usage();
170		}
171	}
172
173	if ((listmode && optind >= argc + 1)
174	    || (writemode && optind + 3 != argc)
175	    || (readmode && optind + 2 != argc)
176	    || (attachedmode && optind + 1 != argc)
177	    || (dumpbarmode && (optind + 2 > argc || optind + 4 < argc))
178	    || (width == 8 && !dumpbarmode))
179		usage();
180
181	if (listmode) {
182		list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose,
183		    bars, bridge, caps, errors, vpd, listmode);
184	} else if (attachedmode) {
185		chkattached(argv[optind]);
186	} else if (readmode) {
187		readit(argv[optind], argv[optind + 1], width);
188	} else if (writemode) {
189		writeit(argv[optind], argv[optind + 1], argv[optind + 2],
190		    width);
191	} else if (dumpbarmode) {
192		dump_bar(argv[optind], argv[optind + 1],
193		    optind + 2 < argc ? argv[optind + 2] : NULL,
194		    optind + 3 < argc ? argv[optind + 3] : NULL,
195		    width, verbose);
196	} else {
197		usage();
198	}
199
200	return (exitstatus);
201}
202
203static void
204list_devs(const char *name, int verbose, int bars, int bridge, int caps,
205    int errors, int vpd, int listmode)
206{
207	int fd;
208	struct pci_conf_io pc;
209	struct pci_conf conf[255], *p;
210	struct pci_match_conf patterns[1];
211	int none_count = 0;
212
213	if (verbose)
214		load_vendors();
215
216	fd = open(_PATH_DEVPCI, (bridge || caps || errors) ? O_RDWR : O_RDONLY,
217	    0);
218	if (fd < 0)
219		err(1, "%s", _PATH_DEVPCI);
220
221	bzero(&pc, sizeof(struct pci_conf_io));
222	pc.match_buf_len = sizeof(conf);
223	pc.matches = conf;
224	if (name != NULL) {
225		bzero(&patterns, sizeof(patterns));
226		patterns[0].pc_sel = getsel(name);
227		patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN |
228		    PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV |
229		    PCI_GETCONF_MATCH_FUNC;
230		pc.num_patterns = 1;
231		pc.pat_buf_len = sizeof(patterns);
232		pc.patterns = patterns;
233	}
234
235	do {
236		if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
237			err(1, "ioctl(PCIOCGETCONF)");
238
239		/*
240		 * 255 entries should be more than enough for most people,
241		 * but if someone has more devices, and then changes things
242		 * around between ioctls, we'll do the cheesy thing and
243		 * just bail.  The alternative would be to go back to the
244		 * beginning of the list, and print things twice, which may
245		 * not be desirable.
246		 */
247		if (pc.status == PCI_GETCONF_LIST_CHANGED) {
248			warnx("PCI device list changed, please try again");
249			exitstatus = 1;
250			close(fd);
251			return;
252		} else if (pc.status ==  PCI_GETCONF_ERROR) {
253			warnx("error returned from PCIOCGETCONF ioctl");
254			exitstatus = 1;
255			close(fd);
256			return;
257		}
258		if (listmode == 2)
259			printf("drv\tselector\tclass    rev  hdr  "
260			    "vendor device subven subdev\n");
261		for (p = conf; p < &conf[pc.num_matches]; p++) {
262			if (listmode == 2)
263				printf("%s%d@pci%d:%d:%d:%d:"
264				    "\t%06x   %02x   %02x   "
265				    "%04x   %04x   %04x   %04x\n",
266				    *p->pd_name ? p->pd_name : "none",
267				    *p->pd_name ? (int)p->pd_unit :
268				    none_count++, p->pc_sel.pc_domain,
269				    p->pc_sel.pc_bus, p->pc_sel.pc_dev,
270				    p->pc_sel.pc_func, (p->pc_class << 16) |
271				    (p->pc_subclass << 8) | p->pc_progif,
272				    p->pc_revid, p->pc_hdr,
273				    p->pc_vendor, p->pc_device,
274				    p->pc_subvendor, p->pc_subdevice);
275			else
276				printf("%s%d@pci%d:%d:%d:%d:"
277				    "\tclass=0x%06x rev=0x%02x hdr=0x%02x "
278				    "vendor=0x%04x device=0x%04x "
279				    "subvendor=0x%04x subdevice=0x%04x\n",
280				    *p->pd_name ? p->pd_name : "none",
281				    *p->pd_name ? (int)p->pd_unit :
282				    none_count++, p->pc_sel.pc_domain,
283				    p->pc_sel.pc_bus, p->pc_sel.pc_dev,
284				    p->pc_sel.pc_func, (p->pc_class << 16) |
285				    (p->pc_subclass << 8) | p->pc_progif,
286				    p->pc_revid, p->pc_hdr,
287				    p->pc_vendor, p->pc_device,
288				    p->pc_subvendor, p->pc_subdevice);
289			if (verbose)
290				list_verbose(p);
291			if (bars)
292				list_bars(fd, p);
293			if (bridge)
294				list_bridge(fd, p);
295			if (caps)
296				list_caps(fd, p, caps);
297			if (errors)
298				list_errors(fd, p);
299			if (vpd)
300				list_vpd(fd, p);
301		}
302	} while (pc.status == PCI_GETCONF_MORE_DEVS);
303
304	close(fd);
305}
306
307static void
308print_bus_range(int fd, struct pci_conf *p, int secreg, int subreg)
309{
310	uint8_t secbus, subbus;
311
312	secbus = read_config(fd, &p->pc_sel, secreg, 1);
313	subbus = read_config(fd, &p->pc_sel, subreg, 1);
314	printf("    bus range  = %u-%u\n", secbus, subbus);
315}
316
317static void
318print_window(int reg, const char *type, int range, uint64_t base,
319    uint64_t limit)
320{
321
322	printf("    window[%02x] = type %s, range %2d, addr %#jx-%#jx, %s\n",
323	    reg, type, range, (uintmax_t)base, (uintmax_t)limit,
324	    base < limit ? "enabled" : "disabled");
325}
326
327static void
328print_special_decode(bool isa, bool vga, bool subtractive)
329{
330	bool comma;
331
332	if (isa || vga || subtractive) {
333		comma = false;
334		printf("    decode     = ");
335		if (isa) {
336			printf("ISA");
337			comma = true;
338		}
339		if (vga) {
340			printf("%sVGA", comma ? ", " : "");
341			comma = true;
342		}
343		if (subtractive)
344			printf("%ssubtractive", comma ? ", " : "");
345		printf("\n");
346	}
347}
348
349static void
350print_bridge_windows(int fd, struct pci_conf *p)
351{
352	uint64_t base, limit;
353	uint32_t val;
354	uint16_t bctl;
355	bool subtractive;
356	int range;
357
358	/*
359	 * XXX: This assumes that a window with a base and limit of 0
360	 * is not implemented.  In theory a window might be programmed
361	 * at the smallest size with a base of 0, but those do not seem
362	 * common in practice.
363	 */
364	val = read_config(fd, &p->pc_sel, PCIR_IOBASEL_1, 1);
365	if (val != 0 || read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1) != 0) {
366		if ((val & PCIM_BRIO_MASK) == PCIM_BRIO_32) {
367			base = PCI_PPBIOBASE(
368			    read_config(fd, &p->pc_sel, PCIR_IOBASEH_1, 2),
369			    val);
370			limit = PCI_PPBIOLIMIT(
371			    read_config(fd, &p->pc_sel, PCIR_IOLIMITH_1, 2),
372			    read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1));
373			range = 32;
374		} else {
375			base = PCI_PPBIOBASE(0, val);
376			limit = PCI_PPBIOLIMIT(0,
377			    read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1));
378			range = 16;
379		}
380		print_window(PCIR_IOBASEL_1, "I/O Port", range, base, limit);
381	}
382
383	base = PCI_PPBMEMBASE(0,
384	    read_config(fd, &p->pc_sel, PCIR_MEMBASE_1, 2));
385	limit = PCI_PPBMEMLIMIT(0,
386	    read_config(fd, &p->pc_sel, PCIR_MEMLIMIT_1, 2));
387	print_window(PCIR_MEMBASE_1, "Memory", 32, base, limit);
388
389	val = read_config(fd, &p->pc_sel, PCIR_PMBASEL_1, 2);
390	if (val != 0 || read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2) != 0) {
391		if ((val & PCIM_BRPM_MASK) == PCIM_BRPM_64) {
392			base = PCI_PPBMEMBASE(
393			    read_config(fd, &p->pc_sel, PCIR_PMBASEH_1, 4),
394			    val);
395			limit = PCI_PPBMEMLIMIT(
396			    read_config(fd, &p->pc_sel, PCIR_PMLIMITH_1, 4),
397			    read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2));
398			range = 64;
399		} else {
400			base = PCI_PPBMEMBASE(0, val);
401			limit = PCI_PPBMEMLIMIT(0,
402			    read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2));
403			range = 32;
404		}
405		print_window(PCIR_PMBASEL_1, "Prefetchable Memory", range, base,
406		    limit);
407	}
408
409	/*
410	 * XXX: This list of bridges that are subtractive but do not set
411	 * progif to indicate it is copied from pci_pci.c.
412	 */
413	subtractive = p->pc_progif == PCIP_BRIDGE_PCI_SUBTRACTIVE;
414	switch (p->pc_device << 16 | p->pc_vendor) {
415	case 0xa002177d:		/* Cavium ThunderX */
416	case 0x124b8086:		/* Intel 82380FB Mobile */
417	case 0x060513d7:		/* Toshiba ???? */
418		subtractive = true;
419	}
420	if (p->pc_vendor == 0x8086 && (p->pc_device & 0xff00) == 0x2400)
421		subtractive = true;
422
423	bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_1, 2);
424	print_special_decode(bctl & PCIB_BCR_ISA_ENABLE,
425	    bctl & PCIB_BCR_VGA_ENABLE, subtractive);
426}
427
428static void
429print_cardbus_mem_window(int fd, struct pci_conf *p, int basereg, int limitreg,
430    bool prefetch)
431{
432
433	print_window(basereg, prefetch ? "Prefetchable Memory" : "Memory", 32,
434	    PCI_CBBMEMBASE(read_config(fd, &p->pc_sel, basereg, 4)),
435	    PCI_CBBMEMLIMIT(read_config(fd, &p->pc_sel, limitreg, 4)));
436}
437
438static void
439print_cardbus_io_window(int fd, struct pci_conf *p, int basereg, int limitreg)
440{
441	uint32_t base, limit;
442	uint32_t val;
443	int range;
444
445	val = read_config(fd, &p->pc_sel, basereg, 2);
446	if ((val & PCIM_CBBIO_MASK) == PCIM_CBBIO_32) {
447		base = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, basereg, 4));
448		limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 4));
449		range = 32;
450	} else {
451		base = PCI_CBBIOBASE(val);
452		limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 2));
453		range = 16;
454	}
455	print_window(basereg, "I/O Port", range, base, limit);
456}
457
458static void
459print_cardbus_windows(int fd, struct pci_conf *p)
460{
461	uint16_t bctl;
462
463	bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_2, 2);
464	print_cardbus_mem_window(fd, p, PCIR_MEMBASE0_2, PCIR_MEMLIMIT0_2,
465	    bctl & CBB_BCR_PREFETCH_0_ENABLE);
466	print_cardbus_mem_window(fd, p, PCIR_MEMBASE1_2, PCIR_MEMLIMIT1_2,
467	    bctl & CBB_BCR_PREFETCH_1_ENABLE);
468	print_cardbus_io_window(fd, p, PCIR_IOBASE0_2, PCIR_IOLIMIT0_2);
469	print_cardbus_io_window(fd, p, PCIR_IOBASE1_2, PCIR_IOLIMIT1_2);
470	print_special_decode(bctl & CBB_BCR_ISA_ENABLE,
471	    bctl & CBB_BCR_VGA_ENABLE, false);
472}
473
474static void
475list_bridge(int fd, struct pci_conf *p)
476{
477
478	switch (p->pc_hdr & PCIM_HDRTYPE) {
479	case PCIM_HDRTYPE_BRIDGE:
480		print_bus_range(fd, p, PCIR_SECBUS_1, PCIR_SUBBUS_1);
481		print_bridge_windows(fd, p);
482		break;
483	case PCIM_HDRTYPE_CARDBUS:
484		print_bus_range(fd, p, PCIR_SECBUS_2, PCIR_SUBBUS_2);
485		print_cardbus_windows(fd, p);
486		break;
487	}
488}
489
490static void
491list_bars(int fd, struct pci_conf *p)
492{
493	int i, max;
494
495	switch (p->pc_hdr & PCIM_HDRTYPE) {
496	case PCIM_HDRTYPE_NORMAL:
497		max = PCIR_MAX_BAR_0;
498		break;
499	case PCIM_HDRTYPE_BRIDGE:
500		max = PCIR_MAX_BAR_1;
501		break;
502	case PCIM_HDRTYPE_CARDBUS:
503		max = PCIR_MAX_BAR_2;
504		break;
505	default:
506		return;
507	}
508
509	for (i = 0; i <= max; i++)
510		print_bar(fd, p, "bar   ", PCIR_BAR(i));
511}
512
513void
514print_bar(int fd, struct pci_conf *p, const char *label, uint16_t bar_offset)
515{
516	uint64_t base;
517	const char *type;
518	struct pci_bar_io bar;
519	int range;
520
521	bar.pbi_sel = p->pc_sel;
522	bar.pbi_reg = bar_offset;
523	if (ioctl(fd, PCIOCGETBAR, &bar) < 0)
524		return;
525	if (PCI_BAR_IO(bar.pbi_base)) {
526		type = "I/O Port";
527		range = 32;
528		base = bar.pbi_base & PCIM_BAR_IO_BASE;
529	} else {
530		if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
531			type = "Prefetchable Memory";
532		else
533			type = "Memory";
534		switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
535		case PCIM_BAR_MEM_32:
536			range = 32;
537			break;
538		case PCIM_BAR_MEM_1MB:
539			range = 20;
540			break;
541		case PCIM_BAR_MEM_64:
542			range = 64;
543			break;
544		default:
545			range = -1;
546		}
547		base = bar.pbi_base & ~((uint64_t)0xf);
548	}
549	printf("    %s[%02x] = type %s, range %2d, base %#jx, ",
550	    label, bar_offset, type, range, (uintmax_t)base);
551	printf("size %ju, %s\n", (uintmax_t)bar.pbi_length,
552	    bar.pbi_enabled ? "enabled" : "disabled");
553}
554
555static void
556list_verbose(struct pci_conf *p)
557{
558	struct pci_vendor_info	*vi;
559	struct pci_device_info	*di;
560	const char *dp;
561
562	TAILQ_FOREACH(vi, &pci_vendors, link) {
563		if (vi->id == p->pc_vendor) {
564			printf("    vendor     = '%s'\n", vi->desc);
565			break;
566		}
567	}
568	if (vi == NULL) {
569		di = NULL;
570	} else {
571		TAILQ_FOREACH(di, &vi->devs, link) {
572			if (di->id == p->pc_device) {
573				printf("    device     = '%s'\n", di->desc);
574				break;
575			}
576		}
577	}
578	if ((dp = guess_class(p)) != NULL)
579		printf("    class      = %s\n", dp);
580	if ((dp = guess_subclass(p)) != NULL)
581		printf("    subclass   = %s\n", dp);
582}
583
584static void
585list_vpd(int fd, struct pci_conf *p)
586{
587	struct pci_list_vpd_io list;
588	struct pci_vpd_element *vpd, *end;
589
590	list.plvi_sel = p->pc_sel;
591	list.plvi_len = 0;
592	list.plvi_data = NULL;
593	if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0)
594		return;
595
596	list.plvi_data = malloc(list.plvi_len);
597	if (ioctl(fd, PCIOCLISTVPD, &list) < 0) {
598		free(list.plvi_data);
599		return;
600	}
601
602	vpd = list.plvi_data;
603	end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len);
604	for (; vpd < end; vpd = PVE_NEXT(vpd)) {
605		if (vpd->pve_flags == PVE_FLAG_IDENT) {
606			printf("    VPD ident  = '%.*s'\n",
607			    (int)vpd->pve_datalen, vpd->pve_data);
608			continue;
609		}
610
611		/* Ignore the checksum keyword. */
612		if (!(vpd->pve_flags & PVE_FLAG_RW) &&
613		    memcmp(vpd->pve_keyword, "RV", 2) == 0)
614			continue;
615
616		/* Ignore remaining read-write space. */
617		if (vpd->pve_flags & PVE_FLAG_RW &&
618		    memcmp(vpd->pve_keyword, "RW", 2) == 0)
619			continue;
620
621		/* Handle extended capability keyword. */
622		if (!(vpd->pve_flags & PVE_FLAG_RW) &&
623		    memcmp(vpd->pve_keyword, "CP", 2) == 0) {
624			printf("    VPD ro CP  = ID %02x in map 0x%x[0x%x]\n",
625			    (unsigned int)vpd->pve_data[0],
626			    PCIR_BAR((unsigned int)vpd->pve_data[1]),
627			    (unsigned int)vpd->pve_data[3] << 8 |
628			    (unsigned int)vpd->pve_data[2]);
629			continue;
630		}
631
632		/* Remaining keywords should all have ASCII values. */
633		printf("    VPD %s %c%c  = '%.*s'\n",
634		    vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro",
635		    vpd->pve_keyword[0], vpd->pve_keyword[1],
636		    (int)vpd->pve_datalen, vpd->pve_data);
637	}
638	free(list.plvi_data);
639}
640
641/*
642 * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
643 */
644static struct
645{
646	int	class;
647	int	subclass;
648	const char *desc;
649} pci_nomatch_tab[] = {
650	{PCIC_OLD,		-1,			"old"},
651	{PCIC_OLD,		PCIS_OLD_NONVGA,	"non-VGA display device"},
652	{PCIC_OLD,		PCIS_OLD_VGA,		"VGA-compatible display device"},
653	{PCIC_STORAGE,		-1,			"mass storage"},
654	{PCIC_STORAGE,		PCIS_STORAGE_SCSI,	"SCSI"},
655	{PCIC_STORAGE,		PCIS_STORAGE_IDE,	"ATA"},
656	{PCIC_STORAGE,		PCIS_STORAGE_FLOPPY,	"floppy disk"},
657	{PCIC_STORAGE,		PCIS_STORAGE_IPI,	"IPI"},
658	{PCIC_STORAGE,		PCIS_STORAGE_RAID,	"RAID"},
659	{PCIC_STORAGE,		PCIS_STORAGE_ATA_ADMA,	"ATA (ADMA)"},
660	{PCIC_STORAGE,		PCIS_STORAGE_SATA,	"SATA"},
661	{PCIC_STORAGE,		PCIS_STORAGE_SAS,	"SAS"},
662	{PCIC_STORAGE,		PCIS_STORAGE_NVM,	"NVM"},
663	{PCIC_STORAGE,		PCIS_STORAGE_UFS,	"UFS"},
664	{PCIC_NETWORK,		-1,			"network"},
665	{PCIC_NETWORK,		PCIS_NETWORK_ETHERNET,	"ethernet"},
666	{PCIC_NETWORK,		PCIS_NETWORK_TOKENRING,	"token ring"},
667	{PCIC_NETWORK,		PCIS_NETWORK_FDDI,	"fddi"},
668	{PCIC_NETWORK,		PCIS_NETWORK_ATM,	"ATM"},
669	{PCIC_NETWORK,		PCIS_NETWORK_ISDN,	"ISDN"},
670	{PCIC_NETWORK,		PCIS_NETWORK_WORLDFIP,	"WorldFip"},
671	{PCIC_NETWORK,		PCIS_NETWORK_PICMG,	"PICMG"},
672	{PCIC_NETWORK,		PCIS_NETWORK_INFINIBAND,	"InfiniBand"},
673	{PCIC_NETWORK,		PCIS_NETWORK_HFC,	"host fabric"},
674	{PCIC_DISPLAY,		-1,			"display"},
675	{PCIC_DISPLAY,		PCIS_DISPLAY_VGA,	"VGA"},
676	{PCIC_DISPLAY,		PCIS_DISPLAY_XGA,	"XGA"},
677	{PCIC_DISPLAY,		PCIS_DISPLAY_3D,	"3D"},
678	{PCIC_MULTIMEDIA,	-1,			"multimedia"},
679	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_VIDEO,	"video"},
680	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_AUDIO,	"audio"},
681	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_TELE,	"telephony"},
682	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_HDA,	"HDA"},
683	{PCIC_MEMORY,		-1,			"memory"},
684	{PCIC_MEMORY,		PCIS_MEMORY_RAM,	"RAM"},
685	{PCIC_MEMORY,		PCIS_MEMORY_FLASH,	"flash"},
686	{PCIC_BRIDGE,		-1,			"bridge"},
687	{PCIC_BRIDGE,		PCIS_BRIDGE_HOST,	"HOST-PCI"},
688	{PCIC_BRIDGE,		PCIS_BRIDGE_ISA,	"PCI-ISA"},
689	{PCIC_BRIDGE,		PCIS_BRIDGE_EISA,	"PCI-EISA"},
690	{PCIC_BRIDGE,		PCIS_BRIDGE_MCA,	"PCI-MCA"},
691	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI,	"PCI-PCI"},
692	{PCIC_BRIDGE,		PCIS_BRIDGE_PCMCIA,	"PCI-PCMCIA"},
693	{PCIC_BRIDGE,		PCIS_BRIDGE_NUBUS,	"PCI-NuBus"},
694	{PCIC_BRIDGE,		PCIS_BRIDGE_CARDBUS,	"PCI-CardBus"},
695	{PCIC_BRIDGE,		PCIS_BRIDGE_RACEWAY,	"PCI-RACEway"},
696	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI_TRANSPARENT,
697	    "Semi-transparent PCI-to-PCI"},
698	{PCIC_BRIDGE,		PCIS_BRIDGE_INFINIBAND,	"InfiniBand-PCI"},
699	{PCIC_BRIDGE,		PCIS_BRIDGE_AS_PCI,
700	    "AdvancedSwitching-PCI"},
701	{PCIC_SIMPLECOMM,	-1,			"simple comms"},
702	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_UART,	"UART"},	/* could detect 16550 */
703	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_PAR,	"parallel port"},
704	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MULSER,	"multiport serial"},
705	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MODEM,	"generic modem"},
706	{PCIC_BASEPERIPH,	-1,			"base peripheral"},
707	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PIC,	"interrupt controller"},
708	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_DMA,	"DMA controller"},
709	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_TIMER,	"timer"},
710	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RTC,	"realtime clock"},
711	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PCIHOT,	"PCI hot-plug controller"},
712	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_SDHC,	"SD host controller"},
713	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_IOMMU,	"IOMMU"},
714	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RCEC,
715	    "Root Complex Event Collector"},
716	{PCIC_INPUTDEV,		-1,			"input device"},
717	{PCIC_INPUTDEV,		PCIS_INPUTDEV_KEYBOARD,	"keyboard"},
718	{PCIC_INPUTDEV,		PCIS_INPUTDEV_DIGITIZER,"digitizer"},
719	{PCIC_INPUTDEV,		PCIS_INPUTDEV_MOUSE,	"mouse"},
720	{PCIC_INPUTDEV,		PCIS_INPUTDEV_SCANNER,	"scanner"},
721	{PCIC_INPUTDEV,		PCIS_INPUTDEV_GAMEPORT,	"gameport"},
722	{PCIC_DOCKING,		-1,			"docking station"},
723	{PCIC_PROCESSOR,	-1,			"processor"},
724	{PCIC_SERIALBUS,	-1,			"serial bus"},
725	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FW,	"FireWire"},
726	{PCIC_SERIALBUS,	PCIS_SERIALBUS_ACCESS,	"AccessBus"},
727	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SSA,	"SSA"},
728	{PCIC_SERIALBUS,	PCIS_SERIALBUS_USB,	"USB"},
729	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FC,	"Fibre Channel"},
730	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SMBUS,	"SMBus"},
731	{PCIC_SERIALBUS,	PCIS_SERIALBUS_INFINIBAND,	"InfiniBand"},
732	{PCIC_SERIALBUS,	PCIS_SERIALBUS_IPMI,	"IPMI"},
733	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SERCOS,	"SERCOS"},
734	{PCIC_SERIALBUS,	PCIS_SERIALBUS_CANBUS,	"CANbus"},
735	{PCIC_SERIALBUS,	PCIS_SERIALBUS_MIPI_I3C,	"MIPI I3C"},
736	{PCIC_WIRELESS,		-1,			"wireless controller"},
737	{PCIC_WIRELESS,		PCIS_WIRELESS_IRDA,	"iRDA"},
738	{PCIC_WIRELESS,		PCIS_WIRELESS_IR,	"IR"},
739	{PCIC_WIRELESS,		PCIS_WIRELESS_RF,	"RF"},
740	{PCIC_WIRELESS,		PCIS_WIRELESS_BLUETOOTH,	"bluetooth"},
741	{PCIC_WIRELESS,		PCIS_WIRELESS_BROADBAND,	"broadband"},
742	{PCIC_WIRELESS,		PCIS_WIRELESS_80211A,	"ethernet 802.11a"},
743	{PCIC_WIRELESS,		PCIS_WIRELESS_80211B,	"ethernet 802.11b"},
744	{PCIC_WIRELESS,		PCIS_WIRELESS_CELL,
745	    "cellular controller/modem"},
746	{PCIC_WIRELESS,		PCIS_WIRELESS_CELL_E,
747	    "cellular controller/modem plus ethernet"},
748	{PCIC_INTELLIIO,	-1,			"intelligent I/O controller"},
749	{PCIC_INTELLIIO,	PCIS_INTELLIIO_I2O,	"I2O"},
750	{PCIC_SATCOM,		-1,			"satellite communication"},
751	{PCIC_SATCOM,		PCIS_SATCOM_TV,		"sat TV"},
752	{PCIC_SATCOM,		PCIS_SATCOM_AUDIO,	"sat audio"},
753	{PCIC_SATCOM,		PCIS_SATCOM_VOICE,	"sat voice"},
754	{PCIC_SATCOM,		PCIS_SATCOM_DATA,	"sat data"},
755	{PCIC_CRYPTO,		-1,			"encrypt/decrypt"},
756	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"network/computer crypto"},
757	{PCIC_CRYPTO,		PCIS_CRYPTO_ENTERTAIN,	"entertainment crypto"},
758	{PCIC_DASP,		-1,			"dasp"},
759	{PCIC_DASP,		PCIS_DASP_DPIO,		"DPIO module"},
760	{PCIC_DASP,		PCIS_DASP_PERFCNTRS,	"performance counters"},
761	{PCIC_DASP,		PCIS_DASP_COMM_SYNC,	"communication synchronizer"},
762	{PCIC_DASP,		PCIS_DASP_MGMT_CARD,	"signal processing management"},
763	{PCIC_ACCEL,		-1,			"processing accelerators"},
764	{PCIC_ACCEL,		PCIS_ACCEL_PROCESSING,	"processing accelerators"},
765	{PCIC_INSTRUMENT,	-1,			"non-essential instrumentation"},
766	{0, 0,		NULL}
767};
768
769static const char *
770guess_class(struct pci_conf *p)
771{
772	int	i;
773
774	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
775		if (pci_nomatch_tab[i].class == p->pc_class)
776			return(pci_nomatch_tab[i].desc);
777	}
778	return(NULL);
779}
780
781static const char *
782guess_subclass(struct pci_conf *p)
783{
784	int	i;
785
786	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
787		if ((pci_nomatch_tab[i].class == p->pc_class) &&
788		    (pci_nomatch_tab[i].subclass == p->pc_subclass))
789			return(pci_nomatch_tab[i].desc);
790	}
791	return(NULL);
792}
793
794static int
795load_vendors(void)
796{
797	const char *dbf;
798	FILE *db;
799	struct pci_vendor_info *cv;
800	struct pci_device_info *cd;
801	char buf[1024], str[1024];
802	char *ch;
803	int id, error;
804
805	/*
806	 * Locate the database and initialise.
807	 */
808	TAILQ_INIT(&pci_vendors);
809	if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
810		dbf = _PATH_LPCIVDB;
811	if ((db = fopen(dbf, "r")) == NULL) {
812		dbf = _PATH_PCIVDB;
813		if ((db = fopen(dbf, "r")) == NULL)
814			return(1);
815	}
816	cv = NULL;
817	cd = NULL;
818	error = 0;
819
820	/*
821	 * Scan input lines from the database
822	 */
823	for (;;) {
824		if (fgets(buf, sizeof(buf), db) == NULL)
825			break;
826
827		if ((ch = strchr(buf, '#')) != NULL)
828			*ch = '\0';
829		ch = strchr(buf, '\0') - 1;
830		while (ch > buf && isspace(*ch))
831			*ch-- = '\0';
832		if (ch <= buf)
833			continue;
834
835		/* Can't handle subvendor / subdevice entries yet */
836		if (buf[0] == '\t' && buf[1] == '\t')
837			continue;
838
839		/* Check for vendor entry */
840		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
841			if ((id == 0) || (strlen(str) < 1))
842				continue;
843			if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
844				warn("allocating vendor entry");
845				error = 1;
846				break;
847			}
848			if ((cv->desc = strdup(str)) == NULL) {
849				free(cv);
850				warn("allocating vendor description");
851				error = 1;
852				break;
853			}
854			cv->id = id;
855			TAILQ_INIT(&cv->devs);
856			TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
857			continue;
858		}
859
860		/* Check for device entry */
861		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
862			if ((id == 0) || (strlen(str) < 1))
863				continue;
864			if (cv == NULL) {
865				warnx("device entry with no vendor!");
866				continue;
867			}
868			if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
869				warn("allocating device entry");
870				error = 1;
871				break;
872			}
873			if ((cd->desc = strdup(str)) == NULL) {
874				free(cd);
875				warn("allocating device description");
876				error = 1;
877				break;
878			}
879			cd->id = id;
880			TAILQ_INSERT_TAIL(&cv->devs, cd, link);
881			continue;
882		}
883
884		/* It's a comment or junk, ignore it */
885	}
886	if (ferror(db))
887		error = 1;
888	fclose(db);
889
890	return(error);
891}
892
893uint32_t
894read_config(int fd, struct pcisel *sel, long reg, int width)
895{
896	struct pci_io pi;
897
898	pi.pi_sel = *sel;
899	pi.pi_reg = reg;
900	pi.pi_width = width;
901
902	if (ioctl(fd, PCIOCREAD, &pi) < 0)
903		err(1, "ioctl(PCIOCREAD)");
904
905	return (pi.pi_data);
906}
907
908static struct pcisel
909getdevice(const char *name)
910{
911	struct pci_conf_io pc;
912	struct pci_conf conf[1];
913	struct pci_match_conf patterns[1];
914	char *cp;
915	int fd;
916
917	fd = open(_PATH_DEVPCI, O_RDONLY, 0);
918	if (fd < 0)
919		err(1, "%s", _PATH_DEVPCI);
920
921	bzero(&pc, sizeof(struct pci_conf_io));
922	pc.match_buf_len = sizeof(conf);
923	pc.matches = conf;
924
925	bzero(&patterns, sizeof(patterns));
926
927	/*
928	 * The pattern structure requires the unit to be split out from
929	 * the driver name.  Walk backwards from the end of the name to
930	 * find the start of the unit.
931	 */
932	if (name[0] == '\0')
933		errx(1, "Empty device name");
934	cp = strchr(name, '\0');
935	assert(cp != NULL && cp != name);
936	cp--;
937	while (cp != name && isdigit(cp[-1]))
938		cp--;
939	if (cp == name || !isdigit(*cp))
940		errx(1, "Invalid device name");
941	if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name))
942		errx(1, "Device name is too long");
943	memcpy(patterns[0].pd_name, name, cp - name);
944	patterns[0].pd_unit = strtol(cp, &cp, 10);
945	if (*cp != '\0')
946		errx(1, "Invalid device name");
947	patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT;
948	pc.num_patterns = 1;
949	pc.pat_buf_len = sizeof(patterns);
950	pc.patterns = patterns;
951
952	if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
953		err(1, "ioctl(PCIOCGETCONF)");
954	if (pc.status != PCI_GETCONF_LAST_DEVICE &&
955	    pc.status != PCI_GETCONF_MORE_DEVS)
956		errx(1, "error returned from PCIOCGETCONF ioctl");
957	close(fd);
958	if (pc.num_matches == 0)
959		errx(1, "Device not found");
960	return (conf[0].pc_sel);
961}
962
963static struct pcisel
964parsesel(const char *str)
965{
966	const char *ep;
967	char *eppos;
968	struct pcisel sel;
969	unsigned long selarr[4];
970	int i;
971
972	ep = strchr(str, '@');
973	if (ep != NULL)
974		ep++;
975	else
976		ep = str;
977
978	if (strncmp(ep, "pci", 3) == 0) {
979		ep += 3;
980		i = 0;
981		while (isdigit(*ep) && i < 4) {
982			selarr[i++] = strtoul(ep, &eppos, 10);
983			ep = eppos;
984			if (*ep == ':')
985				ep++;
986		}
987		if (i > 0 && *ep == '\0') {
988			sel.pc_func = (i > 2) ? selarr[--i] : 0;
989			sel.pc_dev = (i > 0) ? selarr[--i] : 0;
990			sel.pc_bus = (i > 0) ? selarr[--i] : 0;
991			sel.pc_domain = (i > 0) ? selarr[--i] : 0;
992			return (sel);
993		}
994	}
995	errx(1, "cannot parse selector %s", str);
996}
997
998static struct pcisel
999getsel(const char *str)
1000{
1001
1002	/*
1003	 * No device names contain colons and selectors always contain
1004	 * at least one colon.
1005	 */
1006	if (strchr(str, ':') == NULL)
1007		return (getdevice(str));
1008	else
1009		return (parsesel(str));
1010}
1011
1012static void
1013readone(int fd, struct pcisel *sel, long reg, int width)
1014{
1015
1016	printf("%0*x", width*2, read_config(fd, sel, reg, width));
1017}
1018
1019static void
1020readit(const char *name, const char *reg, int width)
1021{
1022	long rstart;
1023	long rend;
1024	long r;
1025	char *end;
1026	int i;
1027	int fd;
1028	struct pcisel sel;
1029
1030	fd = open(_PATH_DEVPCI, O_RDWR, 0);
1031	if (fd < 0)
1032		err(1, "%s", _PATH_DEVPCI);
1033
1034	rend = rstart = strtol(reg, &end, 0);
1035	if (end && *end == ':') {
1036		end++;
1037		rend = strtol(end, (char **) 0, 0);
1038	}
1039	sel = getsel(name);
1040	for (i = 1, r = rstart; r <= rend; i++, r += width) {
1041		readone(fd, &sel, r, width);
1042		if (i && !(i % 8))
1043			putchar(' ');
1044		putchar(i % (16/width) ? ' ' : '\n');
1045	}
1046	if (i % (16/width) != 1)
1047		putchar('\n');
1048	close(fd);
1049}
1050
1051static void
1052writeit(const char *name, const char *reg, const char *data, int width)
1053{
1054	int fd;
1055	struct pci_io pi;
1056
1057	pi.pi_sel = getsel(name);
1058	pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
1059	pi.pi_width = width;
1060	pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
1061
1062	fd = open(_PATH_DEVPCI, O_RDWR, 0);
1063	if (fd < 0)
1064		err(1, "%s", _PATH_DEVPCI);
1065
1066	if (ioctl(fd, PCIOCWRITE, &pi) < 0)
1067		err(1, "ioctl(PCIOCWRITE)");
1068	close(fd);
1069}
1070
1071static void
1072chkattached(const char *name)
1073{
1074	int fd;
1075	struct pci_io pi;
1076
1077	pi.pi_sel = getsel(name);
1078
1079	fd = open(_PATH_DEVPCI, O_RDWR, 0);
1080	if (fd < 0)
1081		err(1, "%s", _PATH_DEVPCI);
1082
1083	if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
1084		err(1, "ioctl(PCIOCATTACHED)");
1085
1086	exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
1087	printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
1088	close(fd);
1089}
1090
1091static void
1092dump_bar(const char *name, const char *reg, const char *bar_start,
1093    const char *bar_count, int width, int verbose)
1094{
1095	struct pci_bar_mmap pbm;
1096	uint32_t *dd;
1097	uint16_t *dh;
1098	uint8_t *db;
1099	uint64_t *dx, a, start, count;
1100	char *el;
1101	size_t res;
1102	int fd;
1103
1104	start = 0;
1105	if (bar_start != NULL) {
1106		start = strtoul(bar_start, &el, 0);
1107		if (*el != '\0')
1108			errx(1, "Invalid bar start specification %s",
1109			    bar_start);
1110	}
1111	count = 0;
1112	if (bar_count != NULL) {
1113		count = strtoul(bar_count, &el, 0);
1114		if (*el != '\0')
1115			errx(1, "Invalid count specification %s",
1116			    bar_count);
1117	}
1118
1119	pbm.pbm_sel = getsel(name);
1120	pbm.pbm_reg = strtoul(reg, &el, 0);
1121	if (*reg == '\0' || *el != '\0')
1122		errx(1, "Invalid bar specification %s", reg);
1123	pbm.pbm_flags = 0;
1124	pbm.pbm_memattr = VM_MEMATTR_DEVICE;
1125
1126	fd = open(_PATH_DEVPCI, O_RDWR, 0);
1127	if (fd < 0)
1128		err(1, "%s", _PATH_DEVPCI);
1129
1130	if (ioctl(fd, PCIOCBARMMAP, &pbm) < 0)
1131		err(1, "ioctl(PCIOCBARMMAP)");
1132
1133	if (count == 0)
1134		count = pbm.pbm_bar_length / width;
1135	if (start + count < start || (start + count) * width < (uint64_t)width)
1136		errx(1, "(start + count) x width overflow");
1137	if ((start + count) * width > pbm.pbm_bar_length) {
1138		if (start * width > pbm.pbm_bar_length)
1139			count = 0;
1140		else
1141			count = (pbm.pbm_bar_length - start * width) / width;
1142	}
1143	if (verbose) {
1144		fprintf(stderr,
1145		    "Dumping pci%d:%d:%d:%d BAR %x mapped base %p "
1146		    "off %#x length %#jx from %#jx count %#jx in %d-bytes\n",
1147		    pbm.pbm_sel.pc_domain, pbm.pbm_sel.pc_bus,
1148		    pbm.pbm_sel.pc_dev, pbm.pbm_sel.pc_func,
1149		    pbm.pbm_reg, pbm.pbm_map_base, pbm.pbm_bar_off,
1150		    pbm.pbm_bar_length, start, count, width);
1151	}
1152	switch (width) {
1153	case 1:
1154		db = (uint8_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
1155		    pbm.pbm_bar_off + start * width);
1156		for (a = 0; a < count; a += width, db++) {
1157			res = fwrite(db, width, 1, stdout);
1158			if (res != 1) {
1159				errx(1, "error writing to stdout");
1160				break;
1161			}
1162		}
1163		break;
1164	case 2:
1165		dh = (uint16_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
1166		    pbm.pbm_bar_off + start * width);
1167		for (a = 0; a < count; a += width, dh++) {
1168			res = fwrite(dh, width, 1, stdout);
1169			if (res != 1) {
1170				errx(1, "error writing to stdout");
1171				break;
1172			}
1173		}
1174		break;
1175	case 4:
1176		dd = (uint32_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
1177		    pbm.pbm_bar_off + start * width);
1178		for (a = 0; a < count; a += width, dd++) {
1179			res = fwrite(dd, width, 1, stdout);
1180			if (res != 1) {
1181				errx(1, "error writing to stdout");
1182				break;
1183			}
1184		}
1185		break;
1186	case 8:
1187		dx = (uint64_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
1188		    pbm.pbm_bar_off + start * width);
1189		for (a = 0; a < count; a += width, dx++) {
1190			res = fwrite(dx, width, 1, stdout);
1191			if (res != 1) {
1192				errx(1, "error writing to stdout");
1193				break;
1194			}
1195		}
1196		break;
1197	default:
1198		errx(1, "invalid access width");
1199	}
1200
1201	munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length);
1202	close(fd);
1203}
1204