1*fe81bd2fShubertf /* $NetBSD: pcictl.c,v 1.10 2006/10/01 00:13:28 hubertf 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; 77*fe81bd2fShubertf int print_numbers = 0; 780c91ac68Sthorpej 790c91ac68Sthorpej void cmd_list(int, char *[]); 800c91ac68Sthorpej void cmd_dump(int, char *[]); 810c91ac68Sthorpej 820c91ac68Sthorpej const struct command commands[] = { 830c91ac68Sthorpej { "list", 84*fe81bd2fShubertf "[-n] [-b bus] [-d device] [-f function]", 850c91ac68Sthorpej cmd_list, 860c91ac68Sthorpej O_RDONLY }, 870c91ac68Sthorpej 880c91ac68Sthorpej { "dump", 890c91ac68Sthorpej "[-b bus] -d device [-f function]", 900c91ac68Sthorpej cmd_dump, 910c91ac68Sthorpej O_RDONLY }, 920c91ac68Sthorpej 930c91ac68Sthorpej { 0 }, 940c91ac68Sthorpej }; 950c91ac68Sthorpej 960c91ac68Sthorpej int parse_bdf(const char *); 970c91ac68Sthorpej 980c91ac68Sthorpej void scan_pci(int, int, int, void (*)(u_int, u_int, u_int)); 990c91ac68Sthorpej 1000c91ac68Sthorpej void scan_pci_list(u_int, u_int, u_int); 1010c91ac68Sthorpej void scan_pci_dump(u_int, u_int, u_int); 1020c91ac68Sthorpej 1030c91ac68Sthorpej int 1040c91ac68Sthorpej main(int argc, char *argv[]) 1050c91ac68Sthorpej { 1060c91ac68Sthorpej int i; 1070c91ac68Sthorpej 1080c91ac68Sthorpej /* Must have at least: device command */ 1090c91ac68Sthorpej if (argc < 3) 1100c91ac68Sthorpej usage(); 1110c91ac68Sthorpej 1120c91ac68Sthorpej /* Skip program name, get and skip device name, get command. */ 1130c91ac68Sthorpej dvname = argv[1]; 1140c91ac68Sthorpej cmdname = argv[2]; 1150c91ac68Sthorpej argv += 2; 1160c91ac68Sthorpej argc -= 2; 1170c91ac68Sthorpej 1180c91ac68Sthorpej /* Look up and call the command. */ 1190c91ac68Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 1200c91ac68Sthorpej if (strcmp(cmdname, commands[i].cmd_name) == 0) 1210c91ac68Sthorpej break; 1220c91ac68Sthorpej if (commands[i].cmd_name == NULL) 1230c91ac68Sthorpej errx(1, "unknown command: %s", cmdname); 1240c91ac68Sthorpej 1250c91ac68Sthorpej argnames = commands[i].arg_names; 1260c91ac68Sthorpej 1270c91ac68Sthorpej /* Open the device. */ 1284c61cd90Stron if ((strchr(dvname, '/') == NULL) && 1294c61cd90Stron (snprintf(dvname_store, sizeof(dvname_store), _PATH_DEV "%s", 1304c61cd90Stron dvname) < sizeof(dvname_store))) 1310c91ac68Sthorpej dvname = dvname_store; 1324c61cd90Stron pcifd = open(dvname, commands[i].open_flags); 1334c61cd90Stron if (pcifd < 0) 1344c61cd90Stron err(1, "%s", dvname); 1350c91ac68Sthorpej 1360c91ac68Sthorpej /* Make sure the device is a PCI bus. */ 1370c91ac68Sthorpej if (ioctl(pcifd, PCI_IOC_BUSINFO, &pci_businfo) != 0) 138eda9e509Sgrant errx(1, "%s: not a PCI bus device", dvname); 1390c91ac68Sthorpej 1400c91ac68Sthorpej (*commands[i].cmd_func)(argc, argv); 1410c91ac68Sthorpej exit(0); 1420c91ac68Sthorpej } 1430c91ac68Sthorpej 1440c91ac68Sthorpej void 1450c91ac68Sthorpej usage() 1460c91ac68Sthorpej { 1470c91ac68Sthorpej int i; 1480c91ac68Sthorpej 149b635f565Sjmmv fprintf(stderr, "usage: %s device command [arg [...]]\n", 1500c91ac68Sthorpej getprogname()); 1510c91ac68Sthorpej 1520c91ac68Sthorpej fprintf(stderr, " Available commands:\n"); 1530c91ac68Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 1540c91ac68Sthorpej fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 1550c91ac68Sthorpej commands[i].arg_names); 1560c91ac68Sthorpej 1570c91ac68Sthorpej exit(1); 1580c91ac68Sthorpej } 1590c91ac68Sthorpej 1600c91ac68Sthorpej void 1610c91ac68Sthorpej cmd_list(int argc, char *argv[]) 1620c91ac68Sthorpej { 1630c91ac68Sthorpej int bus, dev, func; 1640c91ac68Sthorpej int ch; 1650c91ac68Sthorpej 1668b531ac9Sthorpej bus = pci_businfo.busno; 1678b531ac9Sthorpej dev = func = -1; 1680c91ac68Sthorpej 169*fe81bd2fShubertf while ((ch = getopt(argc, argv, "nb:d:f:")) != -1) { 1700c91ac68Sthorpej switch (ch) { 1710c91ac68Sthorpej case 'b': 1720c91ac68Sthorpej bus = parse_bdf(optarg); 1730c91ac68Sthorpej break; 1740c91ac68Sthorpej case 'd': 1750c91ac68Sthorpej dev = parse_bdf(optarg); 1760c91ac68Sthorpej break; 1770c91ac68Sthorpej case 'f': 1780c91ac68Sthorpej func = parse_bdf(optarg); 1790c91ac68Sthorpej break; 180*fe81bd2fShubertf case 'n': 181*fe81bd2fShubertf print_numbers = 1; 182*fe81bd2fShubertf break; 1830c91ac68Sthorpej default: 1840c91ac68Sthorpej usage(); 1850c91ac68Sthorpej } 1860c91ac68Sthorpej } 1870c91ac68Sthorpej argv += optind; 1880c91ac68Sthorpej argc -= optind; 1890c91ac68Sthorpej 1900c91ac68Sthorpej if (argc != 0) 1910c91ac68Sthorpej usage(); 1920c91ac68Sthorpej 1930c91ac68Sthorpej scan_pci(bus, dev, func, scan_pci_list); 1940c91ac68Sthorpej } 1950c91ac68Sthorpej 1960c91ac68Sthorpej void 1970c91ac68Sthorpej cmd_dump(int argc, char *argv[]) 1980c91ac68Sthorpej { 1990c91ac68Sthorpej int bus, dev, func; 2000c91ac68Sthorpej int ch; 2010c91ac68Sthorpej 2020c91ac68Sthorpej bus = pci_businfo.busno; 2030c91ac68Sthorpej func = 0; 2040c91ac68Sthorpej dev = -1; 2050c91ac68Sthorpej 206477798fcSthorpej while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 2070c91ac68Sthorpej switch (ch) { 2080c91ac68Sthorpej case 'b': 2090c91ac68Sthorpej bus = parse_bdf(optarg); 2100c91ac68Sthorpej break; 2110c91ac68Sthorpej case 'd': 2120c91ac68Sthorpej dev = parse_bdf(optarg); 2130c91ac68Sthorpej break; 2140c91ac68Sthorpej case 'f': 2150c91ac68Sthorpej func = parse_bdf(optarg); 2160c91ac68Sthorpej break; 2170c91ac68Sthorpej default: 2180c91ac68Sthorpej usage(); 2190c91ac68Sthorpej } 2200c91ac68Sthorpej } 2210c91ac68Sthorpej argv += optind; 2220c91ac68Sthorpej argc -= optind; 2230c91ac68Sthorpej 2240c91ac68Sthorpej if (argc != 0) 2250c91ac68Sthorpej usage(); 2260c91ac68Sthorpej 2270c91ac68Sthorpej if (bus == -1) 2280c91ac68Sthorpej errx(1, "dump: wildcard bus number not permitted"); 2290c91ac68Sthorpej if (dev == -1) 2300c91ac68Sthorpej errx(1, "dump: must specify a device number"); 2310c91ac68Sthorpej if (func == -1) 2320c91ac68Sthorpej errx(1, "dump: wildcard function number not permitted"); 2330c91ac68Sthorpej 2340c91ac68Sthorpej scan_pci(bus, dev, func, scan_pci_dump); 2350c91ac68Sthorpej } 2360c91ac68Sthorpej 2370c91ac68Sthorpej int 2380c91ac68Sthorpej parse_bdf(const char *str) 2390c91ac68Sthorpej { 240f51bc9e6Sjoda long value; 241f51bc9e6Sjoda char *end; 2420c91ac68Sthorpej 2430c91ac68Sthorpej if (strcmp(str, "all") == 0 || 2440c91ac68Sthorpej strcmp(str, "any") == 0) 2450c91ac68Sthorpej return (-1); 2460c91ac68Sthorpej 247f51bc9e6Sjoda value = strtol(str, &end, 0); 248f51bc9e6Sjoda if(*end != '\0') 249f51bc9e6Sjoda errx(1, "\"%s\" is not a number", str); 250f51bc9e6Sjoda 251f51bc9e6Sjoda return value; 2520c91ac68Sthorpej } 2530c91ac68Sthorpej 2540c91ac68Sthorpej void 2550c91ac68Sthorpej scan_pci(int busarg, int devarg, int funcarg, void (*cb)(u_int, u_int, u_int)) 2560c91ac68Sthorpej { 2570c91ac68Sthorpej u_int busmin, busmax; 2580c91ac68Sthorpej u_int devmin, devmax; 2590c91ac68Sthorpej u_int funcmin, funcmax; 2600c91ac68Sthorpej u_int bus, dev, func; 2610c91ac68Sthorpej pcireg_t id, bhlcr; 2620c91ac68Sthorpej 2630c91ac68Sthorpej if (busarg == -1) { 2640c91ac68Sthorpej busmin = 0; 2650c91ac68Sthorpej busmax = 255; 2660c91ac68Sthorpej } else 2670c91ac68Sthorpej busmin = busmax = busarg; 2680c91ac68Sthorpej 2690c91ac68Sthorpej if (devarg == -1) { 2700c91ac68Sthorpej devmin = 0; 2712abd1dd1Sbsh if (pci_businfo.maxdevs <= 0) 2722abd1dd1Sbsh devmax = 0; 2732abd1dd1Sbsh else 2740c91ac68Sthorpej devmax = pci_businfo.maxdevs - 1; 2750c91ac68Sthorpej } else 2760c91ac68Sthorpej devmin = devmax = devarg; 2770c91ac68Sthorpej 2780c91ac68Sthorpej for (bus = busmin; bus <= busmax; bus++) { 2790c91ac68Sthorpej for (dev = devmin; dev <= devmax; dev++) { 2800c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, 0, 2810c91ac68Sthorpej PCI_BHLC_REG, &bhlcr) != 0) 2820c91ac68Sthorpej continue; 2830c91ac68Sthorpej if (funcarg == -1) { 2840c91ac68Sthorpej funcmin = 0; 2850c91ac68Sthorpej if (PCI_HDRTYPE_MULTIFN(bhlcr)) 2860c91ac68Sthorpej funcmax = 7; 2870c91ac68Sthorpej else 2880c91ac68Sthorpej funcmax = 0; 2890c91ac68Sthorpej } else 2900c91ac68Sthorpej funcmin = funcmax = funcarg; 2910c91ac68Sthorpej for (func = funcmin; func <= funcmax; func++) { 2920c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, 2930c91ac68Sthorpej func, PCI_ID_REG, &id) != 0) 2940c91ac68Sthorpej continue; 2950c91ac68Sthorpej 2960c91ac68Sthorpej /* Invalid vendor ID value? */ 2970c91ac68Sthorpej if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) 2980c91ac68Sthorpej continue; 2990c91ac68Sthorpej /* 3000c91ac68Sthorpej * XXX Not invalid, but we've done this 3010c91ac68Sthorpej * ~forever. 3020c91ac68Sthorpej */ 3030c91ac68Sthorpej if (PCI_VENDOR(id) == 0) 3040c91ac68Sthorpej continue; 3050c91ac68Sthorpej 3060c91ac68Sthorpej (*cb)(bus, dev, func); 3070c91ac68Sthorpej } 3080c91ac68Sthorpej } 3090c91ac68Sthorpej } 3100c91ac68Sthorpej } 3110c91ac68Sthorpej 3120c91ac68Sthorpej void 3130c91ac68Sthorpej scan_pci_list(u_int bus, u_int dev, u_int func) 3140c91ac68Sthorpej { 3150c91ac68Sthorpej pcireg_t id, class; 3160c91ac68Sthorpej char devinfo[256]; 3170c91ac68Sthorpej 3180c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, func, PCI_ID_REG, &id) != 0) 3190c91ac68Sthorpej return; 3200c91ac68Sthorpej if (pcibus_conf_read(pcifd, bus, dev, func, PCI_CLASS_REG, &class) != 0) 3210c91ac68Sthorpej return; 3220c91ac68Sthorpej 323*fe81bd2fShubertf printf("%03u:%02u:%01u: ", bus, dev, func); 324*fe81bd2fShubertf if (print_numbers) { 325*fe81bd2fShubertf printf("0x%x (0x%x)\n", id, class); 326*fe81bd2fShubertf } else { 3279ef6a94eSkleink pci_devinfo(id, class, 1, devinfo, sizeof(devinfo)); 328*fe81bd2fShubertf printf("%s\n", devinfo); 329*fe81bd2fShubertf } 3300c91ac68Sthorpej } 3310c91ac68Sthorpej 3320c91ac68Sthorpej void 3330c91ac68Sthorpej scan_pci_dump(u_int bus, u_int dev, u_int func) 3340c91ac68Sthorpej { 3350c91ac68Sthorpej 3360c91ac68Sthorpej pci_conf_print(pcifd, bus, dev, func); 3370c91ac68Sthorpej } 338