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