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