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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * AMD memory enumeration
     29  */
     30 
     31 #include <sys/types.h>
     32 #include <unistd.h>
     33 #include <stropts.h>
     34 #include <sys/fm/protocol.h>
     35 #include <sys/mc.h>
     36 #include <sys/mc_amd.h>
     37 #include <fm/topo_mod.h>
     38 #include <strings.h>
     39 #include <sys/stat.h>
     40 #include <fcntl.h>
     41 
     42 #include "chip.h"
     43 
     44 #define	MAX_CHANNUM	1
     45 #define	MAX_DIMMNUM	7
     46 #define	MAX_CSNUM	7
     47 
     48 static const topo_pgroup_info_t cs_pgroup =
     49 	{ PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
     50 static const topo_pgroup_info_t dimm_pgroup =
     51 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
     52 static const topo_pgroup_info_t mc_pgroup =
     53 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
     54 static const topo_pgroup_info_t rank_pgroup =
     55 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
     56 static const topo_pgroup_info_t chan_pgroup =
     57 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
     58 
     59 static const topo_method_t dimm_methods[] = {
     60 	{ SIMPLE_DIMM_LBL, "Property method", 0,
     61 	    TOPO_STABILITY_INTERNAL, simple_dimm_label},
     62 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0,
     63 	    TOPO_STABILITY_INTERNAL, simple_dimm_label_mp},
     64 	{ SEQ_DIMM_LBL, "Property method", 0,
     65 	    TOPO_STABILITY_INTERNAL, seq_dimm_label},
     66 	{ G4_DIMM_LBL, "Property method", 0,
     67 	    TOPO_STABILITY_INTERNAL, g4_dimm_label},
     68 	{ G12F_DIMM_LBL, "Property method", 0,
     69 	    TOPO_STABILITY_INTERNAL, g12f_dimm_label},
     70 	{ GET_DIMM_SERIAL, "Property method", 0,
     71 	    TOPO_STABILITY_INTERNAL, get_dimm_serial},
     72 	{ NULL }
     73 };
     74 
     75 const topo_method_t rank_methods[] = {
     76 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
     77 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
     78 	    mem_asru_compute },
     79 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
     80 	    TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL,
     81 	    rank_fmri_present },
     82 	{ TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
     83 	    TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
     84 	    rank_fmri_replaced },
     85 	{ NULL }
     86 };
     87 
     88 const topo_method_t ntv_page_retire_methods[] = {
     89 	{ TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
     90 	    TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
     91 	    ntv_page_retire },
     92 	{ TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
     93 	    TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
     94 	    ntv_page_unretire },
     95 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
     96 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
     97 	    ntv_page_service_state },
     98 	{ NULL }
     99 };
    100 
    101 /*
    102  * Serials, Labels are obtained from SMBIOS, so
    103  * we leave out the related methods, any other
    104  * methods that will be added to gen_cs_methods
    105  * should be added to x86pi_gen_cs_methods too
    106  */
    107 static const topo_method_t x86pi_gen_cs_methods[] = {
    108 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
    109 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
    110 	    mem_asru_compute },
    111 	{ NULL }
    112 };
    113 
    114 static const topo_method_t gen_cs_methods[] = {
    115 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
    116 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
    117 	    mem_asru_compute },
    118 	{ SIMPLE_CS_LBL_MP, "Property method", 0,
    119 	    TOPO_STABILITY_INTERNAL, simple_cs_label_mp},
    120 	{ GET_DIMM_SERIAL, "Property method", 0,
    121 	    TOPO_STABILITY_INTERNAL, get_dimm_serial},
    122 	{ NULL }
    123 };
    124 
    125 static nvlist_t *cs_fmri[MC_CHIP_NCS];
    126 
    127 /*
    128  * Called when there is no memory-controller driver to provide topology
    129  * information.  Generate a maximal memory topology that is appropriate
    130  * for the chip revision.  The memory-controller node has already been
    131  * bound as mcnode, and the parent of that is cnode.
    132  *
    133  * We create a tree of dram-channel and chip-select nodes below the
    134  * memory-controller node.  There will be two dram channels and 8 chip-selects
    135  * below each, regardless of actual socket type, processor revision and so on.
    136  * This is adequate for generic diagnosis up to family 0x10 revision D.
    137  */
    138 /*ARGSUSED*/
    139 static int
    140 amd_generic_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *cnode,
    141     tnode_t *mcnode, int family, int model, nvlist_t *auth)
    142 {
    143 	int chan, cs;
    144 
    145 	/*
    146 	 * Elsewhere we have already returned for families less than 0xf.
    147 	 * This "generic" topology is adequate for all of family 0xf and
    148 	 * for revisions A to D of family 0x10 (for the list of models
    149 	 * in each revision, refer to usr/src/uts/i86pc/os/cpuid_subr.c).
    150 	 * We cover all family 0x10 models, till model 9.
    151 	 */
    152 	if (family > 0x10 || (family == 0x10 && model > 9))
    153 		return (1);
    154 
    155 	if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0,
    156 	    MAX_CHANNUM) < 0) {
    157 		whinge(mod, NULL, "amd_generic_mc_create: range create for "
    158 		    "channels failed\n");
    159 		return (-1);
    160 	}
    161 
    162 	for (chan = 0; chan <= MAX_CHANNUM; chan++) {
    163 		tnode_t *chnode;
    164 		nvlist_t *fmri;
    165 		int err;
    166 
    167 		if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth,
    168 		    &fmri) != 0) {
    169 			whinge(mod, NULL, "amd_generic_mc_create: mkrsrc "
    170 			    "failed\n");
    171 			return (-1);
    172 		}
    173 
    174 		if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME,
    175 		    chan, fmri)) == NULL) {
    176 			nvlist_free(fmri);
    177 			whinge(mod, NULL, "amd_generic_mc_create: node "
    178 			    "bind failed\n");
    179 			return (-1);
    180 		}
    181 
    182 		nvlist_free(fmri);
    183 
    184 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
    185 
    186 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
    187 		    TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err);
    188 
    189 		if (FM_AWARE_SMBIOS(mod)) {
    190 			if (topo_node_label_set(chnode, NULL, &err) == -1)
    191 				whinge(mod, NULL, "amd_generic_mc_create: "
    192 				    "topo_node_label_set\n");
    193 			if (topo_node_fru_set(chnode, NULL, 0, &err) != 0)
    194 				whinge(mod, NULL, "amd_generic_mc_create: "
    195 				    "topo_node_fru_set failed\n");
    196 		}
    197 
    198 		if (topo_node_range_create(mod, chnode, CS_NODE_NAME,
    199 		    0, MAX_CSNUM) < 0) {
    200 			whinge(mod, NULL, "amd_generic_mc_create: "
    201 			    "range create for cs failed\n");
    202 			return (-1);
    203 		}
    204 
    205 		for (cs = 0; cs <= MAX_CSNUM; cs++) {
    206 			tnode_t *csnode;
    207 
    208 			if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth,
    209 			    &fmri) != 0) {
    210 				whinge(mod, NULL, "amd_generic_mc_create: "
    211 				    "mkrsrc for cs failed\n");
    212 				return (-1);
    213 			}
    214 
    215 			if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME,
    216 			    cs, fmri)) == NULL) {
    217 				nvlist_free(fmri);
    218 				whinge(mod, NULL, "amd_generic_mc_create: "
    219 				    "bind for cs failed\n");
    220 				return (-1);
    221 			}
    222 
    223 			/*
    224 			 * Dynamic ASRU for page faults within a chip-select.
    225 			 * The topology does not represent pages (there are
    226 			 * too many) so when a page is faulted we generate
    227 			 * an ASRU to represent the individual page.
    228 			 * If SMBIOS meets FMA needs, derive labels & serials
    229 			 * for DIMMS and apply to chip-select nodes.
    230 			 * If deriving from SMBIOS, skip IPMI
    231 			 */
    232 			if (FM_AWARE_SMBIOS(mod)) {
    233 				if (topo_method_register(mod, csnode,
    234 				    x86pi_gen_cs_methods) < 0)
    235 					whinge(mod, NULL,
    236 					    "amd_generic_mc_create: "
    237 					    "method registration failed\n");
    238 			} else {
    239 				if (topo_method_register(mod, csnode,
    240 				    gen_cs_methods) < 0)
    241 					whinge(mod, NULL,
    242 					    "amd_generic_mc_create: method"
    243 					    "registration failed\n");
    244 			}
    245 
    246 			(void) topo_node_asru_set(csnode, fmri,
    247 			    TOPO_ASRU_COMPUTE, &err);
    248 			nvlist_free(fmri);
    249 
    250 			/*
    251 			 * If SMBIOS meets FMA needs, set DIMM as the FRU for
    252 			 * the chip-select node. Use the channel & chip-select
    253 			 * numbers to get the DIMM instance.
    254 			 * Send via inst : dram channel number
    255 			 * Receive via inst : dimm instance
    256 			 */
    257 			if (FM_AWARE_SMBIOS(mod)) {
    258 				int inst;
    259 				id_t dimm_smbid;
    260 				const char *serial;
    261 				const char *part;
    262 				const char *rev;
    263 				char *label;
    264 
    265 				(void) topo_pgroup_create(csnode,
    266 				    &cs_pgroup, &err);
    267 				inst = chan;
    268 				dimm_smbid = memnode_to_smbiosid(smbid,
    269 				    CS_NODE_NAME, cs, &inst);
    270 				serial = chip_serial_smbios_get(mod,
    271 				    dimm_smbid);
    272 				part = chip_part_smbios_get(mod,
    273 				    dimm_smbid);
    274 				rev = chip_rev_smbios_get(mod, dimm_smbid);
    275 				label = (char *)chip_label_smbios_get(mod,
    276 				    chnode, dimm_smbid, NULL);
    277 
    278 				(void) topo_prop_set_string(csnode, PGNAME(CS),
    279 				    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
    280 				    serial, &err);
    281 				(void) topo_prop_set_string(csnode, PGNAME(CS),
    282 				    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
    283 				    part, &err);
    284 				(void) topo_prop_set_string(csnode, PGNAME(CS),
    285 				    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
    286 				    rev, &err);
    287 
    288 				/*
    289 				 * We apply DIMM labels to chip-select nodes,
    290 				 * FRU for chip-selects should be DIMMs, and
    291 				 * we do not derive dimm nodes for Family 0x10
    292 				 * so FRU fmri is NULL, but FRU Labels are set,
    293 				 * the FRU labels point to the DIMM.
    294 				 */
    295 				(void) topo_node_label_set(csnode, label, &err);
    296 				topo_mod_strfree(mod, label);
    297 			}
    298 		}
    299 	}
    300 
    301 	return (0);
    302 }
    303 
    304 static nvlist_t *
    305 amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
    306 {
    307 	mc_snapshot_info_t mcs;
    308 	void *buf = NULL;
    309 	uint8_t ver;
    310 
    311 	nvlist_t *nvl = NULL;
    312 	char path[64];
    313 	int fd, err;
    314 
    315 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
    316 	fd = open(path, O_RDONLY);
    317 
    318 	if (fd == -1) {
    319 		/*
    320 		 * Some v20z and v40z systems may have had the 3rd-party
    321 		 * NWSnps packagae installed which installs a /dev/mc
    322 		 * link.  So try again via /devices.
    323 		 */
    324 		(void) snprintf(path, sizeof (path),
    325 		    "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
    326 		    MC_AMD_DEV_OFFSET + id);
    327 		fd = open(path, O_RDONLY);
    328 	}
    329 
    330 	if (fd == -1)
    331 		return (NULL);	/* do not whinge */
    332 
    333 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
    334 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
    335 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
    336 
    337 		whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
    338 		    path, strerror(errno));
    339 
    340 		free(buf);
    341 		(void) close(fd);
    342 		return (NULL);
    343 	}
    344 
    345 	(void) close(fd);
    346 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
    347 	topo_mod_free(mod, buf, mcs.mcs_size);
    348 
    349 	if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
    350 		whinge(mod, NULL, "mc nvlist is not versioned\n");
    351 		nvlist_free(nvl);
    352 		return (NULL);
    353 	} else if (ver != MC_NVLIST_VERS1) {
    354 		whinge(mod, NULL, "mc nvlist version mismatch\n");
    355 		nvlist_free(nvl);
    356 		return (NULL);
    357 	}
    358 
    359 	return (err ? NULL : nvl);
    360 }
    361 
    362 int
    363 amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl,
    364     nvlist_t *auth)
    365 {
    366 	uint64_t *csnumarr;
    367 	char **csnamearr;
    368 	uint_t ncs, ncsname;
    369 	tnode_t *ranknode;
    370 	nvlist_t *fmri, *pfmri = NULL;
    371 	uint64_t dsz, rsz;
    372 	int nerr = 0;
    373 	int err;
    374 	int i;
    375 
    376 	if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
    377 	    &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
    378 	    &csnamearr, &ncsname) != 0 || ncs != ncsname) {
    379 		whinge(mod, &nerr, "amd_rank_create: "
    380 		    "csnums/csnames extraction failed\n");
    381 		return (nerr);
    382 	}
    383 
    384 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
    385 		whinge(mod, &nerr, "amd_rank_create: parent fmri lookup "
    386 		    "failed\n");
    387 		return (nerr);
    388 	}
    389 
    390 	if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
    391 		whinge(mod, &nerr, "amd_rank_create: range create failed\n");
    392 		nvlist_free(pfmri);
    393 		return (nerr);
    394 	}
    395 
    396 	if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
    397 	    &err) == 0) {
    398 		rsz = dsz / ncs;
    399 	} else {
    400 		whinge(mod, &nerr, "amd_rank_create: parent dimm has no "
    401 		    "size\n");
    402 		return (nerr);
    403 	}
    404 
    405 	for (i = 0; i < ncs; i++) {
    406 		if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
    407 			whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n");
    408 			continue;
    409 		}
    410 
    411 		if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
    412 		    fmri)) == NULL) {
    413 			nvlist_free(fmri);
    414 			whinge(mod, &nerr, "amd_rank_create: node bind "
    415 			    "failed\n");
    416 			continue;
    417 		}
    418 
    419 		nvlist_free(fmri);
    420 		if (FM_AWARE_SMBIOS(mod))
    421 			(void) topo_node_fru_set(ranknode, NULL, 0, &err);
    422 		else
    423 			(void) topo_node_fru_set(ranknode, pfmri, 0, &err);
    424 
    425 		/*
    426 		 * If a rank is faulted the asru is the associated
    427 		 * chip-select, but if a page within a rank is faulted
    428 		 * the asru is just that page.  Hence the dual preconstructed
    429 		 * and computed ASRU.
    430 		 */
    431 		if (topo_method_register(mod, ranknode, rank_methods) < 0)
    432 			whinge(mod, &nerr, "amd_rank_create: "
    433 			    "topo_method_register failed");
    434 
    435 		if (! is_xpv() && topo_method_register(mod, ranknode,
    436 		    ntv_page_retire_methods) < 0)
    437 			whinge(mod, &nerr, "amd_rank_create: "
    438 			    "topo_method_register failed");
    439 
    440 		(void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
    441 		    TOPO_ASRU_COMPUTE, &err);
    442 
    443 		(void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
    444 
    445 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
    446 		    TOPO_PROP_IMMUTABLE, rsz, &err);
    447 
    448 		(void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
    449 		    TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
    450 
    451 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
    452 		    TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
    453 	}
    454 
    455 	nvlist_free(pfmri);
    456 
    457 	return (nerr);
    458 }
    459 
    460 static int
    461 amd_dimm_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
    462     const char *name, nvlist_t *mc, nvlist_t *auth)
    463 {
    464 	int i, err, nerr = 0;
    465 	int perr = 0;
    466 	nvpair_t *nvp;
    467 	tnode_t *dimmnode;
    468 	nvlist_t *fmri, **dimmarr = NULL;
    469 	uint64_t num;
    470 	uint_t ndimm;
    471 	id_t smbid;
    472 	const char *serial;
    473 	const char *part;
    474 	const char *rev;
    475 
    476 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
    477 		whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n");
    478 		return (-1);
    479 	}
    480 
    481 	if (ndimm == 0)
    482 		return (0);	/* no dimms present on this node */
    483 
    484 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
    485 		whinge(mod, NULL, "amd_dimm_create: range create failed\n");
    486 		return (-1);
    487 	}
    488 
    489 	for (i = 0; i < ndimm; i++) {
    490 		if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
    491 			whinge(mod, &nerr, "amd_dimm_create: dimm num property "
    492 			    "missing\n");
    493 			continue;
    494 		}
    495 
    496 		if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
    497 			whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n");
    498 			continue;
    499 		}
    500 		if (FM_AWARE_SMBIOS(mod)) {
    501 			smbid = memnode_to_smbiosid(chip_smbid, DIMM_NODE_NAME,
    502 			    i, NULL);
    503 			serial = chip_serial_smbios_get(mod, smbid);
    504 			part = chip_part_smbios_get(mod, smbid);
    505 			rev = chip_rev_smbios_get(mod, smbid);
    506 			perr += nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID,
    507 			    serial);
    508 			perr += nvlist_add_string(fmri, FM_FMRI_HC_PART,
    509 			    part);
    510 			perr += nvlist_add_string(fmri, FM_FMRI_HC_REVISION,
    511 			    rev);
    512 
    513 			if (perr != 0)
    514 				whinge(mod, NULL, "amd_dimm_create:"
    515 				    "nvlist_add_string failed\n");
    516 		}
    517 
    518 		if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
    519 		    == NULL) {
    520 			nvlist_free(fmri);
    521 			whinge(mod, &nerr, "amd_dimm_create: node bind "
    522 			    "failed\n");
    523 			continue;
    524 		}
    525 
    526 		if (!FM_AWARE_SMBIOS(mod))
    527 			if (topo_method_register(mod,
    528 			    dimmnode, dimm_methods) < 0)
    529 				whinge(mod, &nerr, "amd_dimm_create: "
    530 				    "topo_method_register failed");
    531 
    532 		(void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
    533 
    534 		if (FM_AWARE_SMBIOS(mod)) {
    535 			char *label;
    536 
    537 			nvlist_free(fmri);
    538 			(void) topo_node_resource(dimmnode,
    539 			    &fmri, &err);
    540 
    541 			label = (char *)chip_label_smbios_get(mod,
    542 			    pnode, smbid, NULL);
    543 			if (topo_node_label_set(dimmnode, label,
    544 			    &perr) == -1)
    545 				topo_mod_dprintf(mod, "Failed"
    546 				    "to set label\n");
    547 			topo_mod_strfree(mod, label);
    548 
    549 			(void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
    550 			    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
    551 			    serial, &err);
    552 			(void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
    553 			    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
    554 			    part, &err);
    555 			(void) topo_prop_set_string(dimmnode, PGNAME(DIMM),
    556 			    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
    557 			    rev, &err);
    558 		}
    559 
    560 		(void) topo_node_asru_set(dimmnode, fmri, 0, &err);
    561 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
    562 		nvlist_free(fmri);
    563 
    564 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
    565 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
    566 			if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
    567 			    strcmp(nvpair_name(nvp), "csnums") == 0 ||
    568 			    nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
    569 			    strcmp(nvpair_name(nvp), "csnames") == 0)
    570 				continue;	/* used in amd_rank_create() */
    571 
    572 			nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
    573 		}
    574 
    575 		nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth);
    576 	}
    577 
    578 	return (nerr == 0 ? 0 : -1);
    579 }
    580 
    581 static int
    582 amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
    583     nvlist_t *auth)
    584 {
    585 	int i, err, nerr = 0;
    586 	nvpair_t *nvp;
    587 	tnode_t *csnode;
    588 	nvlist_t *fmri, **csarr = NULL;
    589 	uint64_t csnum;
    590 	uint_t ncs;
    591 
    592 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
    593 		return (-1);
    594 
    595 	if (ncs == 0)
    596 		return (0);	/* no chip-selects configured on this node */
    597 
    598 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
    599 		return (-1);
    600 
    601 	for (i = 0; i < ncs; i++) {
    602 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
    603 			whinge(mod, &nerr, "amd_cs_create: cs num property "
    604 			    "missing\n");
    605 			continue;
    606 		}
    607 
    608 		if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
    609 			whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n");
    610 			continue;
    611 		}
    612 
    613 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
    614 		    == NULL) {
    615 			nvlist_free(fmri);
    616 			whinge(mod, &nerr, "amd_cs_create: node bind failed\n");
    617 			continue;
    618 		}
    619 
    620 		cs_fmri[csnum] = fmri;	/* nvlist will be freed in mc_create */
    621 
    622 		(void) topo_node_asru_set(csnode, fmri, 0, &err);
    623 
    624 		(void) topo_node_fru_set(csnode, fmri, 0, &err);
    625 
    626 		(void) topo_pgroup_create(csnode, &cs_pgroup, &err);
    627 
    628 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
    629 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
    630 			nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
    631 		}
    632 	}
    633 
    634 	return (nerr == 0 ? 0 : -1);
    635 }
    636 
    637 static int
    638 amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
    639     nvlist_t *auth)
    640 {
    641 	tnode_t *chnode;
    642 	nvlist_t *fmri;
    643 	char *socket;
    644 	int i, nchan;
    645 	nvlist_t *pfmri = NULL;
    646 	int err, nerr = 0;
    647 
    648 	/*
    649 	 * We will enumerate the number of channels present even if only
    650 	 * channel A is in use (i.e., running in 64-bit mode).  Only
    651 	 * the socket 754 package has a single channel.
    652 	 */
    653 	if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
    654 	    &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0)
    655 		nchan = 1;
    656 	else
    657 		nchan = 2;
    658 
    659 	topo_mod_strfree(mod, socket);
    660 
    661 	if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
    662 		return (-1);
    663 
    664 	(void) topo_node_fru(pnode, &pfmri, NULL, &err);
    665 
    666 	for (i = 0; i < nchan; i++) {
    667 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
    668 			whinge(mod, &nerr, "amd_dramchan_create: mkrsrc "
    669 			    "failed\n");
    670 			continue;
    671 		}
    672 
    673 		if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
    674 		    == NULL) {
    675 			nvlist_free(fmri);
    676 			whinge(mod, &nerr, "amd_dramchan_create: node bind "
    677 			    "failed\n");
    678 			continue;
    679 		}
    680 
    681 		(void) topo_node_asru_set(chnode, fmri, 0, &err);
    682 		if (pfmri)
    683 			(void) topo_node_fru_set(chnode, pfmri, 0, &err);
    684 
    685 		nvlist_free(fmri);
    686 
    687 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
    688 
    689 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
    690 		    TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
    691 	}
    692 	if (pfmri)
    693 		nvlist_free(pfmri);
    694 
    695 	return (nerr == 0 ? 0 : -1);
    696 }
    697 
    698 static int
    699 amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
    700 {
    701 	nvpair_t *nvp;
    702 	int nerr = 0;
    703 
    704 	if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
    705 		whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
    706 		return (-1);
    707 	}
    708 
    709 	for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
    710 	    nvp = nvlist_next_nvpair(htnvl, nvp)) {
    711 		if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
    712 			nerr++;
    713 	}
    714 
    715 	return (nerr == 0 ? 0 : -1);
    716 }
    717 
    718 void
    719 amd_mc_create(topo_mod_t *mod,  uint16_t smbid, tnode_t *pnode,
    720     const char *name, nvlist_t *auth, int32_t procnodeid,
    721     int32_t procnodes_per_pkg, int family,
    722     int model, int *nerrp)
    723 {
    724 	tnode_t *mcnode;
    725 	nvlist_t *rfmri, *fmri;
    726 	nvpair_t *nvp;
    727 	nvlist_t *mc = NULL;
    728 	int i, err;
    729 	int mcnum = procnodeid % procnodes_per_pkg;
    730 	char *serial = NULL;
    731 	char *part = NULL;
    732 	char *rev = NULL;
    733 
    734 	/*
    735 	 * Return with no error for anything before AMD family 0xf - we
    736 	 * won't generate even a generic memory topology for earlier
    737 	 * families.
    738 	 */
    739 	if (family < 0xf)
    740 		return;
    741 
    742 	if (topo_node_lookup(pnode, name, mcnum) != NULL)
    743 		return;
    744 
    745 	if (FM_AWARE_SMBIOS(mod)) {
    746 		(void) topo_node_resource(pnode, &rfmri, &err);
    747 		(void) nvlist_lookup_string(rfmri, "serial", &serial);
    748 		(void) nvlist_lookup_string(rfmri, "part", &part);
    749 		(void) nvlist_lookup_string(rfmri, "revision", &rev);
    750 	}
    751 
    752 	if (mkrsrc(mod, pnode, name, mcnum, auth, &fmri) != 0) {
    753 		if (FM_AWARE_SMBIOS(mod))
    754 			nvlist_free(rfmri);
    755 		whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
    756 		return;
    757 	}
    758 
    759 	if (FM_AWARE_SMBIOS(mod)) {
    760 		(void) nvlist_add_string(fmri, "serial", serial);
    761 		(void) nvlist_add_string(fmri, "part", part);
    762 		(void) nvlist_add_string(fmri, "revision", rev);
    763 		nvlist_free(rfmri);
    764 	}
    765 
    766 	if ((mcnode = topo_node_bind(mod, pnode, name, mcnum,
    767 	    fmri)) == NULL) {
    768 		nvlist_free(fmri);
    769 		whinge(mod, nerrp, "mc_create: mc bind failed\n");
    770 		return;
    771 	}
    772 	if (topo_node_fru_set(mcnode, NULL, 0, &err) < 0)
    773 		whinge(mod, nerrp, "mc_create: topo_node_fru_set failed\n");
    774 
    775 	if (FM_AWARE_SMBIOS(mod)) {
    776 		if (topo_node_label_set(mcnode, NULL, &err) == -1)
    777 			topo_mod_dprintf(mod, "Failed to set label\n");
    778 	}
    779 
    780 	nvlist_free(fmri);
    781 
    782 	if (topo_pgroup_create(mcnode, &mc_pgroup, &err) < 0)
    783 		whinge(mod, nerrp, "mc_create: topo_pgroup_create failed\n");
    784 
    785 	if (topo_prop_set_int32(mcnode, PGNAME(MCT), MCT_PROCNODE_ID,
    786 	    TOPO_PROP_IMMUTABLE, procnodeid, nerrp) != 0)
    787 		whinge(mod, nerrp, "mc_create: topo_prop_set_int32 failed to"
    788 		    "add node id\n");
    789 
    790 	if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
    791 		/*
    792 		 * If a memory-controller driver exists for this chip model
    793 		 * it has not attached or has otherwise malfunctioned;
    794 		 * alternatively no memory-controller driver exists for this
    795 		 * (presumably newly-released) cpu model.  We fallback to
    796 		 * creating a generic maximal topology.
    797 		 */
    798 		if (amd_generic_mc_create(mod, smbid, pnode, mcnode,
    799 		    family, model, auth) != 0)
    800 			whinge(mod, nerrp,
    801 			    "mc_create: amd_generic_mc_create failed\n");
    802 		return;
    803 	}
    804 
    805 	/*
    806 	 * Add memory controller properties
    807 	 */
    808 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
    809 	    nvp = nvlist_next_nvpair(mc, nvp)) {
    810 		char *name = nvpair_name(nvp);
    811 		data_type_t type = nvpair_type(nvp);
    812 
    813 		if (type == DATA_TYPE_NVLIST_ARRAY &&
    814 		    (strcmp(name, "cslist") == 0 ||
    815 		    strcmp(name, "dimmlist") == 0)) {
    816 			continue;
    817 		} else if (type == DATA_TYPE_UINT8 &&
    818 		    strcmp(name, MC_NVLIST_VERSTR) == 0) {
    819 			continue;
    820 		} else if (type == DATA_TYPE_NVLIST &&
    821 		    strcmp(name, "htconfig") == 0) {
    822 			nvlist_t *htnvl;
    823 
    824 			(void) nvpair_value_nvlist(nvp, &htnvl);
    825 			if (amd_htconfig(mod, pnode, htnvl) != 0)
    826 				whinge(mod, nerrp,
    827 				    "mc_create: amd_htconfig failed\n");
    828 		} else {
    829 			if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
    830 				whinge(mod, nerrp,
    831 				    "mc_create: nvprop_add failed\n");
    832 		}
    833 	}
    834 
    835 	if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
    836 	    amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
    837 	    amd_dimm_create(mod, smbid, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
    838 		whinge(mod, nerrp, "mc_create: create children failed\n");
    839 
    840 	/*
    841 	 * Free the fmris for the chip-selects allocated in amd_cs_create
    842 	 */
    843 	for (i = 0; i < MC_CHIP_NCS; i++) {
    844 		if (cs_fmri[i] != NULL) {
    845 			nvlist_free(cs_fmri[i]);
    846 			cs_fmri[i] = NULL;
    847 		}
    848 	}
    849 
    850 	nvlist_free(mc);
    851 }
    852