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 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Create a topology node for a PRI node of type 'pciexrc' 29 */ 30 #include <sys/types.h> 31 #include <strings.h> 32 #include <sys/fm/protocol.h> 33 #include <fm/topo_mod.h> 34 #include <fm/topo_hc.h> 35 #include <libdevinfo.h> 36 #include <sys/pci.h> 37 #include "pi_impl.h" 38 39 #define PCIEX_MAX_DEVICE 255 40 #define PCIEX_MAX_BDF_SIZE 23 /* '0x' + sizeof (UNIT64_MAX) + '\0' */ 41 42 #define TOPO_PGROUP_PCIEX "pciex" 43 #define _ENUM_NAME "enum_pciexrc" 44 45 static char *drv_name = NULL; 46 47 static const topo_pgroup_info_t io_pgroup = 48 { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 49 50 static const topo_pgroup_info_t pci_pgroup = 51 { TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 52 53 static int pi_enum_pciexrc_finddev(topo_mod_t *, md_t *, mde_cookie_t, 54 tnode_t *); 55 56 static char *pi_enum_pciexrc_findbdf(topo_mod_t *, di_node_t); 57 58 static int pi_enum_pciexrc_defer(topo_mod_t *, md_t *, mde_cookie_t, 59 topo_instance_t, tnode_t *, const char *, tnode_t *, void *); 60 61 62 /* 63 * Create a pciexrc topo by calling the pciexrc enumerator for this instance. 64 */ 65 int 66 pi_enum_pciexrc(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, 67 topo_instance_t inst, tnode_t *t_parent, const char *hc_name, 68 tnode_t **t_node) 69 { 70 int result; 71 72 topo_mod_dprintf(mod, "%s called for node_0x%llx type %s\n", 73 _ENUM_NAME, (uint64_t)mde_node, hc_name); 74 75 *t_node = NULL; 76 77 /* 78 * Create the root complex topo node. Use the generic enumerator to 79 * do this, and then we will add more attributes below. 80 */ 81 result = pi_enum_generic_impl(mod, mdp, mde_node, inst, t_parent, 82 t_parent, hc_name, _ENUM_NAME, t_node, 0); 83 if (result != 0 || *t_node == NULL) { 84 topo_mod_dprintf(mod, 85 "%s node_0x%llx failed to create topo node: %s\n", 86 _ENUM_NAME, (uint64_t)mde_node, 87 topo_strerror(topo_mod_errno(mod))); 88 return (result); 89 } 90 91 /* Update the topo node with more specific information */ 92 result = pi_enum_update(mod, mdp, mde_node, t_parent, *t_node, 93 hc_name); 94 if (result != 0) { 95 topo_mod_dprintf(mod, 96 "%s node_0x%llx failed to create node properites: %s\n", 97 _ENUM_NAME, (uint64_t)mde_node, 98 topo_strerror(topo_mod_errno(mod))); 99 return (result); 100 } 101 102 result = pi_enum_pciexrc_finddev(mod, mdp, mde_node, *t_node); 103 if (result == 0) { 104 /* 105 * The node exists in this domain. We will call the PCIBUS 106 * enumerator after the entire PRI graph has been walked so 107 * that all the possible FRU nodes are available for bus's 108 * that span multiple FRU boundaries. 109 */ 110 result = pi_defer_add(mod, mde_node, t_parent, *t_node, 111 pi_enum_pciexrc_defer, NULL); 112 if (result != 0) { 113 /* We cannot defer the call, so we need to do it now */ 114 result = pi_enum_pciexrc_defer(mod, mdp, mde_node, inst, 115 t_parent, hc_name, *t_node, NULL); 116 } 117 } else { 118 /* 119 * It is OK if the node does not exist for further PCIBUS 120 * enumeration. We can return success having created the 121 * root complex node itself. 122 */ 123 result = 0; 124 } 125 topo_mod_dprintf(mod, "%s added node_0x%llx type %s\n", 126 _ENUM_NAME, (uint64_t)mde_node, hc_name); 127 128 return (result); 129 } 130 131 132 /* ARGSUSED */ 133 static int 134 pi_enum_pciexrc_defer(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, 135 topo_instance_t inst, tnode_t *t_parent, const char *hc_name, 136 tnode_t *t_node, void *private) 137 { 138 int result; 139 topo_instance_t min; 140 topo_instance_t max; 141 142 topo_mod_dprintf(mod, 143 "%s node_0x%llx deferred enumeration starting\n", _ENUM_NAME, 144 (uint64_t)mde_node); 145 146 /* Make sure our dependent modules are loaded */ 147 if (topo_mod_load(mod, PCI_BUS, TOPO_VERSION) == NULL) { 148 topo_mod_dprintf(mod, "%s could not load %s module: %s\n", 149 _ENUM_NAME, PCI_BUS, topo_strerror(topo_mod_errno(mod))); 150 return (-1); 151 } 152 153 /* Create a node range for children of this bus */ 154 min = 0; 155 max = PCIEX_MAX_DEVICE; 156 result = topo_node_range_create(mod, t_node, PCI_BUS, min, max); 157 if (result != 0) { 158 topo_mod_dprintf(mod, 159 "%s node_0x%llx failed to create node range: %s\n", 160 _ENUM_NAME, topo_strerror(topo_mod_errno(mod))); 161 return (-1); 162 } 163 164 /* 165 * Invoke the pcibus enumerator for this node. 166 */ 167 result = topo_mod_enumerate(mod, t_node, PCI_BUS, PCIEX_BUS, 168 min, max, NULL); 169 if (result != 0) { 170 topo_mod_dprintf(mod, 171 "%s node_0x%llx enumeration failed: %s\n", _ENUM_NAME, 172 (uint64_t)mde_node, topo_strerror(topo_mod_errno(mod))); 173 } 174 175 topo_mod_dprintf(mod, "%s added node_0x%llx type %s\n", 176 _ENUM_NAME, (uint64_t)mde_node, hc_name); 177 178 return (result); 179 } 180 181 182 /* 183 * Update PCIEXRC/HOSTBRIDGE topo node with node-specific information 184 * 185 * The following is mostly a duplicate of code contained in: 186 * usr/src/lib/fm/topo/modules/sun4v/cpuboard/ 187 * cpuboard_hostbridge.c:cpuboard_rc_node_create 188 */ 189 int 190 pi_enum_update(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, 191 tnode_t *t_parent, tnode_t *t_node, const char *hc_name) 192 { 193 int result; 194 int err; 195 int is_hbridge = 0; 196 int is_pciexrc = 0; 197 char *path = NULL; 198 char *bdf = NULL; 199 char *_enum_name; 200 nvlist_t *modfmri; 201 nvlist_t *devfmri; 202 di_node_t dnode; 203 204 /* 205 * Determine if decorating a PCIE root complex or a hostbridge 206 * node. 207 */ 208 if (strncmp(hc_name, PCIEX_ROOT, strlen(hc_name)) == 0) { 209 is_pciexrc = 1; 210 _enum_name = "enum_pciexrc"; 211 } else if (strncmp(hc_name, HOSTBRIDGE, strlen(hc_name)) == 0) { 212 is_hbridge = 1; 213 _enum_name = "enum_hostbridge"; 214 } else { 215 topo_mod_dprintf(mod, 216 "pi_enum_update node_0x%llx unknown hc name %s\n", 217 (uint64_t)mde_node, hc_name); 218 return (-1); 219 } 220 221 if (t_parent == NULL || t_node == NULL) { 222 topo_mod_dprintf(mod, "%s node_0x%llx has no parent\n", 223 _enum_name, (uint64_t)mde_node); 224 return (-1); 225 } 226 227 /* 228 * Calculate the device path for this root complex node. 229 */ 230 path = pi_get_path(mod, mdp, mde_node); 231 if (path == NULL) { 232 if (is_hbridge == 1) { 233 /* "path" not required for hostbridge */ 234 return (0); 235 } 236 topo_mod_dprintf(mod, "%s node_0x%llx has no path\n", 237 _enum_name, (uint64_t)mde_node); 238 return (-1); 239 } 240 241 /* 242 * Set the ASRU for this node using the dev scheme 243 */ 244 devfmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, path, NULL); 245 if (devfmri == NULL) { 246 topo_mod_dprintf(mod, "%s node_0x%llx fmri creation failed\n", 247 _enum_name, (uint64_t)mde_node); 248 result = -1; 249 goto out; 250 } 251 252 result = topo_node_asru_set(t_node, devfmri, 0, &err); 253 nvlist_free(devfmri); 254 if (result != 0) { 255 topo_mod_dprintf(mod, "%s node_0x%llx failed to set ASRU\n", 256 _enum_name, (uint64_t)mde_node); 257 (void) topo_mod_seterrno(mod, err); 258 goto out; 259 } 260 261 /* 262 * Create property groups. 263 */ 264 result = topo_pgroup_create(t_node, &io_pgroup, &err); 265 if (result < 0) { 266 topo_mod_dprintf(mod, "%s node_0x%llx " 267 "topo_pgroup_create for io pgroup failed\n", 268 _enum_name, (uint64_t)mde_node); 269 (void) topo_mod_seterrno(mod, err); 270 goto out; 271 } 272 273 if (is_pciexrc == 1) { 274 result = topo_pgroup_create(t_node, &pci_pgroup, &err); 275 if (result < 0) { 276 topo_mod_dprintf(mod, "%s node_0x%llx " 277 "topo_pgroup_create for pci pgroup failed\n", 278 _enum_name, (uint64_t)mde_node); 279 (void) topo_mod_seterrno(mod, err); 280 goto out; 281 } 282 } 283 284 result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DEV, 285 TOPO_PROP_IMMUTABLE, path, &err); 286 if (result != 0) { 287 topo_mod_dprintf(mod, 288 "%s node_0x%llx failed to set DEV property\n", 289 _enum_name, (uint64_t)mde_node); 290 (void) topo_mod_seterrno(mod, err); 291 goto out; 292 } 293 294 /* device type is always "pciex" */ 295 result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE, 296 TOPO_PROP_IMMUTABLE, TOPO_PGROUP_PCIEX, &err); 297 if (result < 0) { 298 topo_mod_dprintf(mod, 299 "%s node_0x%llx failed to set DEVTYPE property\n", 300 _enum_name, (uint64_t)mde_node); 301 (void) topo_mod_seterrno(mod, err); 302 goto out; 303 } 304 305 /* 306 * Derived the driver name from the device path. 307 */ 308 dnode = di_init(path, DIIOC); 309 if (dnode == DI_NODE_NIL) { 310 topo_mod_dprintf(mod, "%s node_0x%llx failed to get node\n", 311 _enum_name, (uint64_t)mde_node); 312 result = -1; 313 goto out; 314 } 315 drv_name = di_driver_name(dnode); 316 if (drv_name == NULL) { 317 topo_mod_dprintf(mod, "%s node_0x%llx failed to get driver " 318 " name\n", _enum_name, (uint64_t)mde_node); 319 di_fini(dnode); 320 result = -1; 321 goto out; 322 } 323 324 if (is_pciexrc == 1) { 325 /* 326 * Derived the BDF property from the devinfo node. 327 */ 328 bdf = pi_enum_pciexrc_findbdf(mod, dnode); 329 if (bdf == NULL) { 330 topo_mod_dprintf(mod, "%s: node_0x%llx failed to " 331 "find BDF", _enum_name, (uint64_t)mde_node); 332 di_fini(dnode); 333 result = -1; 334 goto out; 335 } 336 } 337 di_fini(dnode); 338 topo_mod_dprintf(mod, "%s node_0x%llx driver name is %s\n", 339 _enum_name, (uint64_t)mde_node, drv_name); 340 341 result = topo_prop_set_string(t_node, TOPO_PGROUP_IO, TOPO_IO_DRIVER, 342 TOPO_PROP_IMMUTABLE, drv_name, &err); 343 if (result < 0) { 344 topo_mod_dprintf(mod, 345 "%s node_0x%llx failed to set DRIVER property\n", 346 _enum_name, (uint64_t)mde_node); 347 (void) topo_mod_seterrno(mod, err); 348 goto out; 349 } 350 351 modfmri = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION, drv_name); 352 if (modfmri == NULL) { 353 topo_mod_dprintf(mod, 354 "%s node_0x%llx failed to create module fmri\n", 355 _enum_name, (uint64_t)mde_node); 356 (void) topo_mod_seterrno(mod, err); 357 result = -1; 358 goto out; 359 } 360 result = topo_prop_set_fmri(t_node, TOPO_PGROUP_IO, TOPO_IO_MODULE, 361 TOPO_PROP_IMMUTABLE, modfmri, &err); 362 nvlist_free(modfmri); 363 if (result < 0) { 364 topo_mod_dprintf(mod, 365 "%s node_0x%llx failed to set MODULE property\n", 366 _enum_name, (uint64_t)mde_node); 367 (void) topo_mod_seterrno(mod, err); 368 goto out; 369 } 370 371 if (is_pciexrc == 1) { 372 /* This is a PCIEX root complex */ 373 result = topo_prop_set_string(t_node, TOPO_PGROUP_PCI, 374 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err); 375 if (result < 0) { 376 topo_mod_dprintf(mod, 377 "%s node_0x%llx failed to set EXCAP property\n", 378 _enum_name, (uint64_t)mde_node); 379 (void) topo_mod_seterrno(mod, err); 380 goto out; 381 } 382 383 /* Set BDF for root complex */ 384 result = topo_prop_set_string(t_node, TOPO_PGROUP_PCI, 385 TOPO_PCI_BDF, TOPO_PROP_IMMUTABLE, bdf, &err); 386 if (result < 0) { 387 topo_mod_dprintf(mod, 388 "%s node_0x%llx failed to set BDF property\n", 389 _enum_name, (uint64_t)mde_node); 390 (void) topo_mod_seterrno(mod, err); 391 goto out; 392 } 393 394 /* Create a node range for the children of this root complex */ 395 result = topo_node_range_create(mod, t_node, PCIEX_BUS, 0, 396 PCIEX_MAX_DEVICE); 397 if (result != 0) { 398 topo_mod_dprintf(mod, 399 "%s node_0x%llx failed to create %s range\n", 400 _enum_name, (uint64_t)mde_node, PCIEX_BUS); 401 result = -1; 402 } 403 } 404 405 out: 406 if (path != NULL) { 407 topo_mod_strfree(mod, path); 408 } 409 if (bdf != NULL) { 410 topo_mod_strfree(mod, bdf); 411 } 412 return (result); 413 } 414 415 416 static int 417 pi_enum_pciexrc_finddev(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, 418 tnode_t *t_node) 419 { 420 di_node_t devtree; 421 di_node_t dnode; 422 char *path; 423 424 /* Initialize the device information structure for this module */ 425 devtree = topo_mod_devinfo(mod); 426 if (devtree == DI_NODE_NIL) { 427 topo_mod_dprintf(mod, "devinfo init failed\n"); 428 return (-1); 429 } 430 431 /* 432 * Find the PRI node path property. This will be used to associate 433 * the PRI node with the device node. 434 */ 435 path = pi_get_path(mod, mdp, mde_node); 436 if (path == NULL) { 437 topo_mod_dprintf(mod, "node_0x%llx has no path\n", 438 (uint64_t)mde_node); 439 return (-1); 440 } 441 442 /* 443 * Scan the device node list and find the node associated with 444 * the given PRI node. Equality is defined when the PRI path 445 * is the same as the device node path. 446 */ 447 dnode = di_drv_first_node(drv_name, devtree); 448 while (dnode != DI_NODE_NIL) { 449 char *devfs_path; 450 451 devfs_path = di_devfs_path(dnode); 452 if (devfs_path != NULL) { 453 if (strncmp(devfs_path, path, strlen(path)) == 0) { 454 /* We have found the matching dnode */ 455 break; 456 } 457 } 458 459 /* We have not found the matching dnode yet */ 460 dnode = di_drv_next_node(dnode); 461 } 462 if (dnode != DI_NODE_NIL) { 463 topo_mod_dprintf(mod, "%s node_0x%llx found dev path %s\n", 464 _ENUM_NAME, (uint64_t)mde_node, path); 465 466 /* 467 * Associate this dnode with the topo node. The PCI 468 * enumerator requires this information. 469 */ 470 topo_node_setspecific(t_node, (void *)dnode); 471 } 472 473 topo_mod_strfree(mod, path); 474 return (0); 475 } 476 477 478 /* 479 * Find the BDF property and return as a string. 480 * 481 * The string must be freed with topo_mod_strfree() 482 */ 483 static char * 484 pi_enum_pciexrc_findbdf(topo_mod_t *mod, di_node_t dnode) 485 { 486 uint_t reg; 487 uint_t bdf; 488 char bdf_str[PCIEX_MAX_BDF_SIZE]; 489 unsigned char *buf; 490 di_prop_t di_prop; 491 di_prom_handle_t di_prom_hdl; 492 di_prom_prop_t di_prom_prop; 493 494 /* 495 * Look for the "reg" property from the devinfo node. 496 */ 497 for (di_prop = di_prop_next(dnode, DI_PROP_NIL); 498 di_prop != DI_PROP_NIL; 499 di_prop = di_prop_next(dnode, di_prop)) { 500 if (strncmp(di_prop_name(di_prop), "reg", 501 sizeof (reg)) == 0) { 502 if (di_prop_bytes(di_prop, &buf) < sizeof (uint_t)) { 503 continue; 504 } 505 bcopy(buf, ®, sizeof (uint_t)); 506 break; 507 } 508 } 509 510 /* 511 * If the "reg" property is not found in the di_node; look for it in 512 * OBP prom data. 513 */ 514 if (di_prop == DI_PROP_NIL) { 515 if ((di_prom_hdl = topo_mod_prominfo(mod)) == 516 DI_PROM_HANDLE_NIL) { 517 topo_mod_dprintf(mod, 518 "%s failed to get prom handle\n", _ENUM_NAME); 519 return (NULL); 520 } 521 for (di_prom_prop = 522 di_prom_prop_next(di_prom_hdl, dnode, DI_PROM_PROP_NIL); 523 di_prom_prop != DI_PROM_PROP_NIL; 524 di_prom_prop = 525 di_prom_prop_next(di_prom_hdl, dnode, di_prom_prop)) { 526 if (strncmp(di_prom_prop_name(di_prom_prop), "reg", 527 sizeof (reg)) == 0) { 528 if (di_prom_prop_data(di_prom_prop, &buf) < 529 sizeof (uint_t)) { 530 continue; 531 } 532 bcopy(buf, ®, sizeof (uint_t)); 533 break; 534 } 535 } 536 if (di_prom_prop == DI_PROP_NIL) { 537 topo_mod_dprintf(mod, 538 "%s failed to get reg property\n", _ENUM_NAME); 539 return (NULL); 540 } 541 } 542 543 /* 544 * Caculate BDF 545 * 546 * The reg property is divided like this: 547 * ----------------------------------------------------- 548 * | 23 Bus 16 | 15 Dev 11 | 10 Fn 8 | 7 Reg 0 | 549 * ----------------------------------------------------- 550 * 551 * PCI_REG_* macros strip off Reg and shift to get individual 552 * Bus/Dev/Fn bits. Shift and OR each to get bdf value. 553 */ 554 bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | 555 PCI_REG_FUNC_G(reg); 556 557 /* Pass BDF back as a string */ 558 (void) snprintf(bdf_str, PCIEX_MAX_BDF_SIZE, "0x%x", bdf); 559 topo_mod_dprintf(mod, "%s found BDF %s\n", _ENUM_NAME, bdf_str); 560 561 return (topo_mod_strdup(mod, bdf_str)); 562 } 563