1*9ef6a94eSkleink /* $NetBSD: pcictl.c,v 1.8 2004/04/24 13:41:51 kleink 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> 474c61cd90Stron #include <paths.h> 480c91ac68Sthorpej #include <pci.h> 490c91ac68Sthorpej #include <stdio.h> 500c91ac68Sthorpej #include <stdlib.h> 510c91ac68Sthorpej #include <string.h> 520c91ac68Sthorpej #include <unistd.h> 530c91ac68Sthorpej #include <util.h> 540c91ac68Sthorpej 550c91ac68Sthorpej #include <dev/pci/pcireg.h> 560c91ac68Sthorpej #include <dev/pci/pcidevs.h> 570c91ac68Sthorpej #include <dev/pci/pciio.h> 580c91ac68Sthorpej 590c91ac68Sthorpej struct command { 600c91ac68Sthorpej const char *cmd_name; 610c91ac68Sthorpej const char *arg_names; 620c91ac68Sthorpej void (*cmd_func)(int, char *[]); 630c91ac68Sthorpej int open_flags; 640c91ac68Sthorpej }; 650c91ac68Sthorpej 660c91ac68Sthorpej int main(int, char *[]); 670c91ac68Sthorpej void usage(void); 680c91ac68Sthorpej 690c91ac68Sthorpej int pcifd; 700c91ac68Sthorpej 710c91ac68Sthorpej struct pciio_businfo pci_businfo; 720c91ac68Sthorpej 730c91ac68Sthorpej const char *dvname; 740c91ac68Sthorpej char dvname_store[MAXPATHLEN]; 750c91ac68Sthorpej const char *cmdname; 760c91ac68Sthorpej const char *argnames; 770c91ac68Sthorpej 780c91ac68Sthorpej void cmd_list(int, char *[]); 790c91ac68Sthorpej void cmd_dump(int, char *[]); 800c91ac68Sthorpej 810c91ac68Sthorpej const struct command commands[] = { 820c91ac68Sthorpej { "list", 830c91ac68Sthorpej "[-b bus] [-d device] [-f function]", 840c91ac68Sthorpej cmd_list, 850c91ac68Sthorpej O_RDONLY }, 860c91ac68Sthorpej 870c91ac68Sthorpej { "dump", 880c91ac68Sthorpej "[-b bus] -d device [-f function]", 890c91ac68Sthorpej cmd_dump, 900c91ac68Sthorpej O_RDONLY }, 910c91ac68Sthorpej 920c91ac68Sthorpej { 0 }, 930c91ac68Sthorpej }; 940c91ac68Sthorpej 950c91ac68Sthorpej int parse_bdf(const char *); 960c91ac68Sthorpej 970c91ac68Sthorpej void scan_pci(int, int, int, void (*)(u_int, u_int, u_int)); 980c91ac68Sthorpej 990c91ac68Sthorpej void scan_pci_list(u_int, u_int, u_int); 1000c91ac68Sthorpej void scan_pci_dump(u_int, u_int, u_int); 1010c91ac68Sthorpej 1020c91ac68Sthorpej int 1030c91ac68Sthorpej main(int argc, char *argv[]) 1040c91ac68Sthorpej { 1050c91ac68Sthorpej int i; 1060c91ac68Sthorpej 1070c91ac68Sthorpej /* Must have at least: device command */ 1080c91ac68Sthorpej if (argc < 3) 1090c91ac68Sthorpej usage(); 1100c91ac68Sthorpej 1110c91ac68Sthorpej /* Skip program name, get and skip device name, get command. */ 1120c91ac68Sthorpej dvname = argv[1]; 1130c91ac68Sthorpej cmdname = argv[2]; 1140c91ac68Sthorpej argv += 2; 1150c91ac68Sthorpej argc -= 2; 1160c91ac68Sthorpej 1170c91ac68Sthorpej /* Look up and call the command. */ 1180c91ac68Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 1190c91ac68Sthorpej if (strcmp(cmdname, commands[i].cmd_name) == 0) 1200c91ac68Sthorpej break; 1210c91ac68Sthorpej if (commands[i].cmd_name == NULL) 1220c91ac68Sthorpej errx(1, "unknown command: %s", cmdname); 1230c91ac68Sthorpej 1240c91ac68Sthorpej argnames = commands[i].arg_names; 1250c91ac68Sthorpej 1260c91ac68Sthorpej /* Open the device. */ 1274c61cd90Stron if ((strchr(dvname, '/') == NULL) && 1284c61cd90Stron (snprintf(dvname_store, sizeof(dvname_store), _PATH_DEV "%s", 1294c61cd90Stron dvname) < sizeof(dvname_store))) 1300c91ac68Sthorpej dvname = dvname_store; 1314c61cd90Stron pcifd = open(dvname, commands[i].open_flags); 1324c61cd90Stron if (pcifd < 0) 1334c61cd90Stron err(1, "%s", dvname); 1340c91ac68Sthorpej 1350c91ac68Sthorpej /* Make sure the device is a PCI bus. */ 1360c91ac68Sthorpej if (ioctl(pcifd, PCI_IOC_BUSINFO, &pci_businfo) != 0) 137eda9e509Sgrant errx(1, "%s: not a PCI bus device", dvname); 1380c91ac68Sthorpej 1390c91ac68Sthorpej (*commands[i].cmd_func)(argc, argv); 1400c91ac68Sthorpej exit(0); 1410c91ac68Sthorpej } 1420c91ac68Sthorpej 1430c91ac68Sthorpej void 1440c91ac68Sthorpej usage() 1450c91ac68Sthorpej { 1460c91ac68Sthorpej int i; 1470c91ac68Sthorpej 148b635f565Sjmmv fprintf(stderr, "usage: %s device command [arg [...]]\n", 1490c91ac68Sthorpej getprogname()); 1500c91ac68Sthorpej 1510c91ac68Sthorpej fprintf(stderr, " Available commands:\n"); 1520c91ac68Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 1530c91ac68Sthorpej fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 1540c91ac68Sthorpej commands[i].arg_names); 1550c91ac68Sthorpej 1560c91ac68Sthorpej exit(1); 1570c91ac68Sthorpej } 1580c91ac68Sthorpej 1590c91ac68Sthorpej void 1600c91ac68Sthorpej cmd_list(int argc, char *argv[]) 1610c91ac68Sthorpej { 1620c91ac68Sthorpej int bus, dev, func; 1630c91ac68Sthorpej int ch; 1640c91ac68Sthorpej 1658b531ac9Sthorpej bus = pci_businfo.busno; 1668b531ac9Sthorpej dev = func = -1; 1670c91ac68Sthorpej 1680c91ac68Sthorpej while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 1690c91ac68Sthorpej switch (ch) { 1700c91ac68Sthorpej case 'b': 1710c91ac68Sthorpej bus = parse_bdf(optarg); 1720c91ac68Sthorpej break; 1730c91ac68Sthorpej case 'd': 1740c91ac68Sthorpej dev = parse_bdf(optarg); 1750c91ac68Sthorpej break; 1760c91ac68Sthorpej case 'f': 1770c91ac68Sthorpej func = parse_bdf(optarg); 1780c91ac68Sthorpej break; 1790c91ac68Sthorpej default: 1800c91ac68Sthorpej usage(); 1810c91ac68Sthorpej } 1820c91ac68Sthorpej } 1830c91ac68Sthorpej argv += optind; 1840c91ac68Sthorpej argc -= optind; 1850c91ac68Sthorpej 1860c91ac68Sthorpej if (argc != 0) 1870c91ac68Sthorpej usage(); 1880c91ac68Sthorpej 1890c91ac68Sthorpej scan_pci(bus, dev, func, scan_pci_list); 1900c91ac68Sthorpej } 1910c91ac68Sthorpej 1920c91ac68Sthorpej void 1930c91ac68Sthorpej cmd_dump(int argc, char *argv[]) 1940c91ac68Sthorpej { 1950c91ac68Sthorpej int bus, dev, func; 1960c91ac68Sthorpej int ch; 1970c91ac68Sthorpej 1980c91ac68Sthorpej bus = pci_businfo.busno; 1990c91ac68Sthorpej func = 0; 2000c91ac68Sthorpej dev = -1; 2010c91ac68Sthorpej 202477798fcSthorpej while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 2030c91ac68Sthorpej switch (ch) { 2040c91ac68Sthorpej case 'b': 2050c91ac68Sthorpej bus = parse_bdf(optarg); 2060c91ac68Sthorpej break; 2070c91ac68Sthorpej case 'd': 2080c91ac68Sthorpej dev = parse_bdf(optarg); 2090c91ac68Sthorpej break; 2100c91ac68Sthorpej case 'f': 2110c91ac68Sthorpej func = parse_bdf(optarg); 2120c91ac68Sthorpej break; 2130c91ac68Sthorpej default: 2140c91ac68Sthorpej usage(); 2150c91ac68Sthorpej } 2160c91ac68Sthorpej } 2170c91ac68Sthorpej argv += optind; 2180c91ac68Sthorpej argc -= optind; 2190c91ac68Sthorpej 2200c91ac68Sthorpej if (argc != 0) 2210c91ac68Sthorpej usage(); 2220c91ac68Sthorpej 2230c91ac68Sthorpej if (bus == -1) 2240c91ac68Sthorpej errx(1, "dump: wildcard bus number not permitted"); 2250c91ac68Sthorpej if (dev == -1) 2260c91ac68Sthorpej errx(1, "dump: must specify a device number"); 2270c91ac68Sthorpej if (func == -1) 2280c91ac68Sthorpej errx(1, "dump: wildcard function number not permitted"); 2290c91ac68Sthorpej 2300c91ac68Sthorpej scan_pci(bus, dev, func, scan_pci_dump); 2310c91ac68Sthorpej } 2320c91ac68Sthorpej 2330c91ac68Sthorpej int 2340c91ac68Sthorpej parse_bdf(const char *str) 2350c91ac68Sthorpej { 236f51bc9e6Sjoda long value; 237f51bc9e6Sjoda char *end; 2380c91ac68Sthorpej 2390c91ac68Sthorpej if (strcmp(str, "all") == 0 || 2400c91ac68Sthorpej strcmp(str, "any") == 0) 2410c91ac68Sthorpej return (-1); 2420c91ac68Sthorpej 243f51bc9e6Sjoda value = strtol(str, &end, 0); 244f51bc9e6Sjoda if(*end != '\0') 245f51bc9e6Sjoda errx(1, "\"%s\" is not a number", str); 246f51bc9e6Sjoda 247f51bc9e6Sjoda return value; 2480c91ac68Sthorpej } 2490c91ac68Sthorpej 2500c91ac68Sthorpej void 2510c91ac68Sthorpej scan_pci(int busarg, int devarg, int funcarg, void (*cb)(u_int, u_int, u_int)) 2520c91ac68Sthorpej { 2530c91ac68Sthorpej u_int busmin, busmax; 2540c91ac68Sthorpej u_int devmin, devmax; 2550c91ac68Sthorpej u_int funcmin, funcmax; 2560c91ac68Sthorpej u_int bus, dev, func; 2570c91ac68Sthorpej pcireg_t id, bhlcr; 2580c91ac68Sthorpej 2590c91ac68Sthorpej if (busarg == -1) { 2600c91ac68Sthorpej busmin = 0; 2610c91ac68Sthorpej busmax = 255; 2620c91ac68Sthorpej } else 2630c91ac68Sthorpej busmin = busmax = busarg; 2640c91ac68Sthorpej 2650c91ac68Sthorpej if (devarg == -1) { 2660c91ac68Sthorpej devmin = 0; 2670c91ac68Sthorpej devmax = pci_businfo.maxdevs - 1; 2680c91ac68Sthorpej } else 2690c91ac68Sthorpej devmin = devmax = devarg; 2700c91ac68Sthorpej 2710c91ac68Sthorpej for (bus = busmin; bus <= busmax; bus++) { 2720c91ac68Sthorpej for (dev = devmin; dev <= devmax; dev++) { 2730c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, 0, 2740c91ac68Sthorpej PCI_BHLC_REG, &bhlcr) != 0) 2750c91ac68Sthorpej continue; 2760c91ac68Sthorpej if (funcarg == -1) { 2770c91ac68Sthorpej funcmin = 0; 2780c91ac68Sthorpej if (PCI_HDRTYPE_MULTIFN(bhlcr)) 2790c91ac68Sthorpej funcmax = 7; 2800c91ac68Sthorpej else 2810c91ac68Sthorpej funcmax = 0; 2820c91ac68Sthorpej } else 2830c91ac68Sthorpej funcmin = funcmax = funcarg; 2840c91ac68Sthorpej for (func = funcmin; func <= funcmax; func++) { 2850c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, 2860c91ac68Sthorpej func, PCI_ID_REG, &id) != 0) 2870c91ac68Sthorpej continue; 2880c91ac68Sthorpej 2890c91ac68Sthorpej /* Invalid vendor ID value? */ 2900c91ac68Sthorpej if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) 2910c91ac68Sthorpej continue; 2920c91ac68Sthorpej /* 2930c91ac68Sthorpej * XXX Not invalid, but we've done this 2940c91ac68Sthorpej * ~forever. 2950c91ac68Sthorpej */ 2960c91ac68Sthorpej if (PCI_VENDOR(id) == 0) 2970c91ac68Sthorpej continue; 2980c91ac68Sthorpej 2990c91ac68Sthorpej (*cb)(bus, dev, func); 3000c91ac68Sthorpej } 3010c91ac68Sthorpej } 3020c91ac68Sthorpej } 3030c91ac68Sthorpej } 3040c91ac68Sthorpej 3050c91ac68Sthorpej void 3060c91ac68Sthorpej scan_pci_list(u_int bus, u_int dev, u_int func) 3070c91ac68Sthorpej { 3080c91ac68Sthorpej pcireg_t id, class; 3090c91ac68Sthorpej char devinfo[256]; 3100c91ac68Sthorpej 3110c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, func, PCI_ID_REG, &id) != 0) 3120c91ac68Sthorpej return; 3130c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, func, PCI_CLASS_REG, &class) != 0) 3140c91ac68Sthorpej return; 3150c91ac68Sthorpej 316*9ef6a94eSkleink pci_devinfo(id, class, 1, devinfo, sizeof(devinfo)); 3170c91ac68Sthorpej 3180c91ac68Sthorpej printf("%03u:%02u:%01u: %s\n", bus, dev, func, devinfo); 3190c91ac68Sthorpej } 3200c91ac68Sthorpej 3210c91ac68Sthorpej void 3220c91ac68Sthorpej scan_pci_dump(u_int bus, u_int dev, u_int func) 3230c91ac68Sthorpej { 3240c91ac68Sthorpej 3250c91ac68Sthorpej pci_conf_print(pcifd, bus, dev, func); 3260c91ac68Sthorpej } 327