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 #include <strings.h> 28 #include <sys/fm/protocol.h> 29 #include <fm/topo_hc.h> 30 #include <fm/topo_mod.h> 31 32 #include <hb_sun4.h> 33 #include <hostbridge.h> 34 #include <pcibus.h> 35 #include <did.h> 36 #include <util.h> 37 38 #include "hb_mdesc.h" 39 40 static const topo_pgroup_info_t io_pgroup = 41 { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 42 static const topo_pgroup_info_t pci_pgroup = 43 { TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 44 45 /* 46 * get_rcs() 47 * Description: 48 * Return a list of PX instances in the dev tree. 49 */ 50 static busorrc_t * 51 get_rcs(topo_mod_t *mod) 52 { 53 busorrc_t *rcs = NULL; 54 di_node_t devtree; 55 di_node_t pnode; 56 57 /* Scan for buses, top-level devinfo nodes with the right driver */ 58 devtree = topo_mod_devinfo(mod); 59 if (devtree == DI_NODE_NIL) { 60 topo_mod_dprintf(mod, "devinfo init failed.\n"); 61 return (NULL); 62 } 63 pnode = di_drv_first_node(PX, devtree); 64 while (pnode != DI_NODE_NIL) { 65 if (busorrc_add(mod, &rcs, pnode) < 0) { 66 topo_mod_dprintf(mod, "busorrc_add() failed.\n"); 67 busorrc_free(mod, rcs); 68 return (NULL); 69 } 70 pnode = di_drv_next_node(pnode); 71 } 72 return (rcs); 73 } 74 75 /* 76 * find_dnode() 77 * Description: 78 * Find the dev pointer of a rc given its bus address, ba 79 */ 80 static di_node_t 81 find_dnode(busorrc_t *rcs, uint64_t ba) 82 { 83 busorrc_t *p; 84 for (p = rcs; p != NULL; p = p->br_nextbus) { 85 if (ba == p->br_ba_bc) { 86 return (p->br_din); 87 } 88 } 89 return (NULL); 90 } 91 92 /* 93 * hb_tnode_create() 94 * Description: 95 * Create a topo node 96 */ 97 static tnode_t * 98 hb_tnode_create(topo_mod_t *mod, tnode_t *parent, const char *name, 99 int inst, void *priv) 100 { 101 int err; 102 tnode_t *node; 103 nvlist_t *fmri; 104 nvlist_t *auth = topo_mod_auth(mod, parent); 105 106 if (parent == NULL || inst < 0) { 107 return (NULL); 108 } 109 110 /* Create FMRI */ 111 if ((fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, 112 inst, NULL, auth, NULL, NULL, NULL)) == NULL) { 113 topo_mod_dprintf(mod, "create of tnode for %s failed: %s\n", 114 name, topo_strerror(topo_mod_errno(mod))); 115 nvlist_free(auth); 116 return (NULL); 117 } 118 nvlist_free(auth); 119 120 /* Create and bind node */ 121 node = topo_node_bind(mod, parent, name, inst, fmri); 122 if (node == NULL) { 123 nvlist_free(fmri); 124 topo_mod_dprintf(mod, "unable to bind a node(%s): %s\n", 125 name, topo_strerror(topo_mod_errno(mod))); 126 return (NULL); /* mod_errno already set */ 127 } 128 129 nvlist_free(fmri); 130 topo_node_setspecific(node, priv); 131 132 /* Inherit the parent 's FRU and label */ 133 (void) topo_node_fru_set(node, NULL, 0, &err); 134 (void) topo_node_label_set(node, NULL, &err); 135 136 return (node); 137 } 138 139 /* 140 * platform_pciexhostbridge_declare() 141 * Description: 142 * This is a sun4v specific function to create the hostbridge topo node. 143 */ 144 tnode_t * 145 platform_pciexhostbridge_declare(topo_mod_t *mod, tnode_t *parent, 146 topo_instance_t inst) 147 { 148 tnode_t *hbn; 149 void *priv = NULL; 150 151 topo_mod_dprintf(mod, "Create node %s=%d\n", HOSTBRIDGE, inst); 152 153 hbn = hb_tnode_create(mod, parent, HOSTBRIDGE, inst, priv); 154 if (hbn == NULL) { 155 topo_mod_dprintf(mod, "Failed to create node %s=%d\n", 156 HOSTBRIDGE, inst); 157 return (NULL); 158 } 159 160 /* Make room for children */ 161 (void) topo_node_range_create(mod, hbn, PCIEX_ROOT, 0, MAX_HB_BUSES); 162 163 return (hbn); 164 } 165 166 /* 167 * platform_pciexhostbridge_declare() 168 * Description: 169 * This is a sun4v specific function to create a root complex topo node, 170 * but do not enumerate its pci buses. 171 */ 172 static tnode_t * 173 platform_pciexrc_declare(topo_mod_t *mod, tnode_t *parent, int inst, 174 uint64_t ba) 175 { 176 int err; 177 tnode_t *rcn; 178 char dnpath[MAXPATHLEN]; 179 nvlist_t *fmri; 180 181 topo_mod_dprintf(mod, "Create node %s=%d\n", PCIEX_ROOT, inst); 182 183 rcn = hb_tnode_create(mod, parent, PCIEX_ROOT, inst, NULL); 184 if (rcn == NULL) { 185 topo_mod_dprintf(mod, "Failed to create node %s=%d\n", 186 PCIEX_ROOT, inst); 187 return (NULL); 188 } 189 190 /* Set ASRU to be the dev-scheme asru */ 191 (void) snprintf(dnpath, sizeof (dnpath), "/pci@%llx", ba); 192 fmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, dnpath, NULL); 193 if (fmri == NULL) { 194 topo_mod_dprintf(mod, "dev:///%s fmri creation failed.\n", 195 dnpath); 196 return (NULL); 197 } 198 if (topo_node_asru_set(rcn, fmri, 0, &err) < 0) { 199 topo_mod_dprintf(mod, "topo_node_asru_set failed\n"); 200 (void) topo_mod_seterrno(mod, err); 201 nvlist_free(fmri); 202 return (NULL); 203 } 204 nvlist_free(fmri); 205 206 /* 207 * Set properties of the root complex node pciexrc 208 */ 209 210 /* Add the io and pci property groups */ 211 if (topo_pgroup_create(rcn, &io_pgroup, &err) < 0) { 212 topo_mod_dprintf(mod, "topo_pgroup_create(iogrp) failed\n"); 213 (void) topo_mod_seterrno(mod, err); 214 return (NULL); 215 } 216 if (topo_pgroup_create(rcn, &pci_pgroup, &err) < 0) { 217 topo_mod_dprintf(mod, "topo_pgroup_create(pcigrp) failed\n"); 218 (void) topo_mod_seterrno(mod, err); 219 return (NULL); 220 } 221 /* Add the devfs path property */ 222 if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEV, 223 TOPO_PROP_IMMUTABLE, dnpath, &err) != 0) { 224 topo_mod_dprintf(mod, "Failed to set %s property\n", 225 TOPO_IO_DEV); 226 (void) topo_mod_seterrno(mod, err); 227 return (NULL); 228 } 229 230 /* for sun4v, device type is always pciex */ 231 if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEVTYPE, 232 TOPO_PROP_IMMUTABLE, PCIEXTYPE, &err) != 0) { 233 topo_mod_dprintf(mod, "Failed to set %s property\n", 234 TOPO_IO_DEVTYPE); 235 } 236 237 /* sun4v rc driver is always "px" */ 238 if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DRIVER, 239 TOPO_PROP_IMMUTABLE, PX, &err) != 0) { 240 topo_mod_dprintf(mod, "Failed to set %s property\n", 241 TOPO_IO_DRIVER); 242 } 243 if ((fmri = topo_mod_modfmri(mod, FM_MOD_SCHEME_VERSION, PX)) == NULL || 244 (topo_prop_set_fmri(rcn, TOPO_PGROUP_IO, TOPO_IO_MODULE, 245 TOPO_PROP_IMMUTABLE, fmri, &err) != 0)) { 246 topo_mod_dprintf(mod, "Failed to set %s property\n", 247 TOPO_IO_MODULE); 248 } 249 nvlist_free(fmri); 250 251 /* This is a PCIEX Root Complex */ 252 if (topo_prop_set_string(rcn, TOPO_PGROUP_PCI, TOPO_PCI_EXCAP, 253 TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err) != 0) { 254 topo_mod_dprintf(mod, "Failed to set %s property\n", 255 TOPO_PCI_EXCAP); 256 } 257 258 /* Make room for children */ 259 (void) topo_node_range_create(mod, rcn, PCIEX_BUS, 0, MAX_HB_BUSES); 260 261 return (rcn); 262 } 263 264 /* 265 * platform_hb_enum() 266 * Description: 267 * This is an entry function to enumerate the sun4v hostbridges. First, it 268 * reads the hostbridges and their pciexrc root complexes from the PRI or 269 * MD. 270 * For the current sun4v platforms, it is assummed that there is only one 271 * hostbridge. All the pciex root complexes belong to this single hostbridge. 272 * Given the hostbridge/pciexrc information, this enumerator creates the 273 * the hostbridge topo node and pciexrc nodes. If the domain owns the 274 * the root complex, it uses the common hostbridge code to enumerate the 275 * pcibus. If not, it simply create the hostbridge/pciexrc nodes without the 276 * fabric. 277 */ 278 /*ARGSUSED*/ 279 int 280 platform_hb_enum(topo_mod_t *mod, tnode_t *parent, const char *name, 281 topo_instance_t imin, topo_instance_t imax) 282 { 283 int i, j; 284 int err = 0; 285 md_hb_t *hbp; 286 md_rc_t *rcp; 287 md_info_t hbmd; 288 tnode_t **hbnode; 289 int nhbnode = 0; 290 tnode_t **rcnode; 291 int nrcs, nrcnode = 0; 292 busorrc_t *rcs; 293 294 if (imin < 0 || imax < 0 || imin > imax) { 295 topo_mod_dprintf(mod, "Invalid hb range(%d,%d)\n", imin, imax); 296 return (-1); 297 } 298 299 /* get the hostbrige and rootcomplex information in the PRI/MD */ 300 (void) bzero((void *) &hbmd, sizeof (hbmd)); 301 if (hb_mdesc_init(mod, &hbmd) != 0) { 302 topo_mod_dprintf(mod, "failed to get hb from the PRI/MD\n"); 303 return (-1); 304 } 305 306 /* count the number of hb and rc in the PRI/MD */ 307 nrcs = 0; 308 for (i = 0, hbp = hbmd.hbs; i < hbmd.shbs; i++, hbp++) { 309 if (hbp->id < 0) 310 continue; 311 nrcs += hbp->srcs; 312 } 313 if (hbmd.shbs <= 0 || nrcs <= 0) { 314 topo_mod_dprintf(mod, "No hostbridge or pciex bus is found\n"); 315 topo_node_range_destroy(parent, HOSTBRIDGE); 316 hb_mdesc_fini(mod, &hbmd); 317 return (0); 318 } 319 hbnode = topo_mod_zalloc(mod, hbmd.shbs * sizeof (tnode_t *)); 320 rcnode = topo_mod_zalloc(mod, nrcs * sizeof (tnode_t *)); 321 rcs = get_rcs(mod); 322 323 /* process the hostbridge */ 324 for (i = imin; (i <= imax) && (err == 0); i++) { 325 int brd = 0; 326 di_node_t dnode1, dnode2; 327 328 if ((hbp = hb_find_hb(&hbmd, i)) == NULL) { 329 continue; 330 } 331 332 dnode2 = NULL; 333 for (j = 0, rcp = hbp->rcs; j < hbp->srcs; j++, rcp++) { 334 if (rcp->id < 0) 335 continue; 336 dnode1 = find_dnode(rcs, rcp->cfg_handle); 337 if (dnode1 != NULL) { 338 dnode2 = dnode1; 339 if (did_create(mod, dnode1, brd, hbp->id, 340 rcp->id, rcp->id) == NULL) { 341 err = -1; 342 break; 343 } 344 } 345 } 346 347 if (err != 0) 348 break; 349 350 /* 351 * If this hb has a rc in the dev tree, use the common code to 352 * create a hostbridge node 353 */ 354 if (dnode2 != NULL) { 355 hbnode[nhbnode] = pciexhostbridge_declare(mod, parent, 356 dnode2, hbp->id); 357 } else { 358 /* platformm specific */ 359 hbnode[nhbnode] = platform_pciexhostbridge_declare(mod, 360 parent, hbp->id); 361 } 362 if (hbnode[nhbnode] == NULL) { 363 err = -1; 364 break; 365 } 366 367 /* 368 * Create the pciexrc nodes under the hostbridge node 369 * If a rc exists in the dev tree, use the common code to 370 * create a pciexrc node and enumerate the fabric. 371 * Otherwise, only create the pciexrc node. 372 */ 373 for (j = 0, rcp = hbp->rcs; j < hbp->nrcs; j++, rcp++) { 374 if (rcp->id < 0) { 375 topo_mod_dprintf(mod, "skip invalid rc[%d]\n", 376 j); 377 continue; 378 } 379 dnode1 = find_dnode(rcs, rcp->cfg_handle); 380 if (dnode1 != NULL) { 381 /* declare a pciexrc and enumerate its pcibus */ 382 rcnode[nrcnode] = rc_process(mod, 383 hbnode[nhbnode], rcp->id, dnode1); 384 } else { 385 /* only declare the pciexrc */ 386 rcnode[nrcnode] = platform_pciexrc_declare(mod, 387 hbnode[nhbnode], rcp->id, rcp->cfg_handle); 388 } 389 if (rcnode[nrcnode] == NULL) { 390 err = -1; 391 break; 392 } 393 nrcnode++; 394 } 395 396 nhbnode++; 397 } 398 399 /* failure: unbind all hb and rc tnodes */ 400 if (err != 0) { 401 for (i = 0; i < nhbnode; i++) 402 topo_node_unbind(hbnode[i]); 403 for (i = 0; i < nrcnode; i++) 404 topo_node_unbind(rcnode[i]); 405 } 406 407 topo_mod_free(mod, hbnode, hbmd.shbs * sizeof (tnode_t *)); 408 topo_mod_free(mod, rcnode, nrcs * sizeof (tnode_t *)); 409 hb_mdesc_fini(mod, &hbmd); 410 busorrc_free(mod, rcs); 411 412 return (err); 413 } 414 415 /*ARGSUSED*/ 416 int 417 platform_hb_label(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out) 418 { 419 return (labelmethod_inherit(mod, node, in, out)); 420 } 421