Home | History | Annotate | Download | only in hostbridge
      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