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 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 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 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 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 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 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 241 pcmcia_printintr(struct intrspec *ip, int ilev) 242 { 243 obio_printintr(ip, ilev); 244 } 245 246 static void 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 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 * 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 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 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