Home | History | Annotate | Download | only in chip
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <stdarg.h>
     30 #include <string.h>
     31 #include <strings.h>
     32 #include <libnvpair.h>
     33 #include <kstat.h>
     34 #include <unistd.h>
     35 #include <sys/types.h>
     36 #include <fm/topo_mod.h>
     37 #include <sys/devfm.h>
     38 #include <fm/fmd_agent.h>
     39 
     40 #define	BUFSZ	128
     41 
     42 char *
     43 get_fmtstr(topo_mod_t *mod, nvlist_t *in)
     44 {
     45 	char *fmtstr;
     46 	nvlist_t *args;
     47 	int ret;
     48 
     49 	topo_mod_dprintf(mod, "get_fmtstr() called\n");
     50 
     51 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
     52 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
     53 		    strerror(ret));
     54 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
     55 		return (NULL);
     56 	}
     57 	if ((ret = nvlist_lookup_string(args, "format", &fmtstr)) != 0) {
     58 		topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n",
     59 		    strerror(ret));
     60 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
     61 		return (NULL);
     62 	}
     63 	return (fmtstr);
     64 }
     65 
     66 int
     67 store_prop_val(topo_mod_t *mod, char *buf, char *propname, nvlist_t **out)
     68 {
     69 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) {
     70 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
     71 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
     72 	}
     73 	if (nvlist_add_string(*out, TOPO_PROP_VAL_NAME, propname) != 0) {
     74 		topo_mod_dprintf(mod, "Failed to set '%s'\n",
     75 		    TOPO_PROP_VAL_NAME);
     76 		nvlist_free(*out);
     77 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
     78 	}
     79 	if (nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING)
     80 	    != 0) {
     81 		topo_mod_dprintf(mod, "Failed to set '%s'\n",
     82 		    TOPO_PROP_VAL_TYPE);
     83 		nvlist_free(*out);
     84 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
     85 	}
     86 	if (nvlist_add_string(*out, TOPO_PROP_VAL_VAL, buf) != 0) {
     87 		topo_mod_dprintf(mod, "Failed to set '%s'\n",
     88 		    TOPO_PROP_VAL_VAL);
     89 		nvlist_free(*out);
     90 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
     91 	}
     92 	return (0);
     93 }
     94 
     95 /*
     96  * This is a somewhat generic property method for labelling the dimm slots on
     97  * uni-socket x86/x64 platforms.  This method assumes a direct linear
     98  * correlation between the dimm topo node instance number and the dimm slot
     99  * label number.  It takes the following two arguments:
    100  *
    101  * format:	a string containing a printf-like format with a single %d token
    102  *              which this method computes
    103  *
    104  *              i.e.: DIMM %d
    105  *
    106  * offset:      a numeric offset that we'll number the dimms from.  This is to
    107  *              allow for the fact that some systems number the dimm slots
    108  *              from zero and others start from one (like the Ultra 20)
    109  */
    110 /* ARGSUSED */
    111 int
    112 simple_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    113     nvlist_t *in, nvlist_t **out)
    114 {
    115 	char *fmtstr, buf[BUFSZ];
    116 	int ret;
    117 	uint32_t offset;
    118 	nvlist_t *args;
    119 
    120 	topo_mod_dprintf(mod, "simple_dimm_label() called\n");
    121 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    122 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    123 		    strerror(ret));
    124 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    125 	}
    126 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    127 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    128 		    strerror(ret));
    129 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    130 	}
    131 
    132 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    133 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    134 		/* topo errno already set */
    135 		return (-1);
    136 	}
    137 
    138 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    139 	(void) snprintf(buf, BUFSZ, fmtstr,
    140 	    (topo_node_instance(node) + offset));
    141 
    142 	if (store_prop_val(mod, buf, "label", out) != 0) {
    143 		topo_mod_dprintf(mod, "Failed to set label\n");
    144 		/* topo errno already set */
    145 		return (-1);
    146 	}
    147 
    148 	return (0);
    149 }
    150 
    151 
    152 /*
    153  * This is a somewhat generic property method for labelling the dimm slots on
    154  * multi-socket x86/x64 platforms.  It takes the following two arguments:
    155  *
    156  * format:	a string containing a printf-like format with a two %d tokens
    157  *              for the cpu and dimm slot label numbers, which this method
    158  *              computes
    159  *
    160  *              i.e.: CPU %d DIMM %d
    161  *
    162  * offset:      a numeric offset that we'll number the dimms from.  This is to
    163  *              allow for the fact that some systems number the dimm slots
    164  *              from zero while others may start from one
    165  *
    166  * order:	"reverse" or "forward" - sets the direction of the correlation
    167  *              between dimm topo node instance number and DIMM slot number
    168  *
    169  * dimms_per_chip:  the number of DIMM slots per chip
    170  */
    171 /* ARGSUSED */
    172 int
    173 simple_dimm_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    174     nvlist_t *in, nvlist_t **out)
    175 {
    176 	char *fmtstr, *order, buf[BUFSZ];
    177 	tnode_t *chip;
    178 	int ret;
    179 	uint32_t offset, dimms_per_chip;
    180 	nvlist_t *args;
    181 
    182 	topo_mod_dprintf(mod, "simple_dimm_label_mp() called\n");
    183 
    184 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    185 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    186 		    strerror(ret));
    187 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    188 	}
    189 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    190 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    191 		    strerror(ret));
    192 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    193 	}
    194 	if ((ret = nvlist_lookup_uint32(args, "dimms_per_chip",
    195 	    &dimms_per_chip)) != 0) {
    196 		topo_mod_dprintf(mod, "Failed to lookup 'dimms_per_chip' arg "
    197 		    "(%s)\n", strerror(ret));
    198 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    199 	}
    200 	if ((ret = nvlist_lookup_string(args, "order", &order)) != 0) {
    201 		topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n",
    202 		    strerror(ret));
    203 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    204 	}
    205 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    206 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    207 		topo_mod_free(mod, order, BUFSZ);
    208 		/* topo errno already set */
    209 		return (-1);
    210 	}
    211 
    212 	chip = topo_node_parent(topo_node_parent(node));
    213 
    214 	if (strcasecmp(order, "forward") == 0)
    215 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
    216 		(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
    217 		    (topo_node_instance(node) + offset));
    218 	else if (strcasecmp(order, "reverse") == 0)
    219 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
    220 		(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
    221 		    (((topo_node_instance(chip) + 1) * dimms_per_chip)
    222 		    - (topo_node_instance(node)) - 1 + offset));
    223 	else {
    224 		topo_mod_dprintf(mod, "Invalid value for order arg\n");
    225 		topo_mod_free(mod, order, BUFSZ);
    226 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    227 	}
    228 
    229 	if (store_prop_val(mod, buf, "label", out) != 0) {
    230 		topo_mod_dprintf(mod, "Failed to set label\n");
    231 		topo_mod_free(mod, order, BUFSZ);
    232 		/* topo errno already set */
    233 		return (-1);
    234 	}
    235 
    236 	return (0);
    237 }
    238 
    239 /*
    240  * This method assumes a correspondence between the dimm topo node instance
    241  * number and the dimm slot label number, but unlike simple_chip_label_mp, the
    242  * slot numbers aren't reused between CPU's.  This method assumes there
    243  * are 4 DIMM slots per chip.  It takes the following two arguments:
    244  *
    245  * format:	a string containing a printf-like format with a single %d token
    246  *              which this method computes
    247  *
    248  *              i.e.: DIMM %d
    249  *
    250  * offset:      a numeric offset that we'll number the dimms from.  This is to
    251  *              allow for the fact that some systems number the dimm slots
    252  *              from zero and others may start from one
    253  *
    254  * order:	"reverse" or "forward" - sets the direction of the correlation
    255  *              between dimm topo node instance number and DIMM slot number
    256  */
    257 /* ARGSUSED */
    258 int
    259 seq_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    260     nvlist_t *in, nvlist_t **out)
    261 {
    262 	char *fmtstr, *order, buf[BUFSZ];
    263 	int ret;
    264 	uint32_t offset;
    265 	nvlist_t *args;
    266 	tnode_t *chip;
    267 
    268 	topo_mod_dprintf(mod, "seq_dimm_label() called\n");
    269 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    270 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    271 		    strerror(ret));
    272 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    273 	}
    274 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    275 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    276 		    strerror(ret));
    277 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    278 	}
    279 	if ((ret = nvlist_lookup_string(args, "order", &order)) != 0) {
    280 		topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n",
    281 		    strerror(ret));
    282 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    283 	}
    284 
    285 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    286 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    287 		topo_mod_free(mod, order, BUFSZ);
    288 		/* topo errno already set */
    289 		return (-1);
    290 	}
    291 
    292 	chip = topo_node_parent(topo_node_parent(node));
    293 
    294 	if (strcasecmp(order, "forward") == 0)
    295 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
    296 		(void) snprintf(buf, BUFSZ, fmtstr, ((topo_node_instance(node))
    297 		    + (topo_node_instance(chip) * 4) + offset));
    298 	else if (strcasecmp(order, "reverse") == 0)
    299 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
    300 		(void) snprintf(buf, BUFSZ, fmtstr,
    301 		    (((topo_node_instance(chip) + 1) * 4)
    302 		    - (topo_node_instance(node)) - 1 + offset));
    303 	else {
    304 		topo_mod_dprintf(mod, "Invalid value for order arg\n");
    305 		topo_mod_free(mod, order, BUFSZ);
    306 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    307 	}
    308 
    309 	if (store_prop_val(mod, buf, "label", out) != 0) {
    310 		topo_mod_dprintf(mod, "Failed to set label\n");
    311 		topo_mod_free(mod, order, BUFSZ);
    312 		/* topo errno already set */
    313 		return (-1);
    314 	}
    315 
    316 	return (0);
    317 }
    318 
    319 
    320 /*
    321  * This is a somewhat generic property method for labelling the CPU sockets on
    322  * x86/x64 platforms.  This method assumes a correspondence between
    323  * the chip topo node instance number and the CPU socket label number.  It takes
    324  * the following two arguments:
    325  *
    326  * format:	a string containing a printf-like format with a single %d token
    327  *              which this method computes
    328  *
    329  *              i.e.: CPU %d
    330  *
    331  * offset:      a numeric offset that we'll number the CPU's from.  This is to
    332  *              allow for the fact that some systems number the CPU sockets
    333  *              from zero and others start from one (like the X4X00-M2 systems)
    334  */
    335 /* ARGSUSED */
    336 int
    337 simple_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    338     nvlist_t *in, nvlist_t **out)
    339 {
    340 	char *fmtstr, buf[BUFSZ];
    341 	int ret;
    342 	uint32_t offset;
    343 	nvlist_t *args;
    344 
    345 	topo_mod_dprintf(mod, "simple_chip_label() called\n");
    346 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    347 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    348 		    strerror(ret));
    349 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    350 	}
    351 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    352 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    353 		    strerror(ret));
    354 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    355 	}
    356 
    357 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    358 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    359 		/* topo errno already set */
    360 		return (-1);
    361 	}
    362 
    363 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    364 	(void) snprintf(buf, BUFSZ, fmtstr,
    365 	    (topo_node_instance(node) + offset));
    366 
    367 	if (store_prop_val(mod, buf, "label", out) != 0) {
    368 		topo_mod_dprintf(mod, "Failed to set label\n");
    369 		/* topo errno already set */
    370 		return (-1);
    371 	}
    372 
    373 	return (0);
    374 }
    375 
    376 
    377 /*
    378  * This is a somewhat generic property method for labelling the CPU sockets on
    379  * x86/x64 platforms.  This method assumes a correspondence between
    380  * the chip topo node instance number and the CPU socket label number.  It takes
    381  * the following argument:
    382  *
    383  * format:	a string containing a printf-like format with a single %d token
    384  *              which this method computes
    385  *
    386  *              i.e.: CPU %d
    387  *
    388  * offset:      a numeric offset that we'll number the CPU's from.  This is to
    389  *              allow for the fact that some systems number the CPU sockets
    390  *              from zero and others start from one (like the X8450 systems)
    391  */
    392 /* ARGSUSED */
    393 int
    394 fsb2_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    395     nvlist_t *in, nvlist_t **out)
    396 {
    397 	char *fmtstr, buf[BUFSZ];
    398 	int ret;
    399 	uint32_t offset;
    400 	nvlist_t *args;
    401 
    402 	topo_mod_dprintf(mod, "fsb2_chip_label() called\n");
    403 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    404 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    405 		    strerror(ret));
    406 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    407 	}
    408 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    409 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    410 		    strerror(ret));
    411 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    412 	}
    413 
    414 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    415 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    416 		/* topo errno already set */
    417 		return (-1);
    418 	}
    419 
    420 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    421 	(void) snprintf(buf, BUFSZ, fmtstr,
    422 	    ((topo_node_instance(node) / 2) + offset));
    423 
    424 	if (store_prop_val(mod, buf, "label", out) != 0) {
    425 		topo_mod_dprintf(mod, "Failed to set label\n");
    426 		/* topo errno already set */
    427 		return (-1);
    428 	}
    429 
    430 	return (0);
    431 }
    432 
    433 
    434 /*
    435  * This is a custom property method for generating the CPU slot label for the
    436  * Galaxy 4E/4F platforms.
    437  *
    438  * format:	a string containing a printf-like format with a single %c token
    439  *              which this method computes
    440  *
    441  *              i.e.: CPU %c
    442  */
    443 /* ARGSUSED */
    444 int
    445 g4_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    446     nvlist_t *in, nvlist_t **out)
    447 {
    448 	char *fmtstr, buf[BUFSZ], slot_id;
    449 	int err, htid, mapidx;
    450 	uint32_t num_nodes;
    451 	/*
    452 	 * G4 HT node ID to FRU label translation.  The g4map array
    453 	 * is indexed by (number of coherent nodes) / 2 - 1.
    454 	 * The value for a given number of nodes is a char array
    455 	 * indexed by node ID.
    456 	 */
    457 	const char *g4map[] = {
    458 	    "AB",	/* 2 nodes */
    459 	    "ADEH",	/* 4 nodes */
    460 	    "ABDEFH",	/* 6 nodes */
    461 	    "ACBDEFGH"	/* 8 nodes */
    462 	};
    463 
    464 	topo_mod_dprintf(mod, "g4_chip_label() called\n");
    465 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    466 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    467 		/* topo errno already set */
    468 		return (-1);
    469 	}
    470 	/*
    471 	 * The chip-properties property will not exist if this platform has
    472 	 * AMD family 0x10 modules.  In that case we don't want to treat it as a
    473 	 * fatal error as that will cause calls like topo_prop_getprops to fail
    474 	 * to return any properties on this node.  Therefore, if the topo errno
    475 	 * is set to ETOPO_PROP_NOENT, then we'll just set an empty label
    476 	 * and return 0.  If the topo errno is set to anything else we'll
    477 	 * return -1.
    478 	 */
    479 	if (topo_prop_get_uint32(node, "chip-properties", "CoherentNodes",
    480 	    &num_nodes, &err) != 0) {
    481 		if (err == ETOPO_PROP_NOENT) {
    482 			if (store_prop_val(mod, "", "label", out) != 0) {
    483 				topo_mod_dprintf(mod, "Failed to set label\n");
    484 				/* topo errno already set */
    485 				return (-1);
    486 			}
    487 			return (0);
    488 		} else {
    489 			topo_mod_dprintf(mod, "Failed to lookup 'CoherentNodes'"
    490 			    "property\n");
    491 			return (topo_mod_seterrno(mod, err));
    492 		}
    493 	}
    494 
    495 	mapidx = num_nodes / 2 - 1;
    496 	htid = topo_node_instance(node);
    497 
    498 	/* HT nodes must number 0 .. num_nodes - 1 */
    499 	if (htid >= num_nodes) {
    500 		topo_mod_dprintf(mod, "chip node instance range check failed:"
    501 		    "num_nodes=%d, instance=%d\n", num_nodes, htid);
    502 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    503 	}
    504 
    505 	switch (num_nodes) {
    506 		case (2):
    507 		case (4):
    508 		case (6):
    509 		case (8):
    510 			/* htid is already range-checked */
    511 			mapidx = num_nodes / 2 - 1;
    512 			slot_id = g4map[mapidx][htid];
    513 			break;
    514 		default:
    515 			topo_mod_dprintf(mod, "Invalid number of CoherentNodes:"
    516 			    " %d\n", num_nodes);
    517 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    518 	}
    519 
    520 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    521 	(void) snprintf(buf, BUFSZ, fmtstr, slot_id);
    522 
    523 	if (store_prop_val(mod, buf, "label", out) != 0) {
    524 		topo_mod_dprintf(mod, "Failed to set label\n");
    525 		/* topo errno already set */
    526 		return (-1);
    527 	}
    528 
    529 	return (0);
    530 }
    531 
    532 /*
    533  * Utility function used by a4fplus_chip_label to determine the number of chips
    534  * (as opposed to processors) that are installed in the system by counting
    535  * the unique chipids.
    536  */
    537 static int
    538 get_num_chips(topo_mod_t *mod)
    539 {
    540 	fmd_agent_hdl_t *hdl;
    541 	nvlist_t **cpus;
    542 	uint_t ncpu;
    543 	int i, nchip = 0;
    544 	int32_t chipid;
    545 	uint64_t bitmap = 0;
    546 
    547 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
    548 		return (-1);
    549 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) == -1) {
    550 		topo_mod_dprintf(mod, "get physcpu info failed:%s\n",
    551 		    fmd_agent_errmsg(hdl));
    552 		fmd_agent_close(hdl);
    553 		return (-1);
    554 	}
    555 	fmd_agent_close(hdl);
    556 
    557 	for (i = 0; i < ncpu; i++) {
    558 		if (nvlist_lookup_int32(cpus[i], FM_PHYSCPU_INFO_CHIP_ID,
    559 		    &chipid) != 0 || chipid >= 64) {
    560 			topo_mod_dprintf(mod, "lookup chipid failed\n");
    561 			nchip = -1;
    562 			break;
    563 		}
    564 		if ((bitmap & (1 << chipid)) != 0) {
    565 			bitmap |= (1 << chipid);
    566 			nchip++;
    567 		}
    568 	}
    569 
    570 	for (i = 0; i < ncpu; i++)
    571 		nvlist_free(cpus[i]);
    572 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
    573 
    574 	return (nchip);
    575 }
    576 
    577 /*
    578  * This is a custom property method for generating the CPU slot label for the
    579  * Andromeda Fplus platforms.
    580  *
    581  * format:	a string containing a printf-like format with a single %d token
    582  *              which this method computes
    583  *
    584  *              i.e.: CPU %d
    585  */
    586 /* ARGSUSED */
    587 int
    588 a4fplus_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    589     nvlist_t *in, nvlist_t **out)
    590 {
    591 	char *fmtstr, buf[BUFSZ];
    592 	int num_nodes;
    593 
    594 	topo_mod_dprintf(mod, "a4fplus_chip_label() called\n");
    595 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    596 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    597 		/* topo errno already set */
    598 		return (-1);
    599 	}
    600 
    601 	/*
    602 	 * Normally we'd figure out the total number of chip nodes by looking
    603 	 * at the CoherentNodes property.  However, due to the lack of a memory
    604 	 * controller driver for family 0x10, this property wont exist on the
    605 	 * chip nodes on A4Fplus.
    606 	 */
    607 	if ((num_nodes = get_num_chips(mod)) < 0) {
    608 		topo_mod_dprintf(mod, "Failed to determine number of chip "
    609 		    "nodes\n");
    610 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
    611 	}
    612 	switch (num_nodes) {
    613 		case (2):
    614 			/* LINTED: E_SEC_PRINTF_VAR_FMT */
    615 			(void) snprintf(buf, BUFSZ, fmtstr,
    616 			    topo_node_instance(node) + 2);
    617 			break;
    618 		case (4):
    619 			/* LINTED: E_SEC_PRINTF_VAR_FMT */
    620 			(void) snprintf(buf, BUFSZ, fmtstr,
    621 			    topo_node_instance(node));
    622 			break;
    623 		default:
    624 			topo_mod_dprintf(mod, "Invalid number of chip nodes:"
    625 			    " %d\n", num_nodes);
    626 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    627 	}
    628 
    629 
    630 	if (store_prop_val(mod, buf, "label", out) != 0) {
    631 		topo_mod_dprintf(mod, "Failed to set label\n");
    632 		/* topo errno already set */
    633 		return (-1);
    634 	}
    635 
    636 	return (0);
    637 }
    638 
    639 /*
    640  * This is a somewhat generic property method for labelling the chip-select
    641  * nodes on multi-socket AMD family 0x10 platforms.  This is necessary because
    642  * these platforms are not supported by the current AMD memory controller driver
    643  * and therefore we're not able to discover the memory topology on AMD family
    644  * 0x10 systems.  As a result, instead of enumerating the installed dimms and
    645  * their ranks, the chip enumerator generically enumerates all of the possible
    646  * chip-selects beneath each dram channel.
    647  *
    648  * When we diagnose a dimm fault, the FRU fmri will be for the chip-select node,
    649  * so we need to attach FRU labels to the chip-select nodes.
    650  *
    651  * format:	a string containing a printf-like format with a two %d tokens
    652  *              for the cpu and dimm slot label numbers, which this method
    653  *              computes
    654  *
    655  *              i.e.: CPU %d DIMM %d
    656  *
    657  * offset:      a numeric offset that we'll number the dimms from.  This is to
    658  *              allow for the fact that some systems may number the dimm slots
    659  *              from zero while others may start from one
    660  *
    661  * This function computes the DIMM slot number using the following formula:
    662  *
    663  * 	slot = cs - (cs % 2) + channel + offset
    664  */
    665 /* ARGSUSED */
    666 int
    667 simple_cs_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    668     nvlist_t *in, nvlist_t **out)
    669 {
    670 	char *fmtstr, buf[BUFSZ];
    671 	tnode_t *chip, *chan;
    672 	int dimm_num, ret;
    673 	uint32_t offset;
    674 	nvlist_t *args;
    675 
    676 	topo_mod_dprintf(mod, "simple_cs_label_mp() called\n");
    677 
    678 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    679 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    680 		    strerror(ret));
    681 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    682 	}
    683 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    684 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    685 		    strerror(ret));
    686 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    687 	}
    688 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    689 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    690 		/* topo errno already set */
    691 		return (-1);
    692 	}
    693 
    694 	chip = topo_node_parent(topo_node_parent(topo_node_parent(node)));
    695 	chan = topo_node_parent(node);
    696 
    697 	dimm_num = topo_node_instance(node) - (topo_node_instance(node) % 2)
    698 	    + topo_node_instance(chan) + offset;
    699 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    700 	(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
    701 	    dimm_num);
    702 
    703 	if (store_prop_val(mod, buf, "label", out) != 0) {
    704 		topo_mod_dprintf(mod, "Failed to set label\n");
    705 		/* topo errno already set */
    706 		return (-1);
    707 	}
    708 
    709 	return (0);
    710 }
    711 
    712 /* ARGSUSED */
    713 int
    714 g4_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    715     nvlist_t *in, nvlist_t **out)
    716 {
    717 	char *fmtstr, *chip_lbl, buf[BUFSZ];
    718 	tnode_t *chip;
    719 	int ret, err = 0;
    720 	uint32_t offset;
    721 	nvlist_t *args;
    722 
    723 	topo_mod_dprintf(mod, "g4_dimm_label() called\n");
    724 
    725 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    726 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    727 		    strerror(ret));
    728 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    729 	}
    730 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
    731 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
    732 		    strerror(ret));
    733 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    734 	}
    735 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    736 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    737 		/* topo errno already set */
    738 		return (-1);
    739 	}
    740 
    741 	/*
    742 	 * The 4600/4600M2 have a weird way of labeling the chip nodes, so
    743 	 * instead of trying to recompute it, we'll simply look it up and
    744 	 * prepend it to our dimm label.
    745 	 */
    746 	chip = topo_node_parent(topo_node_parent(node));
    747 	if (topo_prop_get_string(chip, TOPO_PGROUP_PROTOCOL, "label", &chip_lbl,
    748 	    &err) != 0) {
    749 		topo_mod_dprintf(mod, "Failed to lookup label prop on %s=%d\n",
    750 		    topo_node_name(chip), topo_node_instance(chip),
    751 		    topo_strerror(err));
    752 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    753 	}
    754 
    755 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    756 	(void) snprintf(buf, BUFSZ, fmtstr, chip_lbl,
    757 	    (topo_node_instance(node) + offset));
    758 
    759 	topo_mod_strfree(mod, chip_lbl);
    760 
    761 	if (store_prop_val(mod, buf, "label", out) != 0) {
    762 		topo_mod_dprintf(mod, "Failed to set label\n");
    763 		/* topo errno already set */
    764 		return (-1);
    765 	}
    766 
    767 	return (0);
    768 }
    769 
    770 /*
    771  * This method is used to compute the labels for DIMM slots on the Galaxy 1F and
    772  * 2F platforms.  It results in following dimm node label assignments:
    773  *
    774  * chip/dimm instances      label
    775  * -------------------      -----
    776  * chip=0/dimm=0            CPU 1 DIMM A0
    777  * chip=0/dimm=1            CPU 1 DIMM B0
    778  * chip=0/dimm=2            CPU 1 DIMM A1
    779  * chip=0/dimm=3            CPU 1 DIMM B1
    780  *
    781  * chip=1/dimm=0            CPU 2 DIMM A0
    782  * chip=1/dimm=1            CPU 2 DIMM B0
    783  * chip=1/dimm=2            CPU 2 DIMM A1
    784  * chip=1/dimm=3            CPU 2 DIMM B1
    785  */
    786 /* ARGSUSED */
    787 int
    788 g12f_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
    789     nvlist_t *in, nvlist_t **out)
    790 {
    791 	char *fmtstr, buf[BUFSZ], chan;
    792 	tnode_t *chip;
    793 	int ret, dimm_inst, slot_num;
    794 	nvlist_t *args;
    795 
    796 	topo_mod_dprintf(mod, "g12f_dimm_label() called\n");
    797 
    798 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
    799 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
    800 		    strerror(ret));
    801 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    802 	}
    803 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
    804 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
    805 		/* topo errno already set */
    806 		return (-1);
    807 	}
    808 
    809 	chip = topo_node_parent(topo_node_parent(node));
    810 	dimm_inst = topo_node_instance(node);
    811 	chan = dimm_inst == 0 || dimm_inst == 2 ? 'A': 'B';
    812 	slot_num = (dimm_inst <= 1 ? 0 : 1);
    813 
    814 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    815 	(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip) + 1, chan,
    816 	    slot_num);
    817 
    818 	if (store_prop_val(mod, buf, "label", out) != 0) {
    819 		topo_mod_dprintf(mod, "Failed to set label\n");
    820 		/* topo errno already set */
    821 		return (-1);
    822 	}
    823 
    824 	return (0);
    825 }
    826