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
main(int argc,char * argv[])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
usage(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
cmd_list(int argc,char * argv[])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
cmd_dump(int argc,char * argv[])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
cmd_read(int argc,char * argv[])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
cmd_write(int argc,char * argv[])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
parse_bdf(const char * str)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
parse_reg(const char * str)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
scan_pci(int busarg,int devarg,int funcarg,void (* cb)(u_int,u_int,u_int))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
scan_pci_list(u_int bus,u_int dev,u_int func)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
scan_pci_dump(u_int bus,u_int dev,u_int func)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