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