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 *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, &fmri, &err);
747 		(void) nvlist_lookup_string(fmri, "serial", &serial);
748 		(void) nvlist_lookup_string(fmri, "part", &part);
749 		(void) nvlist_lookup_string(fmri, "revision", &rev);
750 		nvlist_free(fmri);
751 	}
752 
753 	if (mkrsrc(mod, pnode, name, mcnum, auth, &fmri) != 0) {
754 		whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
755 		return;
756 	}
757 
758 	if (FM_AWARE_SMBIOS(mod)) {
759 		(void) nvlist_add_string(fmri, "serial", serial);
760 		(void) nvlist_add_string(fmri, "part", part);
761 		(void) nvlist_add_string(fmri, "revision", rev);
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