1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
26 */
27
28 #include <stdio.h>
29 #include <stddef.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <libdevinfo.h>
33 #include <sys/pctypes.h>
34 #include <sys/pcmcia.h>
35 #include <sys/utsname.h>
36 #include <sys/avintr.h>
37
38 #include "prtconf.h"
39
40 struct priv_data {
41 char *drv_name; /* parent name */
42 void (*pd_print)(uintptr_t, int); /* print function */
43 };
44
45 extern void indent_to_level();
46 static void obio_printregs(struct regspec *, int);
47 static void obio_printranges(struct rangespec *, int);
48 static void obio_printintr(struct intrspec *, int);
49 static void obio_print(uintptr_t, int);
50 static void pcmcia_printregs(struct pcm_regs *, int);
51 static void pcmcia_printintr(struct intrspec *, int);
52 static void pcmcia_print(uintptr_t, int);
53 static void sbus_print(uintptr_t, int);
54 static struct priv_data *match_priv_data(di_node_t);
55
56 /*
57 * This is a hardcoded list of drivers we print parent private
58 * data as of Solaris 7.
59 */
60 static struct di_priv_format ppd_format[] = {
61 {
62 /*
63 * obio format: applies the following list
64 * of nexus drivers. Note that obio driver
65 * went away with sun4m.
66 */
67 #ifdef __sparc
68 "central dma ebus fhc isa pci rootnex",
69 #else
70 "central dma ebus fhc isa pci pci_pci rootnex",
71 #endif /* __sparc */
72 sizeof (struct ddi_parent_private_data),
73
74 sizeof (struct regspec), /* first pointer */
75 offsetof(struct ddi_parent_private_data, par_reg),
76 offsetof(struct ddi_parent_private_data, par_nreg),
77
78 sizeof (struct intrspec), /* second pointer */
79 offsetof(struct ddi_parent_private_data, par_intr),
80 offsetof(struct ddi_parent_private_data, par_nintr),
81
82 sizeof (struct rangespec), /* third pointer */
83 offsetof(struct ddi_parent_private_data, par_rng),
84 offsetof(struct ddi_parent_private_data, par_nrng),
85
86 0, 0, 0, /* no more pointers */
87 0, 0, 0
88 },
89
90 { /* pcmcia format */
91 "pcic",
92 sizeof (struct pcmcia_parent_private),
93
94 sizeof (struct pcm_regs), /* first pointer */
95 offsetof(struct pcmcia_parent_private, ppd_reg),
96 offsetof(struct pcmcia_parent_private, ppd_nreg),
97
98 sizeof (struct intrspec), /* second pointer */
99 offsetof(struct pcmcia_parent_private, ppd_intrspec),
100 offsetof(struct pcmcia_parent_private, ppd_intr),
101
102 0, 0, 0, /* no more pointers */
103 0, 0, 0,
104 0, 0, 0
105 },
106
107 { /* sbus format--it's different on sun4u!! */
108 "sbus",
109 sizeof (struct ddi_parent_private_data),
110
111 sizeof (struct regspec), /* first pointer */
112 offsetof(struct ddi_parent_private_data, par_reg),
113 offsetof(struct ddi_parent_private_data, par_nreg),
114
115 sizeof (struct intrspec), /* second pointer */
116 offsetof(struct ddi_parent_private_data, par_intr),
117 offsetof(struct ddi_parent_private_data, par_nintr),
118
119 sizeof (struct rangespec), /* third pointer */
120 offsetof(struct ddi_parent_private_data, par_rng),
121 offsetof(struct ddi_parent_private_data, par_nrng),
122
123 0, 0, 0, /* no more pointers */
124 0, 0, 0
125 }
126 };
127
128 static struct priv_data prt_priv_data[] = {
129 { ppd_format[0].drv_name, obio_print},
130 { ppd_format[1].drv_name, pcmcia_print},
131 { ppd_format[2].drv_name, sbus_print}
132 };
133
134 static int nprt_priv_data = sizeof (prt_priv_data)/sizeof (struct priv_data);
135
136 void
init_priv_data(struct di_priv_data * fetch)137 init_priv_data(struct di_priv_data *fetch)
138 {
139 /* no driver private data */
140 fetch->version = DI_PRIVDATA_VERSION_0;
141 fetch->n_driver = 0;
142 fetch->driver = NULL;
143
144 fetch->n_parent = nprt_priv_data;
145 fetch->parent = ppd_format;
146 }
147
148 static void
obio_printregs(struct regspec * rp,int ilev)149 obio_printregs(struct regspec *rp, int ilev)
150 {
151 indent_to_level(ilev);
152 (void) printf(" Bus Type=0x%x, Address=0x%x, Size=0x%x\n",
153 rp->regspec_bustype, rp->regspec_addr, rp->regspec_size);
154 }
155
156 static void
obio_printranges(struct rangespec * rp,int ilev)157 obio_printranges(struct rangespec *rp, int ilev)
158 {
159 indent_to_level(ilev);
160 (void) printf(" Ch: %.2x,%.8x Pa: %.2x,%.8x, Sz: %x\n",
161 rp->rng_cbustype, rp->rng_coffset,
162 rp->rng_bustype, rp->rng_offset,
163 rp->rng_size);
164 }
165
166 static void
obio_printintr(struct intrspec * ip,int ilev)167 obio_printintr(struct intrspec *ip, int ilev)
168 {
169 indent_to_level(ilev);
170 (void) printf(" Interrupt Priority=0x%x (ipl %d)",
171 ip->intrspec_pri, INT_IPL(ip->intrspec_pri));
172 if (ip->intrspec_vec)
173 (void) printf(", vector=0x%x (%d)",
174 ip->intrspec_vec, ip->intrspec_vec);
175 (void) printf("\n");
176 }
177
178 static void
obio_print(uintptr_t data,int ilev)179 obio_print(uintptr_t data, int ilev)
180 {
181 int i, nreg, nrng, nintr;
182 struct ddi_parent_private_data *dp;
183 struct regspec *reg;
184 struct intrspec *intr;
185 struct rangespec *rng;
186
187 dp = (struct ddi_parent_private_data *)data;
188 #ifdef DEBUG
189 dprintf("obio parent private data: nreg = 0x%x offset = 0x%x"
190 " nintr = 0x%x offset = 0x%x nrng = 0x%x offset = %x\n",
191 dp->par_nreg, *((di_off_t *)(&dp->par_reg)),
192 dp->par_nintr, *((di_off_t *)(&dp->par_intr)),
193 dp->par_nrng, *((di_off_t *)(&dp->par_rng)));
194 #endif /* DEBUG */
195 nreg = dp->par_nreg;
196 nintr = dp->par_nintr;
197 nrng = dp->par_nrng;
198
199 /*
200 * All pointers are translated to di_off_t by the devinfo driver.
201 * This is a private agreement between libdevinfo and prtconf.
202 */
203 if (nreg != 0) {
204 indent_to_level(ilev);
205 (void) printf("Register Specifications:\n");
206
207 reg = (struct regspec *)(data + *(di_off_t *)(&dp->par_reg));
208 for (i = 0; i < nreg; ++i)
209 obio_printregs(reg + i, ilev);
210 }
211
212 if (nrng != 0) {
213 indent_to_level(ilev);
214 (void) printf("Range Specifications:\n");
215
216 rng = (struct rangespec *)(data + *(di_off_t *)(&dp->par_rng));
217 for (i = 0; i < nrng; ++i)
218 obio_printranges(rng + i, ilev);
219 }
220
221 if (nintr != 0) {
222 indent_to_level(ilev);
223 (void) printf("Interrupt Specifications:\n");
224
225 intr = (struct intrspec *)(data + *(di_off_t *)(&dp->par_intr));
226 for (i = 0; i < nintr; ++i)
227 obio_printintr(intr + i, ilev);
228 }
229 }
230
231 static void
pcmcia_printregs(struct pcm_regs * rp,int ilev)232 pcmcia_printregs(struct pcm_regs *rp, int ilev)
233 {
234 indent_to_level(ilev);
235 (void) printf(" Phys hi=0x%x, Phys lo=0x%x, Phys len=%x\n",
236 rp->phys_hi, rp->phys_lo, rp->phys_len);
237 }
238
239 static void
pcmcia_printintr(struct intrspec * ip,int ilev)240 pcmcia_printintr(struct intrspec *ip, int ilev)
241 {
242 obio_printintr(ip, ilev);
243 }
244
245 static void
pcmcia_print(uintptr_t data,int ilev)246 pcmcia_print(uintptr_t data, int ilev)
247 {
248 int i, nreg, nintr;
249 struct pcmcia_parent_private *dp;
250 struct pcm_regs *reg;
251 struct intrspec *intr;
252
253 dp = (struct pcmcia_parent_private *)data;
254 #ifdef DEBUG
255 dprintf("pcmcia parent private data: nreg = 0x%x offset = 0x%x"
256 " intr = 0x%x offset = %x\n",
257 dp->ppd_nreg, *(di_off_t *)(&dp->ppd_reg),
258 dp->ppd_intr, *(di_off_t *)(&dp->ppd_intrspec));
259 #endif /* DEBUG */
260 nreg = dp->ppd_nreg;
261 nintr = dp->ppd_intr;
262
263 /*
264 * All pointers are translated to di_off_t by the devinfo driver.
265 * This is a private agreement between libdevinfo and prtconf.
266 */
267 if (nreg != 0) {
268 indent_to_level(ilev);
269 (void) printf("Register Specifications:\n");
270
271 reg = (struct pcm_regs *)(data + *(di_off_t *)(&dp->ppd_reg));
272 for (i = 0; i < nreg; ++i)
273 pcmcia_printregs(reg + i, ilev);
274 }
275
276 if (nintr != 0) {
277 indent_to_level(ilev);
278 (void) printf("Interrupt Specifications:\n");
279
280 intr = (struct intrspec *)
281 (data + *(di_off_t *)(&dp->ppd_intrspec));
282 for (i = 0; i < nintr; ++i)
283 pcmcia_printintr(intr + i, ilev);
284 }
285 }
286
287 static void
sbus_print(uintptr_t data,int ilev)288 sbus_print(uintptr_t data, int ilev)
289 {
290 int i, nreg, nrng, nintr;
291 struct ddi_parent_private_data *dp;
292 struct regspec *reg;
293 struct intrspec *intr;
294 struct rangespec *rng;
295
296 dp = (struct ddi_parent_private_data *)data;
297 #ifdef DEBUG
298 dprintf("sbus parent private data: nreg = 0x%x offset = 0x%x"
299 " nintr = 0x%x offset = 0x%x nrng = 0x%x offset = %x\n",
300 dp->par_nreg, *((di_off_t *)(&dp->par_reg)),
301 dp->par_nintr, *((di_off_t *)(&dp->par_intr)),
302 dp->par_nrng, *((di_off_t *)(&dp->par_rng)));
303 #endif /* DEBUG */
304 nreg = dp->par_nreg;
305 nintr = dp->par_nintr;
306 nrng = dp->par_nrng;
307
308 /*
309 * All pointers are translated to di_off_t by the devinfo driver.
310 * This is a private agreement between libdevinfo and prtconf.
311 */
312 if (nreg != 0) {
313 indent_to_level(ilev);
314 (void) printf("Register Specifications:\n");
315
316 reg = (struct regspec *)(data + *(di_off_t *)(&dp->par_reg));
317 for (i = 0; i < nreg; ++i)
318 obio_printregs(reg + i, ilev);
319 }
320
321
322 if (nrng != 0) {
323 indent_to_level(ilev);
324 (void) printf("Range Specifications:\n");
325
326 rng = (struct rangespec *)(data + *(di_off_t *)(&dp->par_rng));
327 for (i = 0; i < nrng; ++i)
328 obio_printranges(rng + i, ilev);
329 }
330
331 /*
332 * To print interrupt property for children of sbus on sun4u requires
333 * definitions in sysiosbus.h.
334 *
335 * We can't #include <sys/sysiosbus.h> to have the build work on
336 * non sun4u machines. It's not right either to
337 * #include "../../uts/sun4u/sys/sysiosbus.h"
338 * As a result, we will not print the information.
339 */
340 if ((nintr != 0) && (strcmp(opts.o_uts.machine, "sun4u") != 0)) {
341 indent_to_level(ilev);
342 (void) printf("Interrupt Specifications:\n");
343
344 for (i = 0; i < nintr; ++i) {
345 intr = (struct intrspec *)
346 (data + *(di_off_t *)(&dp->par_intr));
347 obio_printintr(intr + i, ilev);
348 }
349 }
350 }
351
352 static struct priv_data *
match_priv_data(di_node_t node)353 match_priv_data(di_node_t node)
354 {
355 int i;
356 size_t len;
357 char *drv_name, *tmp;
358 di_node_t parent;
359 struct priv_data *pdp;
360
361 if ((parent = di_parent_node(node)) == DI_NODE_NIL)
362 return (NULL);
363
364 if ((drv_name = di_driver_name(parent)) == NULL)
365 return (NULL);
366
367 pdp = prt_priv_data;
368 len = strlen(drv_name);
369 for (i = 0; i < nprt_priv_data; ++i, ++pdp) {
370 tmp = pdp->drv_name;
371 while (tmp && (*tmp != '\0')) {
372 if (strncmp(tmp, drv_name, len) == 0) {
373 #ifdef DEBUG
374 dprintf("matched parent private data"
375 " at Node <%s> parent driver <%s>\n",
376 di_node_name(node), drv_name);
377 #endif /* DEBUG */
378 return (pdp);
379 }
380 /*
381 * skip a white space
382 */
383 if (tmp = strchr(tmp, ' '))
384 tmp++;
385 }
386 }
387
388 return (NULL);
389 }
390
391 void
dump_priv_data(int ilev,di_node_t node)392 dump_priv_data(int ilev, di_node_t node)
393 {
394 uintptr_t priv;
395 struct priv_data *pdp;
396
397 if ((priv = (uintptr_t)di_parent_private_data(node)) == NULL)
398 return;
399
400 if ((pdp = match_priv_data(node)) == NULL) {
401 #ifdef DEBUG
402 dprintf("Error: parent private data format unknown\n");
403 #endif /* DEBUG */
404 return;
405 }
406
407 pdp->pd_print(priv, ilev);
408
409 /* ignore driver private data for now */
410 }
411
412 #define LOOKUP_PROP(proptype, ph, nodetype, dev, node, name, data) \
413 ((nodetype == DI_PROM_NODEID) ? \
414 di_prom_prop_lookup_##proptype(ph, node, name, data) : \
415 di_prop_lookup_##proptype(dev, node, name, data))
416 #define ISPCI(s) \
417 (((s) != NULL) && ((strcmp((s), "pci") == 0) || \
418 (strcmp((s), "pciex") == 0)))
419 /*
420 * Print vendor ID and device ID for PCI devices
421 */
422 int
print_pciid(di_node_t node,di_prom_handle_t ph,pcidb_hdl_t * pci)423 print_pciid(di_node_t node, di_prom_handle_t ph, pcidb_hdl_t *pci)
424 {
425 pcidb_vendor_t *vend = NULL;
426 pcidb_device_t *dev = NULL;
427 di_node_t pnode = di_parent_node(node);
428 char *s = NULL;
429 int *i, type = di_nodeid(node);
430
431 if (LOOKUP_PROP(strings, ph, type, DDI_DEV_T_ANY, pnode,
432 "device_type", &s) <= 0)
433 return (0);
434
435 if (!ISPCI(s))
436 return (0); /* not a pci device */
437
438 (void) printf(" (%s", s);
439 if (LOOKUP_PROP(ints, ph, type, DDI_DEV_T_ANY, node,
440 "vendor-id", &i) > 0)
441 (void) printf("%x", i[0]);
442
443 if (pci != NULL)
444 vend = pcidb_lookup_vendor(pci, i[0]);
445
446 if (LOOKUP_PROP(ints, ph, type, DDI_DEV_T_ANY, node,
447 "device-id", &i) > 0)
448 (void) printf(",%x", i[0]);
449
450 if (vend != NULL)
451 dev = pcidb_lookup_device_by_vendor(vend, i[0]);
452
453 (void) printf(") [");
454
455 if (vend != NULL)
456 (void) printf("%s ", pcidb_vendor_name(vend));
457 else
458 (void) printf("unknown vendor, ");
459
460 if (dev != NULL)
461 (void) printf("%s", pcidb_device_name(dev));
462 else
463 (void) printf("unknown device");
464
465 (void) printf("]");
466 return (1);
467 }
468