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