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