1 /* $NetBSD: pcictl.c,v 1.22 2016/09/24 23:12:54 mrg 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 __dead static void usage(void); 67 68 static int pcifd; 69 70 static struct pciio_businfo pci_businfo; 71 72 static const char *dvname; 73 static char dvname_store[MAXPATHLEN]; 74 static const char *cmdname; 75 static int print_numbers = 0; 76 static int print_names = 0; 77 78 static void cmd_list(int, char *[]); 79 static void cmd_dump(int, char *[]); 80 static void cmd_read(int, char *[]); 81 static void cmd_write(int, char *[]); 82 83 static const struct command commands[] = { 84 { "list", 85 "[-Nn] [-b bus] [-d device] [-f function]", 86 cmd_list, 87 O_RDONLY }, 88 89 { "dump", 90 "[-b bus] -d device [-f function]", 91 cmd_dump, 92 O_RDONLY }, 93 94 { "read", 95 "[-b bus] -d device [-f function] reg", 96 cmd_read, 97 O_RDONLY }, 98 99 { "write", 100 "[-b bus] -d device [-f function] reg value", 101 cmd_write, 102 O_WRONLY }, 103 104 { 0, 0, 0, 0 }, 105 }; 106 107 static int parse_bdf(const char *); 108 static u_int parse_reg(const char *); 109 110 static void scan_pci(int, int, int, void (*)(u_int, u_int, u_int)); 111 112 static void scan_pci_list(u_int, u_int, u_int); 113 static void scan_pci_dump(u_int, u_int, u_int); 114 115 int 116 main(int argc, char *argv[]) 117 { 118 int i; 119 120 /* Must have at least: device command */ 121 if (argc < 3) 122 usage(); 123 124 /* Skip program name, get and skip device name, get command. */ 125 dvname = argv[1]; 126 cmdname = argv[2]; 127 argv += 2; 128 argc -= 2; 129 130 /* Look up and call the command. */ 131 for (i = 0; commands[i].cmd_name != NULL; i++) 132 if (strcmp(cmdname, commands[i].cmd_name) == 0) 133 break; 134 if (commands[i].cmd_name == NULL) 135 errx(EXIT_FAILURE, "unknown command: %s", cmdname); 136 137 /* Open the device. */ 138 if ((strchr(dvname, '/') == NULL) && 139 (snprintf(dvname_store, sizeof(dvname_store), _PATH_DEV "%s", 140 dvname) < (int)sizeof(dvname_store))) 141 dvname = dvname_store; 142 pcifd = open(dvname, commands[i].open_flags); 143 if (pcifd < 0) 144 err(EXIT_FAILURE, "%s", dvname); 145 146 /* Make sure the device is a PCI bus. */ 147 if (ioctl(pcifd, PCI_IOC_BUSINFO, &pci_businfo) != 0) 148 errx(EXIT_FAILURE, "%s: not a PCI bus device", dvname); 149 150 (*commands[i].cmd_func)(argc, argv); 151 exit(EXIT_SUCCESS); 152 } 153 154 static void 155 usage(void) 156 { 157 int i; 158 159 fprintf(stderr, "usage: %s device command [arg [...]]\n", 160 getprogname()); 161 162 fprintf(stderr, " Available commands:\n"); 163 for (i = 0; commands[i].cmd_name != NULL; i++) 164 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 165 commands[i].arg_names); 166 167 exit(EXIT_FAILURE); 168 } 169 170 static void 171 cmd_list(int argc, char *argv[]) 172 { 173 int bus, dev, func; 174 int ch; 175 176 bus = -1; 177 dev = func = -1; 178 179 while ((ch = getopt(argc, argv, "b:d:f:Nn")) != -1) { 180 switch (ch) { 181 case 'b': 182 bus = parse_bdf(optarg); 183 break; 184 case 'd': 185 dev = parse_bdf(optarg); 186 break; 187 case 'f': 188 func = parse_bdf(optarg); 189 break; 190 case 'n': 191 print_numbers = 1; 192 break; 193 case 'N': 194 print_names = 1; 195 break; 196 default: 197 usage(); 198 } 199 } 200 argv += optind; 201 argc -= optind; 202 203 if (argc != 0) 204 usage(); 205 206 scan_pci(bus, dev, func, scan_pci_list); 207 } 208 209 static void 210 cmd_dump(int argc, char *argv[]) 211 { 212 int bus, dev, func; 213 int ch; 214 215 bus = pci_businfo.busno; 216 func = 0; 217 dev = -1; 218 219 while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 220 switch (ch) { 221 case 'b': 222 bus = parse_bdf(optarg); 223 break; 224 case 'd': 225 dev = parse_bdf(optarg); 226 break; 227 case 'f': 228 func = parse_bdf(optarg); 229 break; 230 default: 231 usage(); 232 } 233 } 234 argv += optind; 235 argc -= optind; 236 237 if (argc != 0) 238 usage(); 239 240 if (bus == -1) 241 errx(EXIT_FAILURE, "dump: wildcard bus number not permitted"); 242 if (dev == -1) 243 errx(EXIT_FAILURE, "dump: must specify a device number"); 244 if (func == -1) 245 errx(EXIT_FAILURE, "dump: wildcard function number not permitted"); 246 247 scan_pci(bus, dev, func, scan_pci_dump); 248 } 249 250 static void 251 cmd_read(int argc, char *argv[]) 252 { 253 int bus, dev, func; 254 u_int reg; 255 pcireg_t value; 256 int ch; 257 258 bus = pci_businfo.busno; 259 func = 0; 260 dev = -1; 261 262 while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 263 switch (ch) { 264 case 'b': 265 bus = parse_bdf(optarg); 266 break; 267 case 'd': 268 dev = parse_bdf(optarg); 269 break; 270 case 'f': 271 func = parse_bdf(optarg); 272 break; 273 default: 274 usage(); 275 } 276 } 277 argv += optind; 278 argc -= optind; 279 280 if (argc != 1) 281 usage(); 282 reg = parse_reg(argv[0]); 283 if (pcibus_conf_read(pcifd, bus, dev, func, reg, &value) == -1) 284 err(EXIT_FAILURE, "pcibus_conf_read" 285 "(bus %d dev %d func %d reg %u)", bus, dev, func, reg); 286 if (printf("%08x\n", value) < 0) 287 err(EXIT_FAILURE, "printf"); 288 } 289 290 static void 291 cmd_write(int argc, char *argv[]) 292 { 293 int bus, dev, func; 294 u_int reg; 295 pcireg_t value; 296 int ch; 297 298 bus = pci_businfo.busno; 299 func = 0; 300 dev = -1; 301 302 while ((ch = getopt(argc, argv, "b:d:f:")) != -1) { 303 switch (ch) { 304 case 'b': 305 bus = parse_bdf(optarg); 306 break; 307 case 'd': 308 dev = parse_bdf(optarg); 309 break; 310 case 'f': 311 func = parse_bdf(optarg); 312 break; 313 default: 314 usage(); 315 } 316 } 317 argv += optind; 318 argc -= optind; 319 320 if (argc != 2) 321 usage(); 322 reg = parse_reg(argv[0]); 323 __CTASSERT(sizeof(value) == sizeof(u_int)); 324 value = parse_reg(argv[1]); 325 if (pcibus_conf_write(pcifd, bus, dev, func, reg, value) == -1) 326 err(EXIT_FAILURE, "pcibus_conf_write" 327 "(bus %d dev %d func %d reg %u value 0x%x)", 328 bus, dev, func, reg, value); 329 } 330 331 static int 332 parse_bdf(const char *str) 333 { 334 long value; 335 char *end; 336 337 if (strcmp(str, "all") == 0 || 338 strcmp(str, "any") == 0) 339 return (-1); 340 341 errno = 0; 342 value = strtol(str, &end, 0); 343 if ((str[0] == '\0') || (*end != '\0')) 344 errx(EXIT_FAILURE, "\"%s\" is not a number", str); 345 if ((errno == ERANGE) && ((value == LONG_MIN) || (value == LONG_MAX))) 346 errx(EXIT_FAILURE, "out of range: %s", str); 347 if ((value < INT_MIN) || (INT_MAX < value)) 348 errx(EXIT_FAILURE, "out of range: %lu", value); 349 350 return value; 351 } 352 353 static u_int 354 parse_reg(const char *str) 355 { 356 unsigned long value; 357 char *end; 358 359 errno = 0; 360 value = strtoul(str, &end, 0); 361 if (*end != '\0') 362 errx(EXIT_FAILURE, "\"%s\" is not a number", str); 363 if ((errno == ERANGE) && (value == ULONG_MAX)) 364 errx(EXIT_FAILURE, "out of range: %s", str); 365 if (UINT_MAX < value) 366 errx(EXIT_FAILURE, "out of range: %lu", value); 367 368 return value; 369 } 370 371 static void 372 scan_pci(int busarg, int devarg, int funcarg, void (*cb)(u_int, u_int, u_int)) 373 { 374 u_int busmin, busmax; 375 u_int devmin, devmax; 376 u_int funcmin, funcmax; 377 u_int bus, dev, func; 378 pcireg_t id, bhlcr; 379 380 if (busarg == -1) { 381 busmin = 0; 382 busmax = 255; 383 } else 384 busmin = busmax = busarg; 385 386 if (devarg == -1) { 387 devmin = 0; 388 if (pci_businfo.maxdevs <= 0) 389 devmax = 0; 390 else 391 devmax = pci_businfo.maxdevs - 1; 392 } else 393 devmin = devmax = devarg; 394 395 for (bus = busmin; bus <= busmax; bus++) { 396 for (dev = devmin; dev <= devmax; dev++) { 397 if (pcibus_conf_read(pcifd, bus, dev, 0, 398 PCI_BHLC_REG, &bhlcr) != 0) 399 continue; 400 if (funcarg == -1) { 401 funcmin = 0; 402 if (PCI_HDRTYPE_MULTIFN(bhlcr)) 403 funcmax = 7; 404 else 405 funcmax = 0; 406 } else 407 funcmin = funcmax = funcarg; 408 for (func = funcmin; func <= funcmax; func++) { 409 if (pcibus_conf_read(pcifd, bus, dev, 410 func, PCI_ID_REG, &id) != 0) 411 continue; 412 413 /* Invalid vendor ID value? */ 414 if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) 415 continue; 416 /* 417 * XXX Not invalid, but we've done this 418 * ~forever. 419 */ 420 if (PCI_VENDOR(id) == 0) 421 continue; 422 423 (*cb)(bus, dev, func); 424 } 425 } 426 } 427 } 428 429 static void 430 scan_pci_list(u_int bus, u_int dev, u_int func) 431 { 432 pcireg_t id, class; 433 char devinfo[256]; 434 435 if (pcibus_conf_read(pcifd, bus, dev, func, PCI_ID_REG, &id) != 0) 436 return; 437 if (pcibus_conf_read(pcifd, bus, dev, func, PCI_CLASS_REG, &class) != 0) 438 return; 439 440 printf("%03u:%02u:%01u: ", bus, dev, func); 441 if (print_numbers) { 442 printf("0x%08x (0x%08x)", id, class); 443 } else { 444 pci_devinfo(id, class, 1, devinfo, sizeof(devinfo)); 445 printf("%s", devinfo); 446 } 447 if (print_names) { 448 char drvname[16]; 449 if (pci_drvnameonbus(pcifd, bus, dev, func, drvname, 450 sizeof drvname) == 0) 451 printf(" [%s]", drvname); 452 } 453 printf("\n"); 454 } 455 456 static void 457 scan_pci_dump(u_int bus, u_int dev, u_int func) 458 { 459 460 pci_conf_print(pcifd, bus, dev, func); 461 } 462