xref: /illumos-gate/usr/src/cmd/prtconf/prt_xxx.c (revision e86372a01d2d16a5dd4a64e144ed978ba17fe7dd)
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
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
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
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
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
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
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
240 pcmcia_printintr(struct intrspec *ip, int ilev)
241 {
242 	obio_printintr(ip, ilev);
243 }
244 
245 static void
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
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 *
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
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)) == (uintptr_t)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
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