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 <string.h>
33 #include <strings.h>
34 #include <limits.h>
35 #include <alloca.h>
36 #include <kstat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <libnvpair.h>
40 #include <sys/types.h>
41 #include <sys/bitmap.h>
42 #include <sys/processor.h>
43 #include <sys/param.h>
44 #include <sys/fm/protocol.h>
45 #include <sys/systeminfo.h>
46 #include <sys/mc.h>
47 #include <fm/topo_mod.h>
48 
49 #include "chip.h"
50 
51 #ifndef MAX
52 #define	MAX(a, b)	((a) > (b) ? (a) : (b))
53 #endif
54 
55 #define	MAX_DIMMNUM	7
56 #define	MAX_CSNUM	7
57 
58 /*
59  * Enumerates the processing chips, or sockets, (as distinct from cores) in a
60  * system.  For each chip found, the necessary nodes (one or more cores, and
61  * possibly a memory controller) are constructed underneath.
62  */
63 
64 static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
65     topo_instance_t, void *);
66 
67 const topo_modinfo_t chip_info =
68 	{ "chip", CHIP_VERSION, chip_enum, NULL};
69 
70 int
71 _topo_init(topo_mod_t *mod)
72 {
73 	chip_t *chip;
74 
75 	topo_mod_setdebug(mod, TOPO_DBG_ALL);
76 	topo_mod_dprintf(mod, "initializing chip enumerator\n");
77 
78 	if ((chip = topo_mod_zalloc(mod, sizeof (chip_t))) == NULL)
79 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
80 
81 	if ((chip->chip_kc = kstat_open()) == NULL) {
82 		topo_mod_dprintf(mod, "kstat_open failed: %s\n",
83 		    strerror(errno));
84 		topo_mod_free(mod, chip, sizeof (chip_t));
85 		return (topo_mod_seterrno(mod, errno));
86 	}
87 
88 	chip->chip_ncpustats = sysconf(_SC_CPUID_MAX);
89 	if ((chip->chip_cpustats = topo_mod_zalloc(mod, (
90 	    chip->chip_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
91 		(void) kstat_close(chip->chip_kc);
92 		topo_mod_free(mod, chip, sizeof (chip_t));
93 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
94 	}
95 
96 	if (topo_mod_register(mod, &chip_info, (void *)chip) != 0) {
97 		topo_mod_dprintf(mod, "failed to register hc: "
98 		    "%s\n", topo_mod_errmsg(mod));
99 		topo_mod_free(mod, chip->chip_cpustats,
100 		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
101 		(void) kstat_close(chip->chip_kc);
102 		topo_mod_free(mod, chip, sizeof (chip_t));
103 		return (-1); /* mod errno set */
104 	}
105 
106 	return (0);
107 }
108 
109 void
110 _topo_fini(topo_mod_t *mod)
111 {
112 	chip_t *chip = topo_mod_private(mod);
113 
114 	if (chip->chip_cpustats != NULL)
115 		topo_mod_free(mod, chip->chip_cpustats,
116 		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
117 
118 	(void) kstat_close(chip->chip_kc);
119 	topo_mod_free(mod, chip, sizeof (chip_t));
120 
121 	topo_mod_unregister(mod);
122 }
123 
124 static int
125 chip_strprop(tnode_t *cnode, kstat_t *ksp, const char *name)
126 {
127 	int err;
128 	kstat_named_t *k;
129 
130 	if ((k = kstat_data_lookup(ksp, (char *)name)) == NULL)
131 		return (0);
132 
133 	(void) topo_prop_set_string(cnode, CHIP_PGROUP, name,
134 	    TOPO_PROP_SET_ONCE, k->value.str.addr.ptr, &err);
135 
136 	return (-1);
137 }
138 
139 static int
140 chip_longprop(tnode_t *cnode, kstat_t *ksp, const char *name)
141 {
142 	int err;
143 	kstat_named_t *k;
144 
145 	if ((k = kstat_data_lookup(ksp, (char *)name)) == NULL)
146 		return (0);
147 
148 	(void) topo_prop_set_int32(cnode, CHIP_PGROUP, name, TOPO_PROP_SET_ONCE,
149 	    k->value.l, &err);
150 
151 	return (-1);
152 }
153 
154 static nvlist_t *
155 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
156 {
157 	int err;
158 	nvlist_t *asru;
159 
160 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
161 		return (NULL);
162 
163 	err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
164 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
165 	err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
166 	err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
167 	if (s != NULL)
168 		err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
169 	if (err != 0) {
170 		nvlist_free(asru);
171 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
172 		return (NULL);
173 	}
174 
175 	return (asru);
176 }
177 
178 static int
179 cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid,
180     chip_t *chip)
181 {
182 	kstat_named_t *k;
183 	topo_hdl_t *thp;
184 	nvlist_t *fmri, *pfmri, *asru, *args;
185 	tnode_t *cnode;
186 	int i, err, nerr = 0;
187 
188 	if (topo_node_range_create(mod, pnode, name, 0,
189 	    chip->chip_ncpustats) < 0)
190 		return (-1);
191 
192 	thp = topo_mod_handle(mod);
193 
194 	for (i = 0; i <= chip->chip_ncpustats; i++) {
195 
196 		if (chip->chip_cpustats[i] == NULL)
197 			continue;
198 
199 		if ((k = kstat_data_lookup(chip->chip_cpustats[i], "chip_id"))
200 		    == NULL || k->value.l != chipid) {
201 			++nerr;
202 			continue;
203 		}
204 
205 		if ((k = kstat_data_lookup(chip->chip_cpustats[i], "clog_id"))
206 		    == NULL) {
207 			++nerr;
208 			continue;
209 		}
210 
211 		args = pfmri = NULL;
212 		if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
213 		    topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
214 		    nvlist_add_nvlist(args,
215 			TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
216 				nvlist_free(pfmri);
217 				nvlist_free(args);
218 				++nerr;
219 				continue;
220 			}
221 
222 		fmri = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name,
223 		    (topo_instance_t)k->value.l, args, &err);
224 		nvlist_free(pfmri);
225 		nvlist_free(args);
226 		if (fmri == NULL) {
227 			++nerr;
228 			continue;
229 		}
230 
231 		if ((cnode = topo_node_bind(mod, pnode, name, i, fmri,
232 		    NULL)) == NULL) {
233 			++nerr;
234 			nvlist_free(fmri);
235 			continue;
236 		}
237 		nvlist_free(fmri);
238 
239 		if ((asru = cpu_fmri_create(mod, i, NULL, 0)) != NULL) {
240 			(void) topo_node_asru_set(cnode, asru, 0, &err);
241 			nvlist_free(asru);
242 		} else {
243 			++nerr;
244 		}
245 		(void) topo_node_fru_set(cnode, NULL, 0, &err);
246 	}
247 
248 	if (nerr != 0)
249 		return (-1);
250 	else
251 		return (0);
252 }
253 
254 static int
255 nvprop_add(nvpair_t *nvp, const char *pgname, tnode_t *node)
256 {
257 	int err;
258 	char *pname = nvpair_name(nvp);
259 
260 	switch (nvpair_type(nvp)) {
261 	case DATA_TYPE_BOOLEAN_VALUE: {
262 		boolean_t val;
263 
264 		if (nvpair_value_boolean_value(nvp, &val) == 0) {
265 			(void) topo_prop_set_string(node, pgname, pname,
266 			    TOPO_PROP_SET_ONCE, (val ? "true" : "false"), &err);
267 		}
268 		return (0);
269 	}
270 
271 	case DATA_TYPE_UINT64: {
272 		uint64_t val;
273 
274 		if (nvpair_value_uint64(nvp, &val) == 0) {
275 			(void) topo_prop_set_uint64(node, pgname, pname,
276 			    TOPO_PROP_SET_ONCE, val, &err);
277 		}
278 		return (0);
279 	}
280 
281 	case DATA_TYPE_STRING: {
282 		char *str;
283 
284 		if (nvpair_value_string(nvp, &str) == 0)
285 			(void) topo_prop_set_string(node, pgname, pname,
286 			    TOPO_PROP_SET_ONCE, str, &err);
287 		return (0);
288 	}
289 
290 	default:
291 		return (-1);
292 	}
293 }
294 
295 nvlist_t *
296 mem_fmri_create(topo_mod_t *mod)
297 {
298 	nvlist_t *asru;
299 
300 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
301 		return (NULL);
302 
303 	if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
304 	    nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) {
305 		nvlist_free(asru);
306 		return (NULL);
307 	}
308 
309 	return (asru);
310 }
311 
312 static int
313 cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
314 {
315 	int i, err, nerr = 0;
316 	nvpair_t *nvp;
317 	tnode_t *csnode;
318 	topo_hdl_t *thp;
319 	nvlist_t *fmri, **csarr = NULL;
320 	nvlist_t *pfmri, *args;
321 	uint64_t csnum;
322 	uint_t ncs;
323 
324 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0 ||
325 	    ncs == 0)
326 		return (-1);
327 
328 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
329 		return (-1);
330 
331 	thp = topo_mod_handle(mod);
332 	for (i = 0; i < ncs; i++) {
333 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
334 			++nerr;
335 			continue;
336 		}
337 
338 		args = pfmri = NULL;
339 		if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
340 		    topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
341 		    nvlist_add_nvlist(args,
342 			TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
343 				nvlist_free(pfmri);
344 				nvlist_free(args);
345 				++nerr;
346 				continue;
347 			}
348 		fmri = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name,
349 		    csnum, args, &err);
350 		nvlist_free(pfmri);
351 		nvlist_free(args);
352 		if (fmri == NULL) {
353 			++nerr;
354 			continue;
355 		}
356 
357 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri,
358 		    NULL)) == NULL) {
359 			nvlist_free(fmri);
360 			++nerr;
361 			continue;
362 		}
363 
364 		nvlist_free(fmri);
365 
366 		(void) topo_pgroup_create(csnode, CS_PGROUP,
367 		    TOPO_STABILITY_PRIVATE, &err);
368 
369 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
370 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
371 			(void) nvprop_add(nvp, CS_PGROUP, csnode);
372 		}
373 	}
374 
375 	if (nerr != 0)
376 		return (-1);
377 	else
378 		return (0);
379 }
380 
381 static int
382 dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc)
383 {
384 	int i, err, nerr = 0;
385 	nvpair_t *nvp;
386 	tnode_t *dimmnode;
387 	nvlist_t *fmri, *asru, **dimmarr = NULL;
388 	nvlist_t *pfmri, *args;
389 	uint64_t ldimmnum;
390 	uint_t ndimm;
391 	topo_hdl_t *thp;
392 
393 	thp = topo_mod_handle(mod);
394 
395 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0 ||
396 	    ndimm == 0)
397 		return (-1);
398 
399 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0)
400 		return (-1);
401 
402 	for (i = 0; i < ndimm; i++) {
403 		if (nvlist_lookup_uint64(dimmarr[i], "num", &ldimmnum) != 0) {
404 			++nerr;
405 			continue;
406 		}
407 
408 		args = pfmri = NULL;
409 		if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
410 		    topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
411 		    nvlist_add_nvlist(args,
412 		    TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
413 				nvlist_free(pfmri);
414 				nvlist_free(args);
415 				++nerr;
416 				continue;
417 			}
418 		fmri = topo_fmri_create(thp,
419 		    FM_FMRI_SCHEME_HC, name, ldimmnum, args, &err);
420 		nvlist_free(pfmri);
421 		nvlist_free(args);
422 		if (fmri == NULL) {
423 			++nerr;
424 			continue;
425 		}
426 
427 		if ((dimmnode = topo_node_bind(mod, pnode, name, ldimmnum, fmri,
428 		    NULL)) == NULL) {
429 			nvlist_free(fmri);
430 			++nerr;
431 			continue;
432 		}
433 
434 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
435 		if ((asru = mem_fmri_create(mod)) != NULL) {
436 			(void) topo_node_asru_set(dimmnode, asru,
437 			    TOPO_ASRU_COMPUTE, &err);
438 			nvlist_free(asru);
439 		}
440 
441 		nvlist_free(fmri);
442 
443 		(void) topo_pgroup_create(dimmnode, DIMM_PGROUP,
444 		    TOPO_STABILITY_PRIVATE, &err);
445 
446 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
447 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
448 			if (nvprop_add(nvp, DIMM_PGROUP, dimmnode) == 0) {
449 				continue;
450 			} else if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY) {
451 				uint64_t *csnumarr;
452 				uint_t ncs;
453 				int i;
454 
455 				if (strcmp(nvpair_name(nvp), "csnums") != 0 ||
456 				    nvpair_value_uint64_array(nvp, &csnumarr,
457 				    &ncs) != 0)
458 					continue;
459 
460 				for (i = 0; i < ncs; i++) {
461 					char name[7];
462 					(void) snprintf(name, sizeof (name),
463 					    "csnum%d", i);
464 					(void) topo_prop_set_uint64(dimmnode,
465 					    DIMM_PGROUP, name,
466 					    TOPO_PROP_SET_ONCE,
467 					    csnumarr[i], &err);
468 				}
469 			}
470 		}
471 	}
472 
473 	if (nerr != 0)
474 		return (-1);
475 	else
476 		return (0);
477 }
478 
479 static nvlist_t *
480 mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
481 {
482 	mc_snapshot_info_t mcs;
483 	void *buf = NULL;
484 
485 	nvlist_t *nvl;
486 	char path[64];
487 	int fd, err;
488 
489 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
490 	fd = open(path, O_RDONLY);
491 
492 	if (fd == -1) {
493 		topo_mod_dprintf(mod, "mc failed to open %s: %s\n",
494 		    path, strerror(errno));
495 		return (NULL);
496 	}
497 
498 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
499 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
500 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
501 
502 		topo_mod_dprintf(mod, "mc failed to snapshot %s: %s\n",
503 		    path, strerror(errno));
504 
505 		free(buf);
506 		(void) close(fd);
507 		return (NULL);
508 	}
509 
510 	(void) close(fd);
511 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
512 	topo_mod_free(mod, buf, mcs.mcs_size);
513 	return (err ? NULL : nvl);
514 }
515 
516 static int
517 mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name)
518 {
519 	int err, rc = 0;
520 	tnode_t *mcnode;
521 	nvlist_t *fmri;
522 	nvpair_t *nvp;
523 	nvlist_t *mc = NULL;
524 	nvlist_t *pfmri, *args;
525 	topo_hdl_t *thp;
526 
527 	thp = topo_mod_handle(mod);
528 	args = pfmri = NULL;
529 	if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
530 	    topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
531 	    nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
532 		nvlist_free(pfmri);
533 		nvlist_free(args);
534 		return (-1);
535 	}
536 	fmri = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name, 0, args, &err);
537 	nvlist_free(pfmri);
538 	nvlist_free(args);
539 	if (fmri == NULL)
540 		return (-1);
541 
542 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
543 		nvlist_free(fmri);
544 		return (-1);
545 	}
546 
547 	/*
548 	 * Gather and create memory controller topology
549 	 */
550 	if ((mc = mc_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL ||
551 	    (mcnode = topo_node_bind(mod, pnode,
552 	    name, 0, fmri, NULL)) == NULL) {
553 		if (mc != NULL)
554 			nvlist_free(mc);
555 		topo_node_range_destroy(pnode, name);
556 		nvlist_free(fmri);
557 		return (-1);
558 	}
559 
560 	(void) topo_node_fru_set(mcnode, NULL, 0, &err);
561 	nvlist_free(fmri);
562 
563 	/*
564 	 * Add memory controller properties
565 	 */
566 	(void) topo_pgroup_create(mcnode, MC_PGROUP,
567 	    TOPO_STABILITY_PRIVATE, &err);
568 
569 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
570 	    nvp = nvlist_next_nvpair(mc, nvp)) {
571 		if (nvprop_add(nvp, MC_PGROUP, mcnode) == 0)
572 			continue;
573 		else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY)
574 			break;
575 	}
576 
577 	if (dimm_create(mod, mcnode, DIMM_NODE_NAME, mc) != 0 ||
578 	    cs_create(mod, mcnode, CS_NODE_NAME, mc) != 0)
579 		rc = -1;
580 
581 	nvlist_free(mc);
582 	return (rc);
583 }
584 
585 static int
586 chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
587     topo_instance_t min, topo_instance_t max, chip_t *chip)
588 {
589 	int i, nerr = 0;
590 	kstat_t *ksp;
591 	ulong_t *chipmap;
592 	tnode_t *cnode;
593 	nvlist_t *pfmri, *fmri, *args;
594 	topo_hdl_t *thp;
595 
596 	thp = topo_mod_handle(mod);
597 
598 	if ((chipmap = topo_mod_zalloc(mod, BT_BITOUL(chip->chip_ncpustats) *
599 	    sizeof (ulong_t))) == NULL)
600 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
601 
602 	for (i = min; i <= MAX(max, chip->chip_ncpustats); i++) {
603 
604 		if (i < min || i > max)
605 			break;
606 
607 		if ((ksp = kstat_lookup(chip->chip_kc, "cpu_info", i, NULL)) ==
608 		    NULL || kstat_read(chip->chip_kc, ksp, NULL) < 0)
609 			continue;
610 
611 		chip->chip_cpustats[i] = ksp;
612 	}
613 
614 	for (i = 0; i <= chip->chip_ncpustats; i++) {
615 		kstat_named_t *k;
616 		int err, chipid;
617 
618 		if ((ksp = chip->chip_cpustats[i]) == NULL)
619 			continue;
620 
621 		if ((k = kstat_data_lookup(ksp, "chip_id")) == NULL) {
622 			++nerr;
623 			continue;
624 		}
625 
626 		chipid = k->value.l;
627 		if (BT_TEST(chipmap, chipid))
628 			continue;
629 
630 		if (chipid < min || chipid > max)
631 			continue;
632 
633 		args = pfmri = NULL;
634 		if (topo_node_resource(pnode, &pfmri, &err) < 0 ||
635 		    topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 ||
636 		    nvlist_add_nvlist(args,
637 		    TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) {
638 			nvlist_free(pfmri);
639 			nvlist_free(args);
640 			++nerr;
641 			continue;
642 		}
643 		fmri = topo_fmri_create(thp,
644 		    FM_FMRI_SCHEME_HC, name, chipid, args, &err);
645 		nvlist_free(pfmri);
646 		nvlist_free(args);
647 		if (fmri == NULL) {
648 			++nerr;
649 			continue;
650 		}
651 
652 		if ((cnode = topo_node_bind(mod, pnode, name, chipid, fmri,
653 		    NULL)) == NULL) {
654 			++nerr;
655 			nvlist_free(fmri);
656 			continue;
657 		}
658 
659 		(void) topo_node_fru_set(cnode, fmri, 0, &err);
660 
661 		nvlist_free(fmri);
662 
663 		(void) topo_pgroup_create(cnode, CHIP_PGROUP,
664 		    TOPO_STABILITY_PRIVATE, &err);
665 		(void) chip_strprop(cnode, ksp, CHIP_VENDOR_ID);
666 		(void) chip_longprop(cnode, ksp, CHIP_FAMILY);
667 		(void) chip_longprop(cnode, ksp, CHIP_MODEL);
668 		(void) chip_longprop(cnode, ksp, CHIP_STEPPING);
669 
670 		if (mc_create(mod, cnode, MC_NODE_NAME) != 0 ||
671 		    cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip) != 0)
672 			++nerr;
673 	}
674 
675 	topo_mod_free(mod, chipmap, BT_BITOUL(chip->chip_ncpustats) *
676 	    sizeof (ulong_t));
677 
678 	if (nerr != 0)
679 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
680 
681 	return (0);
682 }
683 
684 static int
685 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
686     topo_instance_t min, topo_instance_t max, void *arg)
687 {
688 	chip_t *chip = (chip_t *)arg;
689 
690 	if (strcmp(name, "chip") == 0)
691 		return (chip_create(mod, pnode, name, min, max, chip));
692 
693 	return (0);
694 }
695