Home | History | Annotate | Download | only in sun4vpi
      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 2009 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 		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 		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 			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 		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 		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 		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 		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 		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 			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 			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, &reg, 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, &reg, 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