1*477798fcSthorpej /* $NetBSD: pcictl.c,v 1.2 2001/09/14 17:28:36 thorpej Exp $ */ 20c91ac68Sthorpej 30c91ac68Sthorpej /* 40c91ac68Sthorpej * Copyright 2001 Wasabi Systems, Inc. 50c91ac68Sthorpej * All rights reserved. 60c91ac68Sthorpej * 70c91ac68Sthorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 80c91ac68Sthorpej * 90c91ac68Sthorpej * Redistribution and use in source and binary forms, with or without 100c91ac68Sthorpej * modification, are permitted provided that the following conditions 110c91ac68Sthorpej * are met: 120c91ac68Sthorpej * 1. Redistributions of source code must retain the above copyright 130c91ac68Sthorpej * notice, this list of conditions and the following disclaimer. 140c91ac68Sthorpej * 2. Redistributions in binary form must reproduce the above copyright 150c91ac68Sthorpej * notice, this list of conditions and the following disclaimer in the 160c91ac68Sthorpej * documentation and/or other materials provided with the distribution. 170c91ac68Sthorpej * 3. All advertising materials mentioning features or use of this software 180c91ac68Sthorpej * must display the following acknowledgement: 190c91ac68Sthorpej * This product includes software developed for the NetBSD Project by 200c91ac68Sthorpej * Wasabi Systems, Inc. 210c91ac68Sthorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 220c91ac68Sthorpej * or promote products derived from this software without specific prior 230c91ac68Sthorpej * written permission. 240c91ac68Sthorpej * 250c91ac68Sthorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 260c91ac68Sthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 270c91ac68Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 280c91ac68Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 290c91ac68Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 300c91ac68Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 310c91ac68Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 320c91ac68Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 330c91ac68Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 340c91ac68Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 350c91ac68Sthorpej * POSSIBILITY OF SUCH DAMAGE. 360c91ac68Sthorpej */ 370c91ac68Sthorpej 380c91ac68Sthorpej /* 390c91ac68Sthorpej * pcictl(8) -- a program to manipulate the PCI bus 400c91ac68Sthorpej */ 410c91ac68Sthorpej 420c91ac68Sthorpej #include <sys/param.h> 430c91ac68Sthorpej #include <sys/ioctl.h> 440c91ac68Sthorpej #include <err.h> 450c91ac68Sthorpej #include <errno.h> 460c91ac68Sthorpej #include <fcntl.h> 470c91ac68Sthorpej #include <pci.h> 480c91ac68Sthorpej #include <stdio.h> 490c91ac68Sthorpej #include <stdlib.h> 500c91ac68Sthorpej #include <string.h> 510c91ac68Sthorpej #include <unistd.h> 520c91ac68Sthorpej #include <util.h> 530c91ac68Sthorpej 540c91ac68Sthorpej #include <dev/pci/pcireg.h> 550c91ac68Sthorpej #include <dev/pci/pcidevs.h> 560c91ac68Sthorpej #include <dev/pci/pciio.h> 570c91ac68Sthorpej 580c91ac68Sthorpej struct command { 590c91ac68Sthorpej const char *cmd_name; 600c91ac68Sthorpej const char *arg_names; 610c91ac68Sthorpej void (*cmd_func)(int, char *[]); 620c91ac68Sthorpej int open_flags; 630c91ac68Sthorpej }; 640c91ac68Sthorpej 650c91ac68Sthorpej int main(int, char *[]); 660c91ac68Sthorpej void usage(void); 670c91ac68Sthorpej 680c91ac68Sthorpej int pcifd; 690c91ac68Sthorpej 700c91ac68Sthorpej struct pciio_businfo pci_businfo; 710c91ac68Sthorpej 720c91ac68Sthorpej const char *dvname; 730c91ac68Sthorpej char dvname_store[MAXPATHLEN]; 740c91ac68Sthorpej const char *cmdname; 750c91ac68Sthorpej const char *argnames; 760c91ac68Sthorpej 770c91ac68Sthorpej void cmd_list(int, char *[]); 780c91ac68Sthorpej void cmd_dump(int, char *[]); 790c91ac68Sthorpej 800c91ac68Sthorpej const struct command commands[] = { 810c91ac68Sthorpej { "list", 820c91ac68Sthorpej "[-b bus] [-d device] [-f function]", 830c91ac68Sthorpej cmd_list, 840c91ac68Sthorpej O_RDONLY }, 850c91ac68Sthorpej 860c91ac68Sthorpej { "dump", 870c91ac68Sthorpej "[-b bus] -d device [-f function]", 880c91ac68Sthorpej cmd_dump, 890c91ac68Sthorpej O_RDONLY }, 900c91ac68Sthorpej 910c91ac68Sthorpej { 0 }, 920c91ac68Sthorpej }; 930c91ac68Sthorpej 940c91ac68Sthorpej int parse_bdf(const char *); 950c91ac68Sthorpej 960c91ac68Sthorpej void scan_pci(int, int, int, void (*)(u_int, u_int, u_int)); 970c91ac68Sthorpej 980c91ac68Sthorpej void scan_pci_list(u_int, u_int, u_int); 990c91ac68Sthorpej void scan_pci_dump(u_int, u_int, u_int); 1000c91ac68Sthorpej 1010c91ac68Sthorpej int 1020c91ac68Sthorpej main(int argc, char *argv[]) 1030c91ac68Sthorpej { 1040c91ac68Sthorpej int i; 1050c91ac68Sthorpej 1060c91ac68Sthorpej /* Must have at least: device command */ 1070c91ac68Sthorpej if (argc < 3) 1080c91ac68Sthorpej usage(); 1090c91ac68Sthorpej 1100c91ac68Sthorpej /* Skip program name, get and skip device name, get command. */ 1110c91ac68Sthorpej dvname = argv[1]; 1120c91ac68Sthorpej cmdname = argv[2]; 1130c91ac68Sthorpej argv += 2; 1140c91ac68Sthorpej argc -= 2; 1150c91ac68Sthorpej 1160c91ac68Sthorpej /* Look up and call the command. */ 1170c91ac68Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 1180c91ac68Sthorpej if (strcmp(cmdname, commands[i].cmd_name) == 0) 1190c91ac68Sthorpej break; 1200c91ac68Sthorpej if (commands[i].cmd_name == NULL) 1210c91ac68Sthorpej errx(1, "unknown command: %s", cmdname); 1220c91ac68Sthorpej 1230c91ac68Sthorpej argnames = commands[i].arg_names; 1240c91ac68Sthorpej 1250c91ac68Sthorpej /* Open the device. */ 1260c91ac68Sthorpej pcifd = opendisk(dvname, commands[i].open_flags, dvname_store, 1270c91ac68Sthorpej sizeof(dvname_store), 1); 1280c91ac68Sthorpej if (pcifd == -1) 1290c91ac68Sthorpej err(1, "%s", dvname); 1300c91ac68Sthorpej 1310c91ac68Sthorpej dvname = dvname_store; 1320c91ac68Sthorpej 1330c91ac68Sthorpej /* Make sure the device is a PCI bus. */ 1340c91ac68Sthorpej if (ioctl(pcifd, PCI_IOC_BUSINFO, &pci_businfo) != 0) 1350c91ac68Sthorpej errx(1, "%s: not a PCI bus device\n", dvname); 1360c91ac68Sthorpej 1370c91ac68Sthorpej (*commands[i].cmd_func)(argc, argv); 1380c91ac68Sthorpej exit(0); 1390c91ac68Sthorpej } 1400c91ac68Sthorpej 1410c91ac68Sthorpej void 1420c91ac68Sthorpej usage() 1430c91ac68Sthorpej { 1440c91ac68Sthorpej int i; 1450c91ac68Sthorpej 1460c91ac68Sthorpej fprintf(stderr, "Usage: %s device command [arg [...]]\n", 1470c91ac68Sthorpej getprogname()); 1480c91ac68Sthorpej 1490c91ac68Sthorpej fprintf(stderr, " Available commands:\n"); 1500c91ac68Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 1510c91ac68Sthorpej fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 1520c91ac68Sthorpej commands[i].arg_names); 1530c91ac68Sthorpej 1540c91ac68Sthorpej exit(1); 1550c91ac68Sthorpej } 1560c91ac68Sthorpej 1570c91ac68Sthorpej void 1580c91ac68Sthorpej cmd_list(int argc, char *argv[]) 1590c91ac68Sthorpej { 1600c91ac68Sthorpej int bus, dev, func; 1610c91ac68Sthorpej int ch; 1620c91ac68Sthorpej 1630c91ac68Sthorpej bus = dev = func = -1; 1640c91ac68Sthorpej 1650c91ac68Sthorpej while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 1660c91ac68Sthorpej switch (ch) { 1670c91ac68Sthorpej case 'b': 1680c91ac68Sthorpej bus = parse_bdf(optarg); 1690c91ac68Sthorpej break; 1700c91ac68Sthorpej case 'd': 1710c91ac68Sthorpej dev = parse_bdf(optarg); 1720c91ac68Sthorpej break; 1730c91ac68Sthorpej case 'f': 1740c91ac68Sthorpej func = parse_bdf(optarg); 1750c91ac68Sthorpej break; 1760c91ac68Sthorpej default: 1770c91ac68Sthorpej usage(); 1780c91ac68Sthorpej } 1790c91ac68Sthorpej } 1800c91ac68Sthorpej argv += optind; 1810c91ac68Sthorpej argc -= optind; 1820c91ac68Sthorpej 1830c91ac68Sthorpej if (argc != 0) 1840c91ac68Sthorpej usage(); 1850c91ac68Sthorpej 1860c91ac68Sthorpej scan_pci(bus, dev, func, scan_pci_list); 1870c91ac68Sthorpej } 1880c91ac68Sthorpej 1890c91ac68Sthorpej void 1900c91ac68Sthorpej cmd_dump(int argc, char *argv[]) 1910c91ac68Sthorpej { 1920c91ac68Sthorpej int bus, dev, func; 1930c91ac68Sthorpej int ch; 1940c91ac68Sthorpej 1950c91ac68Sthorpej bus = pci_businfo.busno; 1960c91ac68Sthorpej func = 0; 1970c91ac68Sthorpej dev = -1; 1980c91ac68Sthorpej 199*477798fcSthorpej while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 2000c91ac68Sthorpej switch (ch) { 2010c91ac68Sthorpej case 'b': 2020c91ac68Sthorpej bus = parse_bdf(optarg); 2030c91ac68Sthorpej break; 2040c91ac68Sthorpej case 'd': 2050c91ac68Sthorpej dev = parse_bdf(optarg); 2060c91ac68Sthorpej break; 2070c91ac68Sthorpej case 'f': 2080c91ac68Sthorpej func = parse_bdf(optarg); 2090c91ac68Sthorpej break; 2100c91ac68Sthorpej default: 2110c91ac68Sthorpej usage(); 2120c91ac68Sthorpej } 2130c91ac68Sthorpej } 2140c91ac68Sthorpej argv += optind; 2150c91ac68Sthorpej argc -= optind; 2160c91ac68Sthorpej 2170c91ac68Sthorpej if (argc != 0) 2180c91ac68Sthorpej usage(); 2190c91ac68Sthorpej 2200c91ac68Sthorpej if (bus == -1) 2210c91ac68Sthorpej errx(1, "dump: wildcard bus number not permitted"); 2220c91ac68Sthorpej if (dev == -1) 2230c91ac68Sthorpej errx(1, "dump: must specify a device number"); 2240c91ac68Sthorpej if (func == -1) 2250c91ac68Sthorpej errx(1, "dump: wildcard function number not permitted"); 2260c91ac68Sthorpej 2270c91ac68Sthorpej scan_pci(bus, dev, func, scan_pci_dump); 2280c91ac68Sthorpej } 2290c91ac68Sthorpej 2300c91ac68Sthorpej int 2310c91ac68Sthorpej parse_bdf(const char *str) 2320c91ac68Sthorpej { 2330c91ac68Sthorpej 2340c91ac68Sthorpej if (strcmp(str, "all") == 0 || 2350c91ac68Sthorpej strcmp(str, "any") == 0) 2360c91ac68Sthorpej return (-1); 2370c91ac68Sthorpej 2380c91ac68Sthorpej return (atoi(str)); 2390c91ac68Sthorpej } 2400c91ac68Sthorpej 2410c91ac68Sthorpej void 2420c91ac68Sthorpej scan_pci(int busarg, int devarg, int funcarg, void (*cb)(u_int, u_int, u_int)) 2430c91ac68Sthorpej { 2440c91ac68Sthorpej u_int busmin, busmax; 2450c91ac68Sthorpej u_int devmin, devmax; 2460c91ac68Sthorpej u_int funcmin, funcmax; 2470c91ac68Sthorpej u_int bus, dev, func; 2480c91ac68Sthorpej pcireg_t id, bhlcr; 2490c91ac68Sthorpej 2500c91ac68Sthorpej if (busarg == -1) { 2510c91ac68Sthorpej busmin = 0; 2520c91ac68Sthorpej busmax = 255; 2530c91ac68Sthorpej } else 2540c91ac68Sthorpej busmin = busmax = busarg; 2550c91ac68Sthorpej 2560c91ac68Sthorpej if (devarg == -1) { 2570c91ac68Sthorpej devmin = 0; 2580c91ac68Sthorpej devmax = pci_businfo.maxdevs - 1; 2590c91ac68Sthorpej } else 2600c91ac68Sthorpej devmin = devmax = devarg; 2610c91ac68Sthorpej 2620c91ac68Sthorpej for (bus = busmin; bus <= busmax; bus++) { 2630c91ac68Sthorpej for (dev = devmin; dev <= devmax; dev++) { 2640c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, 0, 2650c91ac68Sthorpej PCI_BHLC_REG, &bhlcr) != 0) 2660c91ac68Sthorpej continue; 2670c91ac68Sthorpej if (funcarg == -1) { 2680c91ac68Sthorpej funcmin = 0; 2690c91ac68Sthorpej if (PCI_HDRTYPE_MULTIFN(bhlcr)) 2700c91ac68Sthorpej funcmax = 7; 2710c91ac68Sthorpej else 2720c91ac68Sthorpej funcmax = 0; 2730c91ac68Sthorpej } else 2740c91ac68Sthorpej funcmin = funcmax = funcarg; 2750c91ac68Sthorpej for (func = funcmin; func <= funcmax; func++) { 2760c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, 2770c91ac68Sthorpej func, PCI_ID_REG, &id) != 0) 2780c91ac68Sthorpej continue; 2790c91ac68Sthorpej 2800c91ac68Sthorpej /* Invalid vendor ID value? */ 2810c91ac68Sthorpej if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) 2820c91ac68Sthorpej continue; 2830c91ac68Sthorpej /* 2840c91ac68Sthorpej * XXX Not invalid, but we've done this 2850c91ac68Sthorpej * ~forever. 2860c91ac68Sthorpej */ 2870c91ac68Sthorpej if (PCI_VENDOR(id) == 0) 2880c91ac68Sthorpej continue; 2890c91ac68Sthorpej 2900c91ac68Sthorpej (*cb)(bus, dev, func); 2910c91ac68Sthorpej } 2920c91ac68Sthorpej } 2930c91ac68Sthorpej } 2940c91ac68Sthorpej } 2950c91ac68Sthorpej 2960c91ac68Sthorpej void 2970c91ac68Sthorpej scan_pci_list(u_int bus, u_int dev, u_int func) 2980c91ac68Sthorpej { 2990c91ac68Sthorpej pcireg_t id, class; 3000c91ac68Sthorpej char devinfo[256]; 3010c91ac68Sthorpej 3020c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, func, PCI_ID_REG, &id) != 0) 3030c91ac68Sthorpej return; 3040c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, func, PCI_CLASS_REG, &class) != 0) 3050c91ac68Sthorpej return; 3060c91ac68Sthorpej 3070c91ac68Sthorpej pci_devinfo(id, class, 1, devinfo); 3080c91ac68Sthorpej 3090c91ac68Sthorpej printf("%03u:%02u:%01u: %s\n", bus, dev, func, devinfo); 3100c91ac68Sthorpej } 3110c91ac68Sthorpej 3120c91ac68Sthorpej void 3130c91ac68Sthorpej scan_pci_dump(u_int bus, u_int dev, u_int func) 3140c91ac68Sthorpej { 3150c91ac68Sthorpej 3160c91ac68Sthorpej pci_conf_print(pcifd, bus, dev, func); 3170c91ac68Sthorpej } 318