1 /* $NetBSD: pcictl.c,v 1.6 2002/07/20 08:40:18 grant Exp $ */ 2 3 /* 4 * Copyright 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * pcictl(8) -- a program to manipulate the PCI bus 40 */ 41 42 #include <sys/param.h> 43 #include <sys/ioctl.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <paths.h> 48 #include <pci.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <util.h> 54 55 #include <dev/pci/pcireg.h> 56 #include <dev/pci/pcidevs.h> 57 #include <dev/pci/pciio.h> 58 59 struct command { 60 const char *cmd_name; 61 const char *arg_names; 62 void (*cmd_func)(int, char *[]); 63 int open_flags; 64 }; 65 66 int main(int, char *[]); 67 void usage(void); 68 69 int pcifd; 70 71 struct pciio_businfo pci_businfo; 72 73 const char *dvname; 74 char dvname_store[MAXPATHLEN]; 75 const char *cmdname; 76 const char *argnames; 77 78 void cmd_list(int, char *[]); 79 void cmd_dump(int, char *[]); 80 81 const struct command commands[] = { 82 { "list", 83 "[-b bus] [-d device] [-f function]", 84 cmd_list, 85 O_RDONLY }, 86 87 { "dump", 88 "[-b bus] -d device [-f function]", 89 cmd_dump, 90 O_RDONLY }, 91 92 { 0 }, 93 }; 94 95 int parse_bdf(const char *); 96 97 void scan_pci(int, int, int, void (*)(u_int, u_int, u_int)); 98 99 void scan_pci_list(u_int, u_int, u_int); 100 void scan_pci_dump(u_int, u_int, u_int); 101 102 int 103 main(int argc, char *argv[]) 104 { 105 int i; 106 107 /* Must have at least: device command */ 108 if (argc < 3) 109 usage(); 110 111 /* Skip program name, get and skip device name, get command. */ 112 dvname = argv[1]; 113 cmdname = argv[2]; 114 argv += 2; 115 argc -= 2; 116 117 /* Look up and call the command. */ 118 for (i = 0; commands[i].cmd_name != NULL; i++) 119 if (strcmp(cmdname, commands[i].cmd_name) == 0) 120 break; 121 if (commands[i].cmd_name == NULL) 122 errx(1, "unknown command: %s", cmdname); 123 124 argnames = commands[i].arg_names; 125 126 /* Open the device. */ 127 if ((strchr(dvname, '/') == NULL) && 128 (snprintf(dvname_store, sizeof(dvname_store), _PATH_DEV "%s", 129 dvname) < sizeof(dvname_store))) 130 dvname = dvname_store; 131 pcifd = open(dvname, commands[i].open_flags); 132 if (pcifd < 0) 133 err(1, "%s", dvname); 134 135 /* Make sure the device is a PCI bus. */ 136 if (ioctl(pcifd, PCI_IOC_BUSINFO, &pci_businfo) != 0) 137 errx(1, "%s: not a PCI bus device", dvname); 138 139 (*commands[i].cmd_func)(argc, argv); 140 exit(0); 141 } 142 143 void 144 usage() 145 { 146 int i; 147 148 fprintf(stderr, "Usage: %s device command [arg [...]]\n", 149 getprogname()); 150 151 fprintf(stderr, " Available commands:\n"); 152 for (i = 0; commands[i].cmd_name != NULL; i++) 153 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 154 commands[i].arg_names); 155 156 exit(1); 157 } 158 159 void 160 cmd_list(int argc, char *argv[]) 161 { 162 int bus, dev, func; 163 int ch; 164 165 bus = pci_businfo.busno; 166 dev = func = -1; 167 168 while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 169 switch (ch) { 170 case 'b': 171 bus = parse_bdf(optarg); 172 break; 173 case 'd': 174 dev = parse_bdf(optarg); 175 break; 176 case 'f': 177 func = parse_bdf(optarg); 178 break; 179 default: 180 usage(); 181 } 182 } 183 argv += optind; 184 argc -= optind; 185 186 if (argc != 0) 187 usage(); 188 189 scan_pci(bus, dev, func, scan_pci_list); 190 } 191 192 void 193 cmd_dump(int argc, char *argv[]) 194 { 195 int bus, dev, func; 196 int ch; 197 198 bus = pci_businfo.busno; 199 func = 0; 200 dev = -1; 201 202 while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 203 switch (ch) { 204 case 'b': 205 bus = parse_bdf(optarg); 206 break; 207 case 'd': 208 dev = parse_bdf(optarg); 209 break; 210 case 'f': 211 func = parse_bdf(optarg); 212 break; 213 default: 214 usage(); 215 } 216 } 217 argv += optind; 218 argc -= optind; 219 220 if (argc != 0) 221 usage(); 222 223 if (bus == -1) 224 errx(1, "dump: wildcard bus number not permitted"); 225 if (dev == -1) 226 errx(1, "dump: must specify a device number"); 227 if (func == -1) 228 errx(1, "dump: wildcard function number not permitted"); 229 230 scan_pci(bus, dev, func, scan_pci_dump); 231 } 232 233 int 234 parse_bdf(const char *str) 235 { 236 long value; 237 char *end; 238 239 if (strcmp(str, "all") == 0 || 240 strcmp(str, "any") == 0) 241 return (-1); 242 243 value = strtol(str, &end, 0); 244 if(*end != '\0') 245 errx(1, "\"%s\" is not a number", str); 246 247 return value; 248 } 249 250 void 251 scan_pci(int busarg, int devarg, int funcarg, void (*cb)(u_int, u_int, u_int)) 252 { 253 u_int busmin, busmax; 254 u_int devmin, devmax; 255 u_int funcmin, funcmax; 256 u_int bus, dev, func; 257 pcireg_t id, bhlcr; 258 259 if (busarg == -1) { 260 busmin = 0; 261 busmax = 255; 262 } else 263 busmin = busmax = busarg; 264 265 if (devarg == -1) { 266 devmin = 0; 267 devmax = pci_businfo.maxdevs - 1; 268 } else 269 devmin = devmax = devarg; 270 271 for (bus = busmin; bus <= busmax; bus++) { 272 for (dev = devmin; dev <= devmax; dev++) { 273 if (pcibus_conf_read(pcifd, bus, dev, 0, 274 PCI_BHLC_REG, &bhlcr) != 0) 275 continue; 276 if (funcarg == -1) { 277 funcmin = 0; 278 if (PCI_HDRTYPE_MULTIFN(bhlcr)) 279 funcmax = 7; 280 else 281 funcmax = 0; 282 } else 283 funcmin = funcmax = funcarg; 284 for (func = funcmin; func <= funcmax; func++) { 285 if (pcibus_conf_read(pcifd, bus, dev, 286 func, PCI_ID_REG, &id) != 0) 287 continue; 288 289 /* Invalid vendor ID value? */ 290 if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) 291 continue; 292 /* 293 * XXX Not invalid, but we've done this 294 * ~forever. 295 */ 296 if (PCI_VENDOR(id) == 0) 297 continue; 298 299 (*cb)(bus, dev, func); 300 } 301 } 302 } 303 } 304 305 void 306 scan_pci_list(u_int bus, u_int dev, u_int func) 307 { 308 pcireg_t id, class; 309 char devinfo[256]; 310 311 if (pcibus_conf_read(pcifd, bus, dev, func, PCI_ID_REG, &id) != 0) 312 return; 313 if (pcibus_conf_read(pcifd, bus, dev, func, PCI_CLASS_REG, &class) != 0) 314 return; 315 316 pci_devinfo(id, class, 1, devinfo); 317 318 printf("%03u:%02u:%01u: %s\n", bus, dev, func, devinfo); 319 } 320 321 void 322 scan_pci_dump(u_int bus, u_int dev, u_int func) 323 { 324 325 pci_conf_print(pcifd, bus, dev, func); 326 } 327