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