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