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