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