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 static const topo_method_t gen_cs_methods[] = {
102 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
103 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
104 	    mem_asru_compute },
105 	{ SIMPLE_CS_LBL_MP, "Property method", 0,
106 	    TOPO_STABILITY_INTERNAL, simple_cs_label_mp},
107 	{ GET_CS_SERIAL, "Property method", 0,
108 	    TOPO_STABILITY_INTERNAL, get_cs_serial},
109 	{ NULL }
110 };
111 
112 static nvlist_t *cs_fmri[MC_CHIP_NCS];
113 
114 /*
115  * Called when there is no memory-controller driver to provide topology
116  * information.  Generate a maximal memory topology that is appropriate
117  * for the chip revision.  The memory-controller node has already been
118  * bound as mcnode, and the parent of that is cnode.
119  *
120  * We create a tree of dram-channel and chip-select nodes below the
121  * memory-controller node.  There will be two dram channels and 8 chip-selects
122  * below each, regardless of actual socket type, processor revision and so on.
123  * This is adequate for generic diagnosis up to family 0x10 revision C.
124  * When support for revision D is implemented (or maybe C) we should take
125  * the opportunity to rework the topology tree completely (socket change will
126  * mean there can be no diagnosis history tied to the topology).
127  */
128 /*ARGSUSED*/
129 static int
130 amd_generic_mc_create(topo_mod_t *mod, tnode_t *cnode, tnode_t *mcnode,
131     int family, int model, int stepping, nvlist_t *auth)
132 {
133 	int chan, cs;
134 
135 	/*
136 	 * Elsewhere we have already returned for families less than 0xf.
137 	 * This "generic" topology is adequate for all of family 0xf and
138 	 * for revisions A, B and C of family 0x10 (for the list of models
139 	 * in each revision, refer to usr/src/uts/i86pc/os/cpuid_subr.c).
140 	 * We cover all family 0x10 models, till model 6.
141 	 */
142 	if (family > 0x10 || (family == 0x10 && model > 6))
143 		return (1);
144 
145 	if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0,
146 	    MAX_CHANNUM) < 0) {
147 		whinge(mod, NULL, "amd_generic_mc_create: range create for "
148 		    "channels failed\n");
149 		return (-1);
150 	}
151 
152 	for (chan = 0; chan <= MAX_CHANNUM; chan++) {
153 		tnode_t *chnode;
154 		nvlist_t *fmri;
155 		int err;
156 
157 		if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth,
158 		    &fmri) != 0) {
159 			whinge(mod, NULL, "amd_generic_mc_create: mkrsrc "
160 			    "failed\n");
161 			return (-1);
162 		}
163 
164 		if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME,
165 		    chan, fmri)) == NULL) {
166 			nvlist_free(fmri);
167 			whinge(mod, NULL, "amd_generic_mc_create: node "
168 			    "bind failed\n");
169 			return (-1);
170 		}
171 
172 		nvlist_free(fmri);
173 
174 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
175 
176 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
177 		    TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err);
178 
179 		if (topo_node_range_create(mod, chnode, CS_NODE_NAME,
180 		    0, MAX_CSNUM) < 0) {
181 			whinge(mod, NULL, "amd_generic_mc_create: "
182 			    "range create for cs failed\n");
183 			return (-1);
184 		}
185 
186 		for (cs = 0; cs <= MAX_CSNUM; cs++) {
187 			tnode_t *csnode;
188 
189 			if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth,
190 			    &fmri) != 0) {
191 				whinge(mod, NULL, "amd_generic_mc_create: "
192 				    "mkrsrc for cs failed\n");
193 				return (-1);
194 			}
195 
196 			if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME,
197 			    cs, fmri)) == NULL) {
198 				nvlist_free(fmri);
199 				whinge(mod, NULL, "amd_generic_mc_create: "
200 				    "bind for cs failed\n");
201 				return (-1);
202 			}
203 
204 			/*
205 			 * Dynamic ASRU for page faults within a chip-select.
206 			 * The topology does not represent pages (there are
207 			 * too many) so when a page is faulted we generate
208 			 * an ASRU to represent the individual page.
209 			 */
210 			if (topo_method_register(mod, csnode,
211 			    gen_cs_methods) < 0)
212 				whinge(mod, NULL, "amd_generic_mc_create: "
213 				    "method registration failed\n");
214 
215 			(void) topo_node_asru_set(csnode, fmri,
216 			    TOPO_ASRU_COMPUTE, &err);
217 
218 			nvlist_free(fmri);
219 		}
220 	}
221 
222 	return (0);
223 }
224 
225 static nvlist_t *
226 amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
227 {
228 	mc_snapshot_info_t mcs;
229 	void *buf = NULL;
230 	uint8_t ver;
231 
232 	nvlist_t *nvl = NULL;
233 	char path[64];
234 	int fd, err;
235 
236 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
237 	fd = open(path, O_RDONLY);
238 
239 	if (fd == -1) {
240 		/*
241 		 * Some v20z and v40z systems may have had the 3rd-party
242 		 * NWSnps packagae installed which installs a /dev/mc
243 		 * link.  So try again via /devices.
244 		 */
245 		(void) snprintf(path, sizeof (path),
246 		    "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
247 		    MC_AMD_DEV_OFFSET + id);
248 		fd = open(path, O_RDONLY);
249 	}
250 
251 	if (fd == -1)
252 		return (NULL);	/* do not whinge */
253 
254 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
255 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
256 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
257 
258 		whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
259 		    path, strerror(errno));
260 
261 		free(buf);
262 		(void) close(fd);
263 		return (NULL);
264 	}
265 
266 	(void) close(fd);
267 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
268 	topo_mod_free(mod, buf, mcs.mcs_size);
269 
270 
271 	if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
272 		whinge(mod, NULL, "mc nvlist is not versioned\n");
273 		nvlist_free(nvl);
274 		return (NULL);
275 	} else if (ver != MC_NVLIST_VERS1) {
276 		whinge(mod, NULL, "mc nvlist version mismatch\n");
277 		nvlist_free(nvl);
278 		return (NULL);
279 	}
280 
281 	return (err ? NULL : nvl);
282 }
283 
284 int
285 amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl,
286     nvlist_t *auth)
287 {
288 	uint64_t *csnumarr;
289 	char **csnamearr;
290 	uint_t ncs, ncsname;
291 	tnode_t *ranknode;
292 	nvlist_t *fmri, *pfmri = NULL;
293 	uint64_t dsz, rsz;
294 	int nerr = 0;
295 	int err;
296 	int i;
297 
298 	if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
299 	    &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
300 	    &csnamearr, &ncsname) != 0 || ncs != ncsname) {
301 		whinge(mod, &nerr, "amd_rank_create: "
302 		    "csnums/csnames extraction failed\n");
303 		return (nerr);
304 	}
305 
306 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
307 		whinge(mod, &nerr, "amd_rank_create: parent fmri lookup "
308 		    "failed\n");
309 		return (nerr);
310 	}
311 
312 	if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
313 		whinge(mod, &nerr, "amd_rank_create: range create failed\n");
314 		nvlist_free(pfmri);
315 		return (nerr);
316 	}
317 
318 	if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
319 	    &err) == 0) {
320 		rsz = dsz / ncs;
321 	} else {
322 		whinge(mod, &nerr, "amd_rank_create: parent dimm has no "
323 		    "size\n");
324 		return (nerr);
325 	}
326 
327 	for (i = 0; i < ncs; i++) {
328 		if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
329 			whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n");
330 			continue;
331 		}
332 
333 		if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
334 		    fmri)) == NULL) {
335 			nvlist_free(fmri);
336 			whinge(mod, &nerr, "amd_rank_create: node bind "
337 			    "failed\n");
338 			continue;
339 		}
340 
341 		nvlist_free(fmri);
342 
343 		(void) topo_node_fru_set(ranknode, pfmri, 0, &err);
344 
345 		/*
346 		 * If a rank is faulted the asru is the associated
347 		 * chip-select, but if a page within a rank is faulted
348 		 * the asru is just that page.  Hence the dual preconstructed
349 		 * and computed ASRU.
350 		 */
351 		if (topo_method_register(mod, ranknode, rank_methods) < 0)
352 			whinge(mod, &nerr, "amd_rank_create: "
353 			    "topo_method_register failed");
354 
355 		if (! is_xpv() && topo_method_register(mod, ranknode,
356 		    ntv_page_retire_methods) < 0)
357 			whinge(mod, &nerr, "amd_rank_create: "
358 			    "topo_method_register failed");
359 
360 		(void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
361 		    TOPO_ASRU_COMPUTE, &err);
362 
363 		(void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
364 
365 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
366 		    TOPO_PROP_IMMUTABLE, rsz, &err);
367 
368 		(void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
369 		    TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
370 
371 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
372 		    TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
373 	}
374 
375 	nvlist_free(pfmri);
376 
377 	return (nerr);
378 }
379 
380 static int
381 amd_dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
382     nvlist_t *mc, nvlist_t *auth)
383 {
384 	int i, err, nerr = 0;
385 	nvpair_t *nvp;
386 	tnode_t *dimmnode;
387 	nvlist_t *fmri, **dimmarr = NULL;
388 	uint64_t num;
389 	uint_t ndimm;
390 
391 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
392 		whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n");
393 		return (-1);
394 	}
395 
396 	if (ndimm == 0)
397 		return (0);	/* no dimms present on this node */
398 
399 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
400 		whinge(mod, NULL, "amd_dimm_create: range create failed\n");
401 		return (-1);
402 	}
403 
404 	for (i = 0; i < ndimm; i++) {
405 		if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
406 			whinge(mod, &nerr, "amd_dimm_create: dimm num property "
407 			    "missing\n");
408 			continue;
409 		}
410 
411 		if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
412 			whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n");
413 			continue;
414 		}
415 
416 		if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
417 		    == NULL) {
418 			nvlist_free(fmri);
419 			whinge(mod, &nerr, "amd_dimm_create: node bind "
420 			    "failed\n");
421 			continue;
422 		}
423 
424 		if (topo_method_register(mod, dimmnode, dimm_methods) < 0)
425 			whinge(mod, &nerr, "amd_dimm_create: "
426 			    "topo_method_register failed");
427 
428 		(void) topo_node_asru_set(dimmnode, fmri, 0, &err);
429 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
430 
431 		nvlist_free(fmri);
432 
433 		(void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
434 
435 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
436 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
437 			if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
438 			    strcmp(nvpair_name(nvp), "csnums") == 0 ||
439 			    nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
440 			    strcmp(nvpair_name(nvp), "csnames") == 0)
441 				continue;	/* used in amd_rank_create() */
442 
443 			nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
444 		}
445 
446 		nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth);
447 	}
448 
449 	return (nerr == 0 ? 0 : -1);
450 }
451 
452 static int
453 amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
454     nvlist_t *auth)
455 {
456 	int i, err, nerr = 0;
457 	nvpair_t *nvp;
458 	tnode_t *csnode;
459 	nvlist_t *fmri, **csarr = NULL;
460 	uint64_t csnum;
461 	uint_t ncs;
462 
463 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
464 		return (-1);
465 
466 	if (ncs == 0)
467 		return (0);	/* no chip-selects configured on this node */
468 
469 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
470 		return (-1);
471 
472 	for (i = 0; i < ncs; i++) {
473 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
474 			whinge(mod, &nerr, "amd_cs_create: cs num property "
475 			    "missing\n");
476 			continue;
477 		}
478 
479 		if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
480 			whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n");
481 			continue;
482 		}
483 
484 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
485 		    == NULL) {
486 			nvlist_free(fmri);
487 			whinge(mod, &nerr, "amd_cs_create: node bind failed\n");
488 			continue;
489 		}
490 
491 		cs_fmri[csnum] = fmri;	/* nvlist will be freed in mc_create */
492 
493 		(void) topo_node_asru_set(csnode, fmri, 0, &err);
494 
495 		(void) topo_node_fru_set(csnode, fmri, 0, &err);
496 
497 		(void) topo_pgroup_create(csnode, &cs_pgroup, &err);
498 
499 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
500 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
501 			nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
502 		}
503 	}
504 
505 	return (nerr == 0 ? 0 : -1);
506 }
507 
508 static int
509 amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
510     nvlist_t *auth)
511 {
512 	tnode_t *chnode;
513 	nvlist_t *fmri;
514 	char *socket;
515 	int i, nchan;
516 	nvlist_t *pfmri = NULL;
517 	int err, nerr = 0;
518 
519 	/*
520 	 * We will enumerate the number of channels present even if only
521 	 * channel A is in use (i.e., running in 64-bit mode).  Only
522 	 * the socket 754 package has a single channel.
523 	 */
524 	if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
525 	    &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0)
526 		nchan = 1;
527 	else
528 		nchan = 2;
529 
530 	topo_mod_strfree(mod, socket);
531 
532 	if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
533 		return (-1);
534 
535 	(void) topo_node_fru(pnode, &pfmri, NULL, &err);
536 
537 	for (i = 0; i < nchan; i++) {
538 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
539 			whinge(mod, &nerr, "amd_dramchan_create: mkrsrc "
540 			    "failed\n");
541 			continue;
542 		}
543 
544 		if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
545 		    == NULL) {
546 			nvlist_free(fmri);
547 			whinge(mod, &nerr, "amd_dramchan_create: node bind "
548 			    "failed\n");
549 			continue;
550 		}
551 
552 		(void) topo_node_asru_set(chnode, fmri, 0, &err);
553 		if (pfmri)
554 			(void) topo_node_fru_set(chnode, pfmri, 0, &err);
555 
556 		nvlist_free(fmri);
557 
558 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
559 
560 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
561 		    TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
562 	}
563 	if (pfmri)
564 		nvlist_free(pfmri);
565 
566 	return (nerr == 0 ? 0 : -1);
567 }
568 
569 static int
570 amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
571 {
572 	nvpair_t *nvp;
573 	int nerr = 0;
574 
575 	if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
576 		whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
577 		return (-1);
578 	}
579 
580 	for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
581 	    nvp = nvlist_next_nvpair(htnvl, nvp)) {
582 		if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
583 			nerr++;
584 	}
585 
586 	return (nerr == 0 ? 0 : -1);
587 }
588 
589 void
590 amd_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
591     int family, int model, int stepping, int *nerrp)
592 {
593 	tnode_t *mcnode;
594 	nvlist_t *fmri;
595 	nvpair_t *nvp;
596 	nvlist_t *mc = NULL;
597 	int i, err;
598 
599 	/*
600 	 * Return with no error for anything before AMD family 0xf - we
601 	 * won't generate even a generic memory topolofy for earlier
602 	 * families.
603 	 */
604 	if (family < 0xf)
605 		return;
606 
607 	if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) {
608 		whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
609 		return;
610 	}
611 
612 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
613 		nvlist_free(fmri);
614 		whinge(mod, nerrp, "mc_create: node range create failed\n");
615 		return;
616 	}
617 
618 	if ((mcnode = topo_node_bind(mod, pnode, name, 0,
619 	    fmri)) == NULL) {
620 		nvlist_free(mc);
621 		topo_node_range_destroy(pnode, name);
622 		nvlist_free(fmri);
623 		whinge(mod, nerrp, "mc_create: mc bind failed\n");
624 		return;
625 	}
626 	if (topo_node_fru_set(mcnode, NULL, 0, &err) < 0)
627 		whinge(mod, nerrp, "mc_create: topo_node_fru_set failed\n");
628 
629 	nvlist_free(fmri);
630 
631 	if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
632 		/*
633 		 * If a memory-controller driver exists for this chip model
634 		 * it has not attached or has otherwise malfunctioned;
635 		 * alternatively no memory-controller driver exists for this
636 		 * (presumably newly-released) cpu model.  We fallback to
637 		 * creating a generic maximal topology.
638 		 */
639 		if (amd_generic_mc_create(mod, pnode, mcnode,
640 		    family, model, stepping, auth) != 0)
641 			whinge(mod, nerrp,
642 			    "mc_create: amd_generic_mc_create failed\n");
643 		return;
644 	}
645 
646 	/*
647 	 * Add memory controller properties
648 	 */
649 	if (topo_pgroup_create(mcnode, &mc_pgroup, &err) < 0)
650 		whinge(mod, nerrp, "mc_create: topo_pgroup_create failed\n");
651 
652 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
653 	    nvp = nvlist_next_nvpair(mc, nvp)) {
654 		char *name = nvpair_name(nvp);
655 		data_type_t type = nvpair_type(nvp);
656 
657 		if (type == DATA_TYPE_NVLIST_ARRAY &&
658 		    (strcmp(name, "cslist") == 0 ||
659 		    strcmp(name, "dimmlist") == 0)) {
660 			continue;
661 		} else if (type == DATA_TYPE_UINT8 &&
662 		    strcmp(name, MC_NVLIST_VERSTR) == 0) {
663 			continue;
664 		} else if (type == DATA_TYPE_NVLIST &&
665 		    strcmp(name, "htconfig") == 0) {
666 			nvlist_t *htnvl;
667 
668 			(void) nvpair_value_nvlist(nvp, &htnvl);
669 			if (amd_htconfig(mod, pnode, htnvl) != 0)
670 				whinge(mod, nerrp,
671 				    "mc_create: amd_htconfig failed\n");
672 		} else {
673 			if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
674 				whinge(mod, nerrp,
675 				    "mc_create: nvprop_add failed\n");
676 		}
677 	}
678 
679 	if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
680 	    amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
681 	    amd_dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
682 		whinge(mod, nerrp, "mc_create: create children failed\n");
683 
684 	/*
685 	 * Free the fmris for the chip-selects allocated in amd_cs_create
686 	 */
687 	for (i = 0; i < MC_CHIP_NCS; i++) {
688 		if (cs_fmri[i] != NULL) {
689 			nvlist_free(cs_fmri[i]);
690 			cs_fmri[i] = NULL;
691 		}
692 	}
693 
694 	nvlist_free(mc);
695 }
696