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 2007 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 *, void *);
68 
69 static int mem_asru_compute(topo_mod_t *, tnode_t *, topo_version_t,
70     nvlist_t *, nvlist_t **);
71 
72 static const topo_modops_t chip_ops =
73 	{ chip_enum, NULL};
74 static const topo_modinfo_t chip_info =
75 	{ CHIP_NODE_NAME, FM_FMRI_SCHEME_HC, CHIP_VERSION, &chip_ops };
76 
77 static const topo_pgroup_info_t cs_pgroup =
78 	{ PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
79 static const topo_pgroup_info_t dimm_pgroup =
80 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
81 static const topo_pgroup_info_t mc_pgroup =
82 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
83 static const topo_pgroup_info_t chip_pgroup =
84 	{ PGNAME(CHIP), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
85 static const topo_pgroup_info_t cpu_pgroup =
86 	{ PGNAME(CPU), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
87 static const topo_pgroup_info_t rank_pgroup =
88 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
89 static const topo_pgroup_info_t chan_pgroup =
90 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
91 
92 const topo_method_t rank_methods[] = {
93 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
94 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
95 	    mem_asru_compute },
96 	{ NULL }
97 };
98 
99 const topo_method_t dimm_methods[] = {
100 	{ SIMPLE_DIMM_LBL, "Property method", 0,
101 	    TOPO_STABILITY_INTERNAL, simple_dimm_label},
102 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0,
103 	    TOPO_STABILITY_INTERNAL, simple_dimm_label_mp},
104 	{ SEQ_DIMM_LBL, "Property method", 0,
105 	    TOPO_STABILITY_INTERNAL, seq_dimm_label},
106 	{ NULL }
107 };
108 
109 const topo_method_t chip_methods[] = {
110 	{ SIMPLE_CHIP_LBL, "Property method", 0,
111 	    TOPO_STABILITY_INTERNAL, simple_chip_label},
112 	{ G4_CHIP_LBL, "Property method", 0,
113 	    TOPO_STABILITY_INTERNAL, g4_chip_label},
114 	{ NULL }
115 };
116 
117 static nvlist_t *cs_fmri[MC_CHIP_NCS];
118 
119 static void
120 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
121 {
122 	va_list ap;
123 	char buf[160];
124 
125 	if (nerr != NULL)
126 		++*nerr;
127 
128 	va_start(ap, fmt);
129 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
130 	va_end(ap);
131 
132 	topo_mod_dprintf(mod, "%s", buf);
133 }
134 
135 int
136 _topo_init(topo_mod_t *mod)
137 {
138 	chip_t *chip;
139 
140 	if (getenv("TOPOCHIPDBG"))
141 		topo_mod_setdebug(mod);
142 	topo_mod_dprintf(mod, "initializing chip enumerator\n");
143 
144 	if ((chip = topo_mod_zalloc(mod, sizeof (chip_t))) == NULL)
145 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
146 
147 	if ((chip->chip_kc = kstat_open()) == NULL) {
148 		whinge(mod, NULL, "kstat_open failed: %s\n",
149 		    strerror(errno));
150 		topo_mod_free(mod, chip, sizeof (chip_t));
151 		return (topo_mod_seterrno(mod, errno));
152 	}
153 
154 	chip->chip_ncpustats = sysconf(_SC_CPUID_MAX);
155 	if ((chip->chip_cpustats = topo_mod_zalloc(mod, (
156 	    chip->chip_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
157 		(void) kstat_close(chip->chip_kc);
158 		topo_mod_free(mod, chip, sizeof (chip_t));
159 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
160 	}
161 
162 	if (topo_mod_register(mod, &chip_info, TOPO_VERSION) != 0) {
163 		whinge(mod, NULL, "failed to register hc: "
164 		    "%s\n", topo_mod_errmsg(mod));
165 		topo_mod_free(mod, chip->chip_cpustats,
166 		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
167 		(void) kstat_close(chip->chip_kc);
168 		topo_mod_free(mod, chip, sizeof (chip_t));
169 		return (-1); /* mod errno set */
170 	}
171 	topo_mod_setspecific(mod, (void *)chip);
172 
173 	return (0);
174 }
175 
176 void
177 _topo_fini(topo_mod_t *mod)
178 {
179 	chip_t *chip = topo_mod_getspecific(mod);
180 
181 	if (chip->chip_cpustats != NULL)
182 		topo_mod_free(mod, chip->chip_cpustats,
183 		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
184 
185 	(void) kstat_close(chip->chip_kc);
186 	topo_mod_free(mod, chip, sizeof (chip_t));
187 
188 	topo_mod_unregister(mod);
189 }
190 
191 static int
192 add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
193     const char *pgname, const char *pname)
194 {
195 	int err = 0;
196 	kstat_named_t *k;
197 
198 	if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
199 		return (-1);
200 
201 	if (topo_prop_set_string(node, pgname, pname,
202 	    TOPO_PROP_IMMUTABLE, k->value.str.addr.ptr, &err) == 0) {
203 		return (0);
204 	} else {
205 		whinge(mod, &err, "chip_strprop: failed to add '%s'\n",
206 		    pname);
207 		return (-1);
208 	}
209 }
210 
211 static int
212 add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
213     const char *pgname, const char *pname)
214 {
215 	int err;
216 	kstat_named_t *k;
217 
218 	if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
219 		return (-1);
220 
221 	if (topo_prop_set_int32(node, pgname, pname,
222 	    TOPO_PROP_IMMUTABLE, k->value.l, &err) == 0) {
223 		return (0);
224 	} else {
225 		whinge(mod, &err, "chip_longprop: failed to add '%s'\n",
226 		    pname);
227 		return (-1);
228 	}
229 }
230 
231 static int
232 add_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
233     const char *pgname, ...)
234 {
235 	const char *pname;
236 	va_list ap;
237 	int nerr = 0;
238 
239 	va_start(ap, pgname);
240 	while ((pname = va_arg(ap, const char *)) != NULL) {
241 		if (add_kstat_longprop(mod, node, ksp, pgname, pname) != 0)
242 			nerr++;		/* have whinged elsewhere */
243 	}
244 	va_end(ap);
245 
246 	return (nerr == 0 ? 0 : -1);
247 }
248 
249 static int
250 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst,
251     nvlist_t *auth, nvlist_t **nvl)
252 {
253 	*nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name,
254 	    inst, NULL, auth, NULL, NULL, NULL);
255 	return (nvl != NULL ? 0 : -1);	/* caller must free nvlist */
256 }
257 
258 static nvlist_t *
259 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
260 {
261 	int err;
262 	nvlist_t *asru;
263 
264 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
265 		return (NULL);
266 
267 	err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
268 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
269 	err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
270 	err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
271 	if (s != NULL)
272 		err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
273 	if (err != 0) {
274 		nvlist_free(asru);
275 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
276 		return (NULL);
277 	}
278 
279 	return (asru);
280 }
281 
282 static nvlist_t *
283 mem_fmri_create(topo_mod_t *mod)
284 {
285 	nvlist_t *asru;
286 
287 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
288 		return (NULL);
289 
290 	if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
291 	    nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) {
292 		nvlist_free(asru);
293 		return (NULL);
294 	}
295 
296 	return (asru);
297 }
298 
299 static int
300 cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid,
301     chip_t *chip, nvlist_t *auth)
302 {
303 	kstat_named_t *k;
304 	nvlist_t *fmri, *asru;
305 	tnode_t *cnode;
306 	int err, nerr = 0;
307 	int clogid, cpuid;
308 
309 	if (topo_node_range_create(mod, pnode, name, 0,
310 	    chip->chip_ncpustats) < 0)
311 		return (-1);
312 
313 	for (cpuid = 0; cpuid <= chip->chip_ncpustats; cpuid++) {
314 		if (chip->chip_cpustats[cpuid] == NULL)
315 			continue;
316 
317 		/*
318 		 * The chip_id in the cpu_info kstat numbers the individual
319 		 * chips from 0 to #chips - 1.
320 		 */
321 		if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid],
322 		    "chip_id")) == NULL) {
323 			whinge(mod, &nerr, "cpu_create: chip_id lookup via "
324 			    "kstats failed\n");
325 			continue;
326 		}
327 
328 		if (k->value.l != chipid)
329 			continue;	/* not an error */
330 
331 		/*
332 		 * The clog_id in the cpu_info kstat numbers the virtual
333 		 * processors of a single chip;  these may be separate
334 		 * processor cores, or they may be hardware threads/strands
335 		 * of individual cores.
336 		 *
337 		 * The core_id in the cpu_info kstat tells us which cpus
338 		 * share the same core - i.e., are hardware strands of the
339 		 * same core.  This enumerator does not distinguish stranded
340 		 * cores so core_id is unused.
341 		 */
342 		if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid],
343 		    "clog_id")) == NULL) {
344 			whinge(mod, &nerr, "cpu_create: clog_id lookup via "
345 			    "kstats failed\n");
346 			continue;
347 		}
348 		clogid = k->value.l;
349 
350 		if (mkrsrc(mod, pnode, name, clogid, auth, &fmri) != 0) {
351 			whinge(mod, &nerr, "cpu_create: mkrsrc failed\n");
352 			continue;
353 		}
354 
355 		if ((cnode = topo_node_bind(mod, pnode, name, clogid, fmri))
356 		    == NULL) {
357 			whinge(mod, &nerr, "cpu_create: node bind failed\n");
358 			nvlist_free(fmri);
359 			continue;
360 		}
361 		nvlist_free(fmri);
362 
363 		if ((asru = cpu_fmri_create(mod, cpuid, NULL, 0)) != NULL) {
364 			(void) topo_node_asru_set(cnode, asru, 0, &err);
365 			nvlist_free(asru);
366 		} else {
367 			whinge(mod, &nerr, "cpu_create: cpu_fmri_create "
368 			    "failed\n");
369 		}
370 		(void) topo_node_fru_set(cnode, NULL, 0, &err);
371 
372 		(void) topo_pgroup_create(cnode, &cpu_pgroup, &err);
373 
374 		(void) topo_prop_set_uint32(cnode, PGNAME(CPU), "cpuid",
375 		    TOPO_PROP_IMMUTABLE, cpuid, &err);
376 
377 		if (add_kstat_longprops(mod, cnode, chip->chip_cpustats[cpuid],
378 		    PGNAME(CPU), CPU_CHIP_ID, CPU_CORE_ID, CPU_CLOG_ID,
379 		    NULL) != 0)
380 			nerr++;		/* have whinged elsewhere */
381 	}
382 
383 	return (nerr == 0 ? 0 : -1);
384 }
385 
386 static int
387 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
388 {
389 	int success = 0;
390 	int err;
391 	char *pname = nvpair_name(nvp);
392 
393 	switch (nvpair_type(nvp)) {
394 	case DATA_TYPE_BOOLEAN_VALUE: {
395 		boolean_t val;
396 
397 		if (nvpair_value_boolean_value(nvp, &val) == 0 &&
398 		    topo_prop_set_string(node, pgname, pname,
399 		    TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0)
400 			success = 1;
401 		break;
402 	}
403 
404 	case DATA_TYPE_UINT32: {
405 		uint32_t val;
406 
407 		if (nvpair_value_uint32(nvp, &val) == 0 &&
408 		    topo_prop_set_uint32(node, pgname, pname,
409 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
410 			success = 1;
411 		break;
412 	}
413 
414 	case DATA_TYPE_UINT64: {
415 		uint64_t val;
416 
417 		if (nvpair_value_uint64(nvp, &val) == 0 &&
418 		    topo_prop_set_uint64(node, pgname, pname,
419 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
420 			success = 1;
421 		break;
422 	}
423 
424 	case DATA_TYPE_UINT32_ARRAY: {
425 		uint32_t *arrp;
426 		uint_t nelem;
427 
428 		if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 &&
429 		    nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname,
430 		    TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0)
431 			success = 1;
432 		break;
433 	}
434 
435 	case DATA_TYPE_STRING: {
436 		char *str;
437 
438 		if (nvpair_value_string(nvp, &str) == 0 &&
439 		    topo_prop_set_string(node, pgname, pname,
440 		    TOPO_PROP_IMMUTABLE, str, &err) == 0)
441 			success = 1;
442 		break;
443 	}
444 
445 	default:
446 		whinge(mod, &err, "nvprop_add: Can't handle type %d for "
447 		    "'%s' in property group %s of %s node\n",
448 		    nvpair_type(nvp), pname, pgname, topo_node_name(node));
449 		break;
450 	}
451 
452 	return (success ? 0 : 1);
453 }
454 
455 static int
456 chip_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
457 {
458 	nvpair_t *nvp;
459 	int nerr = 0;
460 
461 	if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
462 		whinge(mod, &nerr, "chip_htconfig: must pass a chip node!");
463 		return (-1);
464 	}
465 
466 	for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
467 	    nvp = nvlist_next_nvpair(htnvl, nvp)) {
468 		if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
469 			nerr++;
470 	}
471 
472 	return (nerr == 0 ? 0 : -1);
473 }
474 
475 static int
476 dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
477     nvlist_t *auth)
478 {
479 	tnode_t *chnode;
480 	nvlist_t *fmri;
481 	char *socket;
482 	int i, nchan;
483 	int err, nerr = 0;
484 
485 	/*
486 	 * We will enumerate the number of channels present even if only
487 	 * channel A is in use (i.e., running in 64-bit mode).  Only
488 	 * the socket 754 package has a single channel.
489 	 */
490 	if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
491 	    &socket, &err) != 0)
492 		return (-1);
493 
494 	if (strcmp(socket, "Socket 754") == 0)
495 		nchan = 1;
496 	else
497 		nchan = 2;
498 
499 	topo_mod_strfree(mod, socket);
500 
501 	if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
502 		return (-1);
503 
504 	for (i = 0; i < nchan; i++) {
505 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
506 			whinge(mod, &nerr, "dramchan_create: mkrsrc "
507 			    "failed\n");
508 			continue;
509 		}
510 
511 		if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
512 		    == NULL) {
513 			nvlist_free(fmri);
514 			whinge(mod, &nerr, "dramchan_create: node bind "
515 			    "failed\n");
516 			continue;
517 		}
518 
519 		nvlist_free(fmri);
520 
521 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
522 
523 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
524 		    TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
525 	}
526 
527 	return (nerr == 0 ? 0 : -1);
528 }
529 
530 static int
531 cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
532     nvlist_t *auth)
533 {
534 	int i, err, nerr = 0;
535 	nvpair_t *nvp;
536 	tnode_t *csnode;
537 	nvlist_t *fmri, **csarr = NULL;
538 	uint64_t csnum;
539 	uint_t ncs;
540 
541 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
542 		return (-1);
543 
544 	if (ncs == 0)
545 		return (0);	/* no chip-selects configured on this node */
546 
547 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
548 		return (-1);
549 
550 	for (i = 0; i < ncs; i++) {
551 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
552 			whinge(mod, &nerr, "cs_create: cs num property "
553 			    "missing\n");
554 			continue;
555 		}
556 
557 		if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
558 			whinge(mod, &nerr, "cs_create: mkrsrc failed\n");
559 			continue;
560 		}
561 
562 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
563 		    == NULL) {
564 			nvlist_free(fmri);
565 			whinge(mod, &nerr, "cs_create: node bind failed\n");
566 			continue;
567 		}
568 
569 		cs_fmri[csnum] = fmri;	/* nvlist will be freed in mc_create */
570 
571 		(void) topo_node_asru_set(csnode, fmri, 0, &err);
572 
573 		(void) topo_pgroup_create(csnode, &cs_pgroup, &err);
574 
575 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
576 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
577 			nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
578 		}
579 	}
580 
581 	return (nerr == 0 ? 0 : -1);
582 }
583 
584 /*
585  * Registered method for asru computation for rank nodes.  The 'node'
586  * argument identifies the node for which we seek an asru.  The 'in'
587  * argument is used to select which asru we will return, as follows:
588  *
589  * - the node name must be "dimm" or "rank"
590  * - if 'in' is NULL then return any statically defined asru for this node
591  * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru
592  *   with unum being the hc path to the dimm or rank (this method is called
593  *   as part of dynamic asru computation for rank nodes only, but dimm_create
594  *   also calls it directly to construct a "mem" scheme asru for a dimm node)
595  * - if 'in' in addition includes an hc-specific member which specifies
596  *   asru-physaddr or asru-offset then these are includes in the "mem" scheme
597  *   asru as additional members physaddr and offset
598  */
599 static int
600 mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru)
601 {
602 	int incl_pa = 0, incl_offset = 0;
603 	nvlist_t *hcsp, *ap;
604 	char *unum, *scheme;
605 	uint64_t pa, offset;
606 	int err;
607 
608 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 ||
609 	    strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
610 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
611 
612 	if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
613 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR,
614 		    &pa) == 0)
615 			incl_pa = 1;
616 
617 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET,
618 		    &offset) == 0)
619 			incl_offset = 1;
620 	}
621 
622 	/* use 'fmri' to obtain resource path;  could use node resource */
623 	if (topo_mod_nvl2str(mod, fmri, &unum) < 0)
624 		return (-1);  /* mod errno set */
625 
626 	if ((ap = mem_fmri_create(mod)) == NULL) {
627 		topo_mod_strfree(mod, unum);
628 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
629 	}
630 
631 	err = nvlist_add_string(ap, FM_FMRI_MEM_UNUM, unum);
632 	if (incl_pa)
633 		err |= nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa);
634 	if (incl_offset)
635 		err |= nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset);
636 
637 	topo_mod_strfree(mod, unum);
638 	if (err != 0) {
639 		nvlist_free(ap);
640 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
641 	}
642 
643 	*asru = ap;
644 
645 	return (0);
646 }
647 
648 /*ARGSUSED*/
649 static int
650 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
651     nvlist_t *in, nvlist_t **out)
652 {
653 	nvlist_t *asru;
654 	nvlist_t *args, *pargs;
655 	int err;
656 
657 	if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
658 	    strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0)
659 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
660 
661 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0)
662 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
663 
664 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) {
665 		if (err == ENOENT) {
666 			if (topo_mod_nvdup(mod, args, &asru) < 0)
667 				return (topo_mod_seterrno(mod, EMOD_NOMEM));
668 		} else {
669 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
670 		}
671 	} else if (mem_asru_create(mod, pargs, &asru) != 0) {
672 		return (-1); /* mod errno already set */
673 	}
674 
675 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
676 		nvlist_free(asru);
677 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
678 	}
679 
680 	err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU);
681 	err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI);
682 	err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru);
683 	if (err != 0) {
684 		nvlist_free(asru);
685 		nvlist_free(*out);
686 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
687 	}
688 
689 	nvlist_free(asru);
690 
691 	return (0);
692 }
693 
694 static int
695 rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl, nvlist_t *auth)
696 {
697 	uint64_t *csnumarr;
698 	char **csnamearr;
699 	uint_t ncs, ncsname;
700 	tnode_t *ranknode;
701 	nvlist_t *fmri, *pfmri = NULL;
702 	uint64_t dsz, rsz;
703 	int nerr = 0;
704 	int err;
705 	int i;
706 
707 	if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
708 	    &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
709 	    &csnamearr, &ncsname) != 0 || ncs != ncsname) {
710 		whinge(mod, &nerr, "rank_create: "
711 		    "csnums/csnames extraction failed\n");
712 		return (nerr);
713 	}
714 
715 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
716 		whinge(mod, &nerr, "rank_create: parent fmri lookup "
717 		    "failed\n");
718 		return (nerr);
719 	}
720 
721 	if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
722 		whinge(mod, &nerr, "rank_create: range create failed\n");
723 		nvlist_free(pfmri);
724 		return (nerr);
725 	}
726 
727 	if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
728 	    &err) == 0) {
729 		rsz = dsz / ncs;
730 	} else {
731 		whinge(mod, &nerr, "rank_create: parent dimm has no size\n");
732 		return (nerr);
733 	}
734 
735 	for (i = 0; i < ncs; i++) {
736 		if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
737 			whinge(mod, &nerr, "rank_create: mkrsrc failed\n");
738 			continue;
739 		}
740 
741 		if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
742 		    fmri)) == NULL) {
743 			nvlist_free(fmri);
744 			whinge(mod, &nerr, "rank_create: node bind "
745 			    "failed\n");
746 			continue;
747 		}
748 
749 		nvlist_free(fmri);
750 
751 		(void) topo_node_fru_set(ranknode, pfmri, 0, &err);
752 
753 		/*
754 		 * If a rank is faulted the asru is the associated
755 		 * chip-select, but if a page within a rank is faulted
756 		 * the asru is just that page.  Hence the dual preconstructed
757 		 * and computed ASRU.
758 		 */
759 		if (topo_method_register(mod, ranknode, rank_methods) < 0)
760 			whinge(mod, &nerr, "rank_create: "
761 			    "topo_method_register failed");
762 
763 		(void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
764 		    TOPO_ASRU_COMPUTE, &err);
765 
766 		(void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
767 
768 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
769 		    TOPO_PROP_IMMUTABLE, rsz, &err);
770 
771 		(void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
772 		    TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
773 
774 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
775 		    TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
776 	}
777 
778 	nvlist_free(pfmri);
779 
780 	return (nerr);
781 }
782 
783 static int
784 dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
785     nvlist_t *auth)
786 {
787 	int i, err, nerr = 0;
788 	nvpair_t *nvp;
789 	tnode_t *dimmnode;
790 	nvlist_t *fmri, *asru, **dimmarr = NULL;
791 	uint64_t num;
792 	uint_t ndimm;
793 
794 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
795 		whinge(mod, NULL, "dimm_create: dimmlist lookup failed\n");
796 		return (-1);
797 	}
798 
799 	if (ndimm == 0)
800 		return (0);	/* no dimms present on this node */
801 
802 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
803 		whinge(mod, NULL, "dimm_create: range create failed\n");
804 		return (-1);
805 	}
806 
807 	for (i = 0; i < ndimm; i++) {
808 		if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
809 			whinge(mod, &nerr, "dimm_create: dimm num property "
810 			    "missing\n");
811 			continue;
812 		}
813 
814 		if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
815 			whinge(mod, &nerr, "dimm_create: mkrsrc failed\n");
816 			continue;
817 		}
818 
819 		if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
820 		    == NULL) {
821 			nvlist_free(fmri);
822 			whinge(mod, &nerr, "dimm_create: node bind "
823 			    "failed\n");
824 			continue;
825 		}
826 
827 		if (topo_method_register(mod, dimmnode, dimm_methods) < 0)
828 			whinge(mod, &nerr, "dimm_create: "
829 			    "topo_method_register failed");
830 
831 		/*
832 		 * Use the mem computation method directly to publish the asru
833 		 * in the "mem" scheme.
834 		 */
835 		if (mem_asru_create(mod, fmri, &asru) == 0) {
836 			(void) topo_node_asru_set(dimmnode, asru, 0, &err);
837 			nvlist_free(asru);
838 		} else {
839 
840 			nvlist_free(fmri);
841 			whinge(mod, &nerr, "dimm_create: mem_asru_compute "
842 			    "failed\n");
843 			continue;
844 		}
845 
846 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
847 
848 		nvlist_free(fmri);
849 
850 		(void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
851 
852 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
853 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
854 			if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
855 			    strcmp(nvpair_name(nvp), "csnums") == 0 ||
856 			    nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
857 			    strcmp(nvpair_name(nvp), "csnames") == 0)
858 				continue;	/* used in rank_create() */
859 
860 			nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
861 		}
862 
863 		nerr += rank_create(mod, dimmnode, dimmarr[i], auth);
864 	}
865 
866 	return (nerr == 0 ? 0 : -1);
867 }
868 
869 static nvlist_t *
870 mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
871 {
872 	mc_snapshot_info_t mcs;
873 	void *buf = NULL;
874 	uint8_t ver;
875 
876 	nvlist_t *nvl = NULL;
877 	char path[64];
878 	int fd, err;
879 
880 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
881 	fd = open(path, O_RDONLY);
882 
883 	if (fd == -1) {
884 		/*
885 		 * Some v20z and v40z systems may have had the 3rd-party
886 		 * NWSnps packagae installed which installs a /dev/mc
887 		 * link.  So try again via /devices.
888 		 */
889 		(void) snprintf(path, sizeof (path),
890 		    "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
891 		    MC_AMD_DEV_OFFSET + id);
892 		fd = open(path, O_RDONLY);
893 	}
894 
895 	if (fd == -1) {
896 		whinge(mod, NULL, "mc failed to open %s: %s\n",
897 		    path, strerror(errno));
898 		return (NULL);
899 	}
900 
901 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
902 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
903 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
904 
905 		whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
906 		    path, strerror(errno));
907 
908 		free(buf);
909 		(void) close(fd);
910 		return (NULL);
911 	}
912 
913 	(void) close(fd);
914 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
915 	topo_mod_free(mod, buf, mcs.mcs_size);
916 
917 
918 	if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
919 		whinge(mod, NULL, "mc nvlist is not versioned\n");
920 		nvlist_free(nvl);
921 		return (NULL);
922 	} else if (ver != MC_NVLIST_VERS1) {
923 		whinge(mod, NULL, "mc nvlist version mismatch\n");
924 		nvlist_free(nvl);
925 		return (NULL);
926 	}
927 
928 	return (err ? NULL : nvl);
929 }
930 
931 static int
932 mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth)
933 {
934 	int err, rc = 0;
935 	tnode_t *mcnode;
936 	nvlist_t *fmri;
937 	nvpair_t *nvp;
938 	nvlist_t *mc = NULL;
939 	int i;
940 
941 	if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) {
942 		whinge(mod, NULL, "mc_create: mkrsrc failed\n");
943 		return (-1);
944 	}
945 
946 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
947 		nvlist_free(fmri);
948 		whinge(mod, NULL, "mc_create: node range create failed\n");
949 		return (-1);
950 	}
951 
952 	/*
953 	 * Gather and create memory controller topology
954 	 */
955 	if ((mc = mc_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL ||
956 	    (mcnode = topo_node_bind(mod, pnode,
957 	    name, 0, fmri)) == NULL) {
958 		if (mc != NULL)
959 			nvlist_free(mc);
960 		topo_node_range_destroy(pnode, name);
961 		nvlist_free(fmri);
962 		whinge(mod, NULL, "mc_create: mc lookup or bind failed\n");
963 		return (-1);
964 	}
965 
966 	(void) topo_node_fru_set(mcnode, NULL, 0, &err);
967 	nvlist_free(fmri);
968 
969 	/*
970 	 * Add memory controller properties
971 	 */
972 	(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
973 
974 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
975 	    nvp = nvlist_next_nvpair(mc, nvp)) {
976 		char *name = nvpair_name(nvp);
977 		data_type_t type = nvpair_type(nvp);
978 
979 		if (type == DATA_TYPE_NVLIST_ARRAY &&
980 		    (strcmp(name, "cslist") == 0 ||
981 		    strcmp(name, "dimmlist") == 0)) {
982 			continue;
983 		} else if (type == DATA_TYPE_UINT8 &&
984 		    strcmp(name, MC_NVLIST_VERSTR) == 0) {
985 			continue;
986 		} else if (type == DATA_TYPE_NVLIST &&
987 		    strcmp(name, "htconfig") == 0) {
988 			nvlist_t *htnvl;
989 
990 			(void) nvpair_value_nvlist(nvp, &htnvl);
991 			if (chip_htconfig(mod, pnode, htnvl) != 0)
992 				rc = -1;
993 		} else {
994 			if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
995 				rc = -1;
996 		}
997 	}
998 
999 	if (dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
1000 	    cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
1001 	    dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
1002 		rc = -1;
1003 
1004 	/*
1005 	 * Free the fmris for the chip-selects allocated in cs_create
1006 	 */
1007 	for (i = 0; i < MC_CHIP_NCS; i++) {
1008 		if (cs_fmri[i] != NULL) {
1009 			nvlist_free(cs_fmri[i]);
1010 			cs_fmri[i] = NULL;
1011 		}
1012 	}
1013 
1014 	nvlist_free(mc);
1015 	return (rc);
1016 }
1017 
1018 static int
1019 chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
1020     topo_instance_t min, topo_instance_t max, chip_t *chip, nvlist_t *auth)
1021 {
1022 	int i, nerr = 0;
1023 	kstat_t *ksp;
1024 	ulong_t *chipmap;
1025 	tnode_t *cnode;
1026 	nvlist_t *fmri;
1027 
1028 	if ((chipmap = topo_mod_zalloc(mod, BT_BITOUL(max) *
1029 	    sizeof (ulong_t))) == NULL)
1030 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1031 
1032 	/*
1033 	 * Read in all cpu_info kstats, for all chip ids.  The ks_instance
1034 	 * argument to kstat_lookup is the logical cpu_id - we will use this
1035 	 * in cpu_create.
1036 	 */
1037 	for (i = 0; i <= chip->chip_ncpustats; i++) {
1038 		if ((ksp = kstat_lookup(chip->chip_kc, "cpu_info", i, NULL)) ==
1039 		    NULL || kstat_read(chip->chip_kc, ksp, NULL) < 0)
1040 			continue;
1041 
1042 		chip->chip_cpustats[i] = ksp;
1043 	}
1044 
1045 	for (i = 0; i <= chip->chip_ncpustats; i++) {
1046 		kstat_named_t *k;
1047 		int err, chipid;
1048 
1049 		if ((ksp = chip->chip_cpustats[i]) == NULL)
1050 			continue;
1051 
1052 		if ((k = kstat_data_lookup(ksp, "chip_id")) == NULL) {
1053 			whinge(mod, &nerr, "chip_create: chip_id lookup "
1054 			    "via kstats failed\n");
1055 			continue;
1056 		}
1057 
1058 		chipid = k->value.l;
1059 		if (BT_TEST(chipmap, chipid))
1060 			continue;
1061 
1062 		if (chipid < min || chipid > max)
1063 			continue;
1064 
1065 		if (mkrsrc(mod, pnode, name, chipid, auth, &fmri) != 0) {
1066 			whinge(mod, &nerr, "chip_create: mkrsrc failed\n");
1067 			continue;
1068 		}
1069 
1070 		if ((cnode = topo_node_bind(mod, pnode, name, chipid, fmri))
1071 		    == NULL) {
1072 			nvlist_free(fmri);
1073 			whinge(mod, &nerr, "chip_create: node bind "
1074 			    "failed for chipid %d\n", chipid);
1075 			continue;
1076 		}
1077 		BT_SET(chipmap, chipid);
1078 
1079 		if (topo_method_register(mod, cnode, chip_methods) < 0)
1080 			whinge(mod, &nerr, "chip_create: "
1081 			    "topo_method_register failed");
1082 
1083 		(void) topo_node_fru_set(cnode, fmri, 0, &err);
1084 
1085 		nvlist_free(fmri);
1086 
1087 		(void) topo_pgroup_create(cnode, &chip_pgroup, &err);
1088 		if (add_kstat_strprop(mod, cnode, ksp, PGNAME(CHIP),
1089 		    CHIP_VENDOR_ID) != 0)
1090 			nerr++;		/* have whinged elsewhere */
1091 
1092 		if (add_kstat_longprops(mod, cnode, ksp, PGNAME(CHIP),
1093 		    CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL) != 0)
1094 			nerr++;		/* have whinged elsewhere */
1095 
1096 		if (cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip, auth)
1097 		    != 0 || mc_create(mod, cnode, MCT_NODE_NAME, auth) != 0)
1098 			nerr++;		/* have whinged elsewhere */
1099 	}
1100 
1101 	topo_mod_free(mod, chipmap, BT_BITOUL(max) * sizeof (ulong_t));
1102 
1103 	if (nerr == 0) {
1104 		return (0);
1105 	} else {
1106 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
1107 		return (-1);
1108 	}
1109 }
1110 
1111 /*ARGSUSED*/
1112 static int
1113 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1114     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
1115 {
1116 	int rv = 0;
1117 	chip_t *chip = (chip_t *)arg;
1118 	nvlist_t *auth = NULL;
1119 
1120 	auth = topo_mod_auth(mod, pnode);
1121 
1122 	if (strcmp(name, CHIP_NODE_NAME) == 0)
1123 		rv = chip_create(mod, pnode, name, min, max, chip, auth);
1124 
1125 	nvlist_free(auth);
1126 
1127 	return (rv);
1128 }
1129