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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <limits.h>
36 #include <alloca.h>
37 #include <kstat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <libnvpair.h>
41 #include <sys/types.h>
42 #include <sys/bitmap.h>
43 #include <sys/processor.h>
44 #include <sys/param.h>
45 #include <sys/fm/protocol.h>
46 #include <sys/systeminfo.h>
47 #include <sys/mc.h>
48 #include <sys/mc_amd.h>
49 #include <fm/topo_mod.h>
50 
51 #include "chip.h"
52 
53 #ifndef MAX
54 #define	MAX(a, b)	((a) > (b) ? (a) : (b))
55 #endif
56 
57 #define	MAX_DIMMNUM	7
58 #define	MAX_CSNUM	7
59 
60 /*
61  * Enumerates the processing chips, or sockets, (as distinct from cores) in a
62  * system.  For each chip found, the necessary nodes (one or more cores, and
63  * possibly a memory controller) are constructed underneath.
64  */
65 
66 static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
67     topo_instance_t, void *);
68 
69 static int mem_asru_compute(topo_mod_t *, tnode_t *, topo_version_t,
70     nvlist_t *, nvlist_t **);
71 
72 const topo_modinfo_t chip_info =
73 	{ "chip", CHIP_VERSION, chip_enum, NULL};
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 	{ NULL }
80 };
81 
82 static const struct debugopt {
83 	const char *optname;
84 	int optval;
85 } debugopts[] = {
86 	{ "err", TOPO_DBG_ERR },
87 	{ "mod", TOPO_DBG_MOD },
88 	{ "log", TOPO_DBG_LOG },
89 	{ "walk", TOPO_DBG_WALK },
90 	{ "tree", TOPO_DBG_TREE },
91 	{ "all", TOPO_DBG_ALL }
92 };
93 
94 static nvlist_t *cs_fmri[MC_CHIP_NCS];
95 
96 static void
97 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
98 {
99 	va_list ap;
100 	char buf[160];
101 
102 	if (nerr != NULL)
103 		++*nerr;
104 
105 	va_start(ap, fmt);
106 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
107 	va_end(ap);
108 
109 	topo_mod_dprintf(mod, "%s", buf);
110 }
111 
112 int
113 _topo_init(topo_mod_t *mod)
114 {
115 	const char *debugstr = getenv("TOPOCHPDBG");
116 	chip_t *chip;
117 	int i;
118 
119 	if (debugstr != NULL) {
120 		for (i = 0; i < sizeof (debugopts) / sizeof (struct debugopt);
121 		    i++) {
122 			if (strncmp(debugstr, debugopts[i].optname, 4) == 0) {
123 				topo_mod_clrdebug(mod);
124 				topo_mod_setdebug(mod, debugopts[i].optval);
125 				break;	/* handle a single option only */
126 			}
127 		}
128 	}
129 
130 	topo_mod_dprintf(mod, "initializing chip enumerator\n");
131 
132 	if ((chip = topo_mod_zalloc(mod, sizeof (chip_t))) == NULL)
133 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
134 
135 	if ((chip->chip_kc = kstat_open()) == NULL) {
136 		whinge(mod, NULL, "kstat_open failed: %s\n",
137 		    strerror(errno));
138 		topo_mod_free(mod, chip, sizeof (chip_t));
139 		return (topo_mod_seterrno(mod, errno));
140 	}
141 
142 	chip->chip_ncpustats = sysconf(_SC_CPUID_MAX);
143 	if ((chip->chip_cpustats = topo_mod_zalloc(mod, (
144 	    chip->chip_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
145 		(void) kstat_close(chip->chip_kc);
146 		topo_mod_free(mod, chip, sizeof (chip_t));
147 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
148 	}
149 
150 	if (topo_mod_register(mod, &chip_info, (void *)chip) != 0) {
151 		whinge(mod, NULL, "failed to register hc: "
152 		    "%s\n", topo_mod_errmsg(mod));
153 		topo_mod_free(mod, chip->chip_cpustats,
154 		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
155 		(void) kstat_close(chip->chip_kc);
156 		topo_mod_free(mod, chip, sizeof (chip_t));
157 		return (-1); /* mod errno set */
158 	}
159 
160 	return (0);
161 }
162 
163 void
164 _topo_fini(topo_mod_t *mod)
165 {
166 	chip_t *chip = topo_mod_private(mod);
167 
168 	if (chip->chip_cpustats != NULL)
169 		topo_mod_free(mod, chip->chip_cpustats,
170 		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
171 
172 	(void) kstat_close(chip->chip_kc);
173 	topo_mod_free(mod, chip, sizeof (chip_t));
174 
175 	topo_mod_unregister(mod);
176 }
177 
178 static int
179 chip_strprop(tnode_t *cnode, kstat_t *ksp, const char *name)
180 {
181 	int err;
182 	kstat_named_t *k;
183 
184 	if ((k = kstat_data_lookup(ksp, (char *)name)) == NULL)
185 		return (0);
186 
187 	(void) topo_prop_set_string(cnode, CHIP_PGROUP, name,
188 	    TOPO_PROP_SET_ONCE, k->value.str.addr.ptr, &err);
189 
190 	return (-1);
191 }
192 
193 static int
194 chip_longprop(tnode_t *cnode, kstat_t *ksp, const char *name)
195 {
196 	int err;
197 	kstat_named_t *k;
198 
199 	if ((k = kstat_data_lookup(ksp, (char *)name)) == NULL)
200 		return (0);
201 
202 	(void) topo_prop_set_int32(cnode, CHIP_PGROUP, name, TOPO_PROP_SET_ONCE,
203 	    k->value.l, &err);
204 
205 	return (-1);
206 }
207 
208 static int
209 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst,
210     nvlist_t **nvl)
211 {
212 	nvlist_t *args = NULL, *pfmri = NULL;
213 	topo_hdl_t *thp = topo_mod_handle(mod);
214 	int err;
215 
216 	if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
217 	    topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
218 	    nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
219 		nvlist_free(pfmri);
220 		nvlist_free(args);
221 		return (-1);
222 	}
223 
224 	*nvl = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name, inst, args, &err);
225 	nvlist_free(pfmri);
226 	nvlist_free(args);
227 
228 	return (nvl != NULL ? 0 : -1);	/* caller must free nvlist */
229 }
230 
231 static nvlist_t *
232 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
233 {
234 	int err;
235 	nvlist_t *asru;
236 
237 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
238 		return (NULL);
239 
240 	err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
241 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
242 	err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
243 	err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
244 	if (s != NULL)
245 		err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
246 	if (err != 0) {
247 		nvlist_free(asru);
248 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
249 		return (NULL);
250 	}
251 
252 	return (asru);
253 }
254 
255 static nvlist_t *
256 mem_fmri_create(topo_mod_t *mod)
257 {
258 	nvlist_t *asru;
259 
260 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
261 		return (NULL);
262 
263 	if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
264 	    nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) {
265 		nvlist_free(asru);
266 		return (NULL);
267 	}
268 
269 	return (asru);
270 }
271 
272 static int
273 cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid,
274     chip_t *chip)
275 {
276 	kstat_named_t *k;
277 	nvlist_t *fmri, *asru;
278 	tnode_t *cnode;
279 	int err, nerr = 0;
280 	int clogid, cpuid;
281 
282 	if (topo_node_range_create(mod, pnode, name, 0,
283 	    chip->chip_ncpustats) < 0)
284 		return (-1);
285 
286 	for (cpuid = 0; cpuid <= chip->chip_ncpustats; cpuid++) {
287 		if (chip->chip_cpustats[cpuid] == NULL)
288 			continue;
289 
290 		/*
291 		 * The chip_id in the cpu_info kstat numbers the individual
292 		 * chips from 0 to #chips - 1.
293 		 */
294 		if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid],
295 		    "chip_id")) == NULL) {
296 			whinge(mod, &nerr, "cpu_create: chip_id lookup via "
297 			    "kstats failed\n");
298 			continue;
299 		}
300 
301 		if (k->value.l != chipid)
302 			continue;	/* not an error */
303 
304 		/*
305 		 * The clog_id in the cpu_info kstat numbers the virtual
306 		 * processors of a single chip;  these may be separate
307 		 * processor cores, or they may be hardware threads/strands
308 		 * of individual cores.
309 		 *
310 		 * The core_id in the cpu_info kstat tells us which cpus
311 		 * share the same core - i.e., are hardware strands of the
312 		 * same core.  This enumerator does not distinguish stranded
313 		 * cores so core_id is unused.
314 		 */
315 		if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid],
316 		    "clog_id")) == NULL) {
317 			whinge(mod, &nerr, "cpu_create: clog_id lookup via "
318 			    "kstats failed\n");
319 			continue;
320 		}
321 		clogid = k->value.l;
322 
323 		if (mkrsrc(mod, pnode, name, clogid, &fmri) != 0) {
324 			whinge(mod, &nerr, "cpu_create: mkrsrc failed\n");
325 			continue;
326 		}
327 
328 		if ((cnode = topo_node_bind(mod, pnode, name, clogid, fmri,
329 		    NULL)) == NULL) {
330 			whinge(mod, &nerr, "cpu_create: node bind failed\n");
331 			nvlist_free(fmri);
332 			continue;
333 		}
334 		nvlist_free(fmri);
335 
336 		if ((asru = cpu_fmri_create(mod, cpuid, NULL, 0)) != NULL) {
337 			(void) topo_node_asru_set(cnode, asru, 0, &err);
338 			nvlist_free(asru);
339 		} else {
340 			whinge(mod, &nerr, "cpu_create: cpu_fmri_create "
341 			    "failed\n");
342 		}
343 		(void) topo_node_fru_set(cnode, NULL, 0, &err);
344 	}
345 
346 	return (nerr == 0 ? 0 : -1);
347 }
348 
349 static int
350 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
351 {
352 	int err = 0;
353 	char *pname = nvpair_name(nvp);
354 
355 	switch (nvpair_type(nvp)) {
356 	case DATA_TYPE_BOOLEAN_VALUE: {
357 		boolean_t val;
358 
359 		if (nvpair_value_boolean_value(nvp, &val) == 0) {
360 			(void) topo_prop_set_string(node, pgname, pname,
361 			    TOPO_PROP_SET_ONCE, (val ? "true" : "false"), &err);
362 		}
363 		return (0);
364 	}
365 
366 	case DATA_TYPE_UINT64: {
367 		uint64_t val;
368 
369 		if (nvpair_value_uint64(nvp, &val) == 0) {
370 			(void) topo_prop_set_uint64(node, pgname, pname,
371 			    TOPO_PROP_SET_ONCE, val, &err);
372 		}
373 		return (0);
374 	}
375 
376 	case DATA_TYPE_STRING: {
377 		char *str;
378 
379 		if (nvpair_value_string(nvp, &str) == 0)
380 			(void) topo_prop_set_string(node, pgname, pname,
381 			    TOPO_PROP_SET_ONCE, str, &err);
382 		return (0);
383 	}
384 
385 	default:
386 		whinge(mod, &err, "nvprop_add: Can't handle type %d for "
387 		    "'%s' in property group %s of %s node\n",
388 		    nvpair_type(nvp), pname, pgname, topo_node_name(node));
389 		return (1);
390 	}
391 }
392 
393 static int
394 dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
395 {
396 	tnode_t *chnode;
397 	nvlist_t *fmri;
398 	char *socket;
399 	int i, nchan;
400 	int err, nerr = 0;
401 
402 	/*
403 	 * We will enumerate the number of channels present even if only
404 	 * channel A is in use (i.e., running in 64-bit mode).  Only
405 	 * the socket 754 package has a single channel.
406 	 */
407 	if (topo_prop_get_string(pnode, MCT_PGROUP, "socket",
408 	    &socket, &err) != 0)
409 		return (-1);
410 
411 	if (strcmp(socket, "Socket 754") == 0)
412 		nchan = 1;
413 	else
414 		nchan = 2;
415 
416 	topo_mod_strfree(mod, socket);
417 
418 	if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
419 		return (-1);
420 
421 	for (i = 0; i < nchan; i++) {
422 		if (mkrsrc(mod, pnode, name, i, &fmri) != 0) {
423 			whinge(mod, &nerr, "dramchan_create: mkrsrc "
424 			    "failed\n");
425 			continue;
426 		}
427 
428 		if ((chnode = topo_node_bind(mod, pnode, name, i, fmri,
429 		    NULL)) == NULL) {
430 			nvlist_free(fmri);
431 			whinge(mod, &nerr, "dramchan_create: node bind "
432 			    "failed\n");
433 			continue;
434 		}
435 
436 		nvlist_free(fmri);
437 
438 		(void) topo_pgroup_create(chnode, CHAN_PGROUP,
439 		    TOPO_STABILITY_PRIVATE, &err);
440 
441 		(void) topo_prop_set_string(chnode, CHAN_PGROUP, "channel",
442 		    TOPO_PROP_SET_ONCE, i == 0 ? "A" : "B", &err);
443 	}
444 
445 	return (nerr == 0 ? 0 : -1);
446 }
447 
448 static int
449 cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
450 {
451 	int i, err, nerr = 0;
452 	nvpair_t *nvp;
453 	tnode_t *csnode;
454 	nvlist_t *fmri, **csarr = NULL;
455 	uint64_t csnum;
456 	uint_t ncs;
457 
458 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
459 		return (-1);
460 
461 	if (ncs == 0)
462 		return (0);	/* no chip-selects configured on this node */
463 
464 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
465 		return (-1);
466 
467 	for (i = 0; i < ncs; i++) {
468 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
469 			whinge(mod, &nerr, "cs_create: cs num property "
470 			    "missing\n");
471 			continue;
472 		}
473 
474 		if (mkrsrc(mod, pnode, name, csnum, &fmri) != 0) {
475 			whinge(mod, &nerr, "cs_create: mkrsrc failed\n");
476 			continue;
477 		}
478 
479 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri,
480 		    NULL)) == NULL) {
481 			nvlist_free(fmri);
482 			whinge(mod, &nerr, "cs_create: node bind failed\n");
483 			continue;
484 		}
485 
486 		cs_fmri[csnum] = fmri;	/* nvlist will be freed in mc_create */
487 
488 		(void) topo_node_asru_set(csnode, fmri, 0, &err);
489 
490 		(void) topo_pgroup_create(csnode, CS_PGROUP,
491 		    TOPO_STABILITY_PRIVATE, &err);
492 
493 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
494 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
495 			nerr += nvprop_add(mod, nvp, CS_PGROUP, csnode);
496 		}
497 	}
498 
499 	return (nerr == 0 ? 0 : -1);
500 }
501 
502 /*
503  * Registered method for asru computation for rank nodes.  The 'node'
504  * argument identifies the node for which we seek an asru.  The 'in'
505  * argument is used to select which asru we will return, as follows:
506  *
507  * - the node name must be "dimm" or "rank"
508  * - if 'in' is NULL then return any statically defined asru for this node
509  * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru
510  *   with unum being the hc path to the dimm or rank (this method is called
511  *   as part of dynamic asru computation for rank nodes only, but dimm_create
512  *   also calls it directly to construct a "mem" scheme asru for a dimm node)
513  * - if 'in' in addition includes an hc-specific member which specifies
514  *   asru-physaddr or asru-offset then these are includes in the "mem" scheme
515  *   asru as additional membersl physaddr and offset
516  */
517 /*ARGSUSED*/
518 static int
519 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
520     nvlist_t *in, nvlist_t **out)
521 {
522 	int incl_pa = 0, incl_offset = 0;
523 	nvlist_t *hcsp, *asru;
524 	uint64_t pa, offset;
525 	char *scheme, *unum;
526 	int err;
527 
528 	if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
529 	    strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0)
530 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
531 
532 	if (in == NULL) {
533 		if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL,
534 		    TOPO_PROP_ASRU, out, &err) == 0)
535 			return (0);
536 		else
537 			return (topo_mod_seterrno(mod, err));
538 	} else {
539 		if (nvlist_lookup_string(in, FM_FMRI_SCHEME, &scheme) != 0 ||
540 		    strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
541 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
542 	}
543 
544 	if (nvlist_lookup_nvlist(in, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
545 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR,
546 		    &pa) == 0)
547 			incl_pa = 1;
548 
549 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET,
550 		    &offset) == 0)
551 			incl_offset = 1;
552 	}
553 
554 	/* use 'in' to obtain resource path;  could use node resource */
555 	if (topo_fmri_nvl2str(topo_mod_handle(mod), in, &unum, &err) < 0)
556 		return (topo_mod_seterrno(mod, err));
557 
558 	if ((asru = mem_fmri_create(mod)) == NULL) {
559 		topo_mod_strfree(mod, unum);
560 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
561 	}
562 
563 	err = nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum);
564 	if (incl_pa)
565 		err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa);
566 	if (incl_offset)
567 		err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset);
568 
569 	topo_mod_strfree(mod, unum);
570 	if (err != 0) {
571 		nvlist_free(asru);
572 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
573 	}
574 
575 	*out = asru;
576 	return (0);
577 }
578 
579 static int
580 rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl)
581 {
582 	uint64_t *csnumarr;
583 	char **csnamearr;
584 	uint_t ncs, ncsname;
585 	tnode_t *ranknode;
586 	nvlist_t *fmri, *pfmri = NULL;
587 	uint64_t dsz, rsz;
588 	int nerr = 0;
589 	int err;
590 	int i;
591 
592 	if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
593 	    &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
594 	    &csnamearr, &ncsname) != 0 || ncs != ncsname) {
595 		whinge(mod, &nerr, "rank_create: "
596 		    "csnums/csnames extraction failed\n");
597 		    return (nerr);
598 	}
599 
600 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
601 		whinge(mod, &nerr, "rank_create: parent fmri lookup "
602 		    "failed\n");
603 		return (nerr);
604 	}
605 
606 	if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
607 		whinge(mod, &nerr, "rank_create: range create failed\n");
608 		nvlist_free(pfmri);
609 		return (nerr);
610 	}
611 
612 	if (topo_prop_get_uint64(pnode, DIMM_PGROUP, "size", &dsz, &err) == 0) {
613 		rsz = dsz / ncs;
614 	} else {
615 		whinge(mod, &nerr, "rank_create: parent dimm has no size\n");
616 		return (nerr);
617 	}
618 
619 	for (i = 0; i < ncs; i++) {
620 		if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, &fmri) < 0) {
621 			whinge(mod, &nerr, "rank_create: mkrsrc failed\n");
622 			continue;
623 		}
624 
625 		if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
626 		    fmri, NULL)) == NULL) {
627 			nvlist_free(fmri);
628 			whinge(mod, &nerr, "rank_create: node bind "
629 			    "failed\n");
630 			continue;
631 		}
632 
633 		nvlist_free(fmri);
634 
635 		(void) topo_node_fru_set(ranknode, pfmri, 0, &err);
636 
637 		/*
638 		 * If a rank is faulted the asru is the associated
639 		 * chip-select, but if a page within a rank is faulted
640 		 * the asru is just that page.  Hence the dual preconstructed
641 		 * and computed ASRU.
642 		 */
643 		(void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
644 			    TOPO_ASRU_COMPUTE, &err);
645 
646 		if (topo_method_register(mod, ranknode, rank_methods) < 0)
647 			whinge(mod, &nerr, "rank_create: "
648 			    "topo_method_register failed");
649 
650 		(void) topo_pgroup_create(ranknode, RANK_PGROUP,
651 		    TOPO_STABILITY_PRIVATE, &err);
652 
653 		(void) topo_prop_set_uint64(ranknode, RANK_PGROUP, "size",
654 		    TOPO_PROP_SET_ONCE, rsz, &err);
655 
656 		(void) topo_prop_set_string(ranknode, RANK_PGROUP, "csname",
657 		    TOPO_PROP_SET_ONCE, csnamearr[i], &err);
658 
659 		(void) topo_prop_set_uint64(ranknode, RANK_PGROUP, "csnum",
660 		    TOPO_PROP_SET_ONCE, csnumarr[i], &err);
661 	}
662 
663 	nvlist_free(pfmri);
664 
665 	return (nerr);
666 }
667 
668 static int
669 dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
670 {
671 	int i, err, nerr = 0;
672 	nvpair_t *nvp;
673 	tnode_t *dimmnode;
674 	nvlist_t *fmri, *asru, **dimmarr = NULL;
675 	uint64_t num;
676 	uint_t ndimm;
677 
678 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
679 		whinge(mod, NULL, "dimm_create: dimmlist lookup failed\n");
680 		return (-1);
681 	}
682 
683 	if (ndimm == 0)
684 		return (0);	/* no dimms present on this node */
685 
686 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
687 		whinge(mod, NULL, "dimm_create: range create failed\n");
688 		return (-1);
689 	}
690 
691 	for (i = 0; i < ndimm; i++) {
692 		if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
693 			whinge(mod, &nerr, "dimm_create: dimm num property "
694 			    "missing\n");
695 			continue;
696 		}
697 
698 		if (mkrsrc(mod, pnode, name, num, &fmri) < 0) {
699 			whinge(mod, &nerr, "dimm_create: mkrsrc failed\n");
700 			continue;
701 		}
702 
703 		if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri,
704 		    NULL)) == NULL) {
705 			nvlist_free(fmri);
706 			whinge(mod, &nerr, "dimm_create: node bind "
707 			    "failed\n");
708 			continue;
709 		}
710 
711 		/*
712 		 * The asru is static but we prefer to publish it in the
713 		 * "mem" scheme so call the compute method directly to
714 		 * perform the conversion.
715 		 */
716 		if (mem_asru_compute(mod, dimmnode,
717 		    TOPO_METH_ASRU_COMPUTE_VERSION, fmri, &asru) == 0) {
718 			(void) topo_node_asru_set(dimmnode, asru, 0, &err);
719 			nvlist_free(asru);
720 		} else {
721 
722 			nvlist_free(fmri);
723 			whinge(mod, &nerr, "dimm_create: mem_asru_compute "
724 			    "failed\n");
725 			continue;
726 		}
727 
728 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
729 
730 		nvlist_free(fmri);
731 
732 		(void) topo_pgroup_create(dimmnode, DIMM_PGROUP,
733 		    TOPO_STABILITY_PRIVATE, &err);
734 
735 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
736 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
737 			if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
738 			    strcmp(nvpair_name(nvp), "csnums") == 0 ||
739 			    nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
740 			    strcmp(nvpair_name(nvp), "csnames") == 0)
741 				continue;	/* used in rank_create() */
742 
743 			nerr += nvprop_add(mod, nvp, DIMM_PGROUP, dimmnode);
744 		}
745 
746 		nerr += rank_create(mod, dimmnode, dimmarr[i]);
747 	}
748 
749 	return (nerr == 0 ? 0 : -1);
750 }
751 
752 static nvlist_t *
753 mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
754 {
755 	mc_snapshot_info_t mcs;
756 	void *buf = NULL;
757 	uint8_t ver;
758 
759 	nvlist_t *nvl;
760 	char path[64];
761 	int fd, err;
762 
763 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
764 	fd = open(path, O_RDONLY);
765 
766 	if (fd == -1) {
767 		/*
768 		 * Some v20z and v40z systems may have had the 3rd-party
769 		 * NWSnps packagae installed which installs a /dev/mc
770 		 * link.  So try again via /devices.
771 		 */
772 		(void) snprintf(path, sizeof (path),
773 		    "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
774 		    MC_AMD_DEV_OFFSET + id);
775 		fd = open(path, O_RDONLY);
776 	}
777 
778 	if (fd == -1) {
779 		whinge(mod, NULL, "mc failed to open %s: %s\n",
780 		    path, strerror(errno));
781 		return (NULL);
782 	}
783 
784 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
785 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
786 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
787 
788 		whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
789 		    path, strerror(errno));
790 
791 		free(buf);
792 		(void) close(fd);
793 		return (NULL);
794 	}
795 
796 	(void) close(fd);
797 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
798 	topo_mod_free(mod, buf, mcs.mcs_size);
799 
800 	if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
801 		whinge(mod, NULL, "mc nvlist is not versioned\n");
802 		nvlist_free(nvl);
803 		return (NULL);
804 	} else if (ver != MC_NVLIST_VERS1) {
805 		whinge(mod, NULL, "mc nvlist version mismatch\n");
806 		nvlist_free(nvl);
807 		return (NULL);
808 	}
809 
810 	return (err ? NULL : nvl);
811 }
812 
813 static int
814 mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
815 {
816 	int err, rc = 0;
817 	tnode_t *mcnode;
818 	nvlist_t *fmri;
819 	nvpair_t *nvp;
820 	nvlist_t *mc = NULL;
821 	int i;
822 
823 	if (mkrsrc(mod, pnode, name, 0, &fmri) != 0) {
824 		whinge(mod, NULL, "mc_create: mkrsrc failed\n");
825 		return (-1);
826 	}
827 
828 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
829 		nvlist_free(fmri);
830 		whinge(mod, NULL, "mc_create: node range create failed\n");
831 		return (-1);
832 	}
833 
834 	/*
835 	 * Gather and create memory controller topology
836 	 */
837 	if ((mc = mc_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL ||
838 	    (mcnode = topo_node_bind(mod, pnode,
839 	    name, 0, fmri, NULL)) == NULL) {
840 		if (mc != NULL)
841 			nvlist_free(mc);
842 		topo_node_range_destroy(pnode, name);
843 		nvlist_free(fmri);
844 		whinge(mod, NULL, "mc_create: mc lookup or bind failed\n");
845 		return (-1);
846 	}
847 
848 	(void) topo_node_fru_set(mcnode, NULL, 0, &err);
849 	nvlist_free(fmri);
850 
851 	/*
852 	 * Add memory controller properties
853 	 */
854 	(void) topo_pgroup_create(mcnode, MCT_PGROUP,
855 	    TOPO_STABILITY_PRIVATE, &err);
856 
857 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
858 	    nvp = nvlist_next_nvpair(mc, nvp)) {
859 		if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY &&
860 		    (strcmp(nvpair_name(nvp), "cslist") == 0 ||
861 		    strcmp(nvpair_name(nvp), "dimmlist") == 0)) {
862 			continue;
863 		} else if (nvpair_type(nvp) == DATA_TYPE_UINT8 &&
864 		    strcmp(nvpair_name(nvp), MC_NVLIST_VERSTR) == 0) {
865 			continue;
866 		} else {
867 			if (nvprop_add(mod, nvp, MCT_PGROUP, mcnode) != 0)
868 				rc = -1;
869 		}
870 	}
871 
872 	if (dramchan_create(mod, mcnode, CHAN_NODE_NAME) != 0 ||
873 	    cs_create(mod, mcnode, CS_NODE_NAME, mc) != 0 ||
874 	    dimm_create(mod, mcnode, DIMM_NODE_NAME, mc) != 0)
875 		rc = -1;
876 
877 	/*
878 	 * Free the fmris for the chip-selects allocated in cs_create
879 	 */
880 	for (i = 0; i < MC_CHIP_NCS; i++) {
881 		if (cs_fmri[i] != NULL) {
882 			nvlist_free(cs_fmri[i]);
883 			cs_fmri[i] = NULL;
884 		}
885 	}
886 
887 	nvlist_free(mc);
888 	return (rc);
889 }
890 
891 static int
892 chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
893     topo_instance_t min, topo_instance_t max, chip_t *chip)
894 {
895 	int i, nerr = 0;
896 	kstat_t *ksp;
897 	ulong_t *chipmap;
898 	tnode_t *cnode;
899 	nvlist_t *fmri;
900 
901 	if ((chipmap = topo_mod_zalloc(mod, BT_BITOUL(max) *
902 	    sizeof (ulong_t))) == NULL)
903 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
904 
905 	/*
906 	 * Read in all cpu_info kstats, for all chip ids.  The ks_instance
907 	 * argument to kstat_lookup is the logical cpu_id - we will use this
908 	 * in cpu_create.
909 	 */
910 	for (i = 0; i <= chip->chip_ncpustats; i++) {
911 		if ((ksp = kstat_lookup(chip->chip_kc, "cpu_info", i, NULL)) ==
912 		    NULL || kstat_read(chip->chip_kc, ksp, NULL) < 0)
913 			continue;
914 
915 		chip->chip_cpustats[i] = ksp;
916 	}
917 
918 	for (i = 0; i <= chip->chip_ncpustats; i++) {
919 		kstat_named_t *k;
920 		int err, chipid;
921 
922 		if ((ksp = chip->chip_cpustats[i]) == NULL)
923 			continue;
924 
925 		if ((k = kstat_data_lookup(ksp, "chip_id")) == NULL) {
926 			whinge(mod, &nerr, "chip_create: chip_id lookup "
927 			    "via kstats failed\n");
928 			continue;
929 		}
930 
931 		chipid = k->value.l;
932 		if (BT_TEST(chipmap, chipid))
933 			continue;
934 
935 		if (chipid < min || chipid > max)
936 			continue;
937 
938 		if (mkrsrc(mod, pnode, name, chipid, &fmri) != 0) {
939 			whinge(mod, &nerr, "chip_create: mkrsrc failed\n");
940 			continue;
941 		}
942 
943 		if ((cnode = topo_node_bind(mod, pnode, name, chipid, fmri,
944 		    NULL)) == NULL) {
945 			nvlist_free(fmri);
946 			whinge(mod, &nerr, "chip_create: node bind "
947 			    "failed for chipid %d\n", chipid);
948 			continue;
949 		}
950 		BT_SET(chipmap, chipid);
951 
952 		(void) topo_node_fru_set(cnode, fmri, 0, &err);
953 
954 		nvlist_free(fmri);
955 
956 		(void) topo_pgroup_create(cnode, CHIP_PGROUP,
957 		    TOPO_STABILITY_PRIVATE, &err);
958 		(void) chip_strprop(cnode, ksp, CHIP_VENDOR_ID);
959 		(void) chip_longprop(cnode, ksp, CHIP_FAMILY);
960 		(void) chip_longprop(cnode, ksp, CHIP_MODEL);
961 		(void) chip_longprop(cnode, ksp, CHIP_STEPPING);
962 
963 		if (cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip) != 0 ||
964 		    mc_create(mod, cnode, MCT_NODE_NAME) != 0)
965 			nerr++;		/* have whinged elsewhere */
966 	}
967 
968 	topo_mod_free(mod, chipmap, BT_BITOUL(max) * sizeof (ulong_t));
969 
970 	if (nerr == 0) {
971 		return (0);
972 	} else {
973 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
974 		return (-1);
975 	}
976 }
977 
978 static int
979 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
980     topo_instance_t min, topo_instance_t max, void *arg)
981 {
982 	chip_t *chip = (chip_t *)arg;
983 
984 	if (strcmp(name, "chip") == 0)
985 		return (chip_create(mod, pnode, name, min, max, chip));
986 
987 	return (0);
988 }
989