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
47static const topo_pgroup_info_t cs_pgroup =
48	{ PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
49static const topo_pgroup_info_t dimm_pgroup =
50	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
51static const topo_pgroup_info_t mc_pgroup =
52	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
53static const topo_pgroup_info_t rank_pgroup =
54	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
55static const topo_pgroup_info_t chan_pgroup =
56	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
57
58static 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
74const 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
87const 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 */
106static 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
113static 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
124static 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*/
138static int
139amd_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
303static nvlist_t *
304amd_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
361int
362amd_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
459static int
460amd_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
580static int
581amd_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
636static int
637amd_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
696static int
697amd_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
716void
717amd_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