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