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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2019, Joyent, Inc.
26  */
27 
28 #include <unistd.h>
29 #include <fcntl.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 <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 <sys/mc_amd.h>
48 #include <sys/mc_intel.h>
49 #include <sys/devfm.h>
50 #include <fm/fmd_agent.h>
51 #include <fm/topo_mod.h>
52 
53 #include "chip.h"
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 *,
65     topo_instance_t, topo_instance_t, void *, void *);
66 
67 static const topo_modops_t chip_ops =
68 	{ chip_enum, NULL};
69 static const topo_modinfo_t chip_info =
70 	{ CHIP_NODE_NAME, FM_FMRI_SCHEME_HC, CHIP_VERSION, &chip_ops };
71 
72 static const topo_pgroup_info_t chip_pgroup =
73 	{ PGNAME(CHIP), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
74 
75 static const topo_pgroup_info_t core_pgroup =
76 	{ PGNAME(CORE), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
77 
78 static const topo_pgroup_info_t strand_pgroup =
79 	{ PGNAME(STRAND), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
80 
81 static const topo_method_t chip_methods[] = {
82 	{ SIMPLE_CHIP_LBL, "Property method", 0,
83 	    TOPO_STABILITY_INTERNAL, simple_chip_label},
84 	{ G4_CHIP_LBL, "Property method", 0,
85 	    TOPO_STABILITY_INTERNAL, g4_chip_label},
86 	{ A4FPLUS_CHIP_LBL, "Property method", 0,
87 	    TOPO_STABILITY_INTERNAL, a4fplus_chip_label},
88 	{ FSB2_CHIP_LBL, "Property method", 0,
89 	    TOPO_STABILITY_INTERNAL, fsb2_chip_label},
90 	{ TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
91 	    TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
92 	    chip_fmri_replaced },
93 	{ NULL }
94 };
95 
96 static const topo_method_t strands_retire_methods[] = {
97 	{ TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
98 	    TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
99 	    retire_strands },
100 	{ TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
101 	    TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
102 	    unretire_strands },
103 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
104 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
105 	    service_state_strands },
106 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
107 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
108 	    unusable_strands },
109 	{ NULL }
110 };
111 
112 int
113 _topo_init(topo_mod_t *mod)
114 {
115 	if (getenv("TOPOCHIPDBG"))
116 		topo_mod_setdebug(mod);
117 	topo_mod_dprintf(mod, "initializing chip enumerator\n");
118 
119 	if (topo_mod_register(mod, &chip_info, TOPO_VERSION) != 0) {
120 		whinge(mod, NULL, "failed to register hc: "
121 		    "%s\n", topo_mod_errmsg(mod));
122 		return (-1); /* mod errno set */
123 	}
124 
125 	return (0);
126 }
127 
128 void
129 _topo_fini(topo_mod_t *mod)
130 {
131 	topo_mod_unregister(mod);
132 }
133 
134 boolean_t
135 is_xpv(void)
136 {
137 	static int r = -1;
138 	char platform[MAXNAMELEN];
139 
140 	if (r != -1)
141 		return (r == 0);
142 
143 	(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
144 	r = strcmp(platform, "i86xpv");
145 	return (r == 0);
146 }
147 
148 static tnode_t *
149 create_node(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, char *name,
150     topo_instance_t inst, nvlist_t *cpu, uint16_t smbios_id)
151 {
152 	nvlist_t *fmri;
153 	tnode_t *cnode;
154 
155 	if (mkrsrc(mod, pnode, name, inst, auth, &fmri) != 0) {
156 		whinge(mod, NULL, "create_node: mkrsrc failed\n");
157 		return (NULL);
158 	}
159 
160 	if (FM_AWARE_SMBIOS(mod)) {
161 		id_t phys_cpu_smbid;
162 		int perr = 0;
163 		const char *serial = NULL;
164 		const char *part = NULL;
165 		const char *rev = NULL;
166 
167 		phys_cpu_smbid = smbios_id;
168 		serial = chip_serial_smbios_get(mod, phys_cpu_smbid);
169 		part = chip_part_smbios_get(mod, phys_cpu_smbid);
170 		rev = chip_rev_smbios_get(mod, phys_cpu_smbid);
171 
172 		perr += nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID,
173 		    serial);
174 		perr += nvlist_add_string(fmri, FM_FMRI_HC_PART,
175 		    part);
176 		perr += nvlist_add_string(fmri, FM_FMRI_HC_REVISION,
177 		    rev);
178 
179 		if (perr != 0)
180 			whinge(mod, NULL,
181 			    "create_node: nvlist_add_string failed\n");
182 
183 		topo_mod_strfree(mod, (char *)serial);
184 		topo_mod_strfree(mod, (char *)part);
185 		topo_mod_strfree(mod, (char *)rev);
186 	} else {
187 		char *serial = NULL;
188 
189 		if (nvlist_lookup_string(cpu, FM_PHYSCPU_INFO_CHIP_IDENTSTR,
190 		    &serial) == 0) {
191 			if (nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID,
192 			    serial) != 0) {
193 				whinge(mod, NULL,
194 				    "create_node: nvlist_add_string failed\n");
195 			}
196 		}
197 	}
198 
199 	cnode = topo_node_bind(mod, pnode, name, inst, fmri);
200 
201 	nvlist_free(fmri);
202 	if (cnode == NULL) {
203 		whinge(mod, NULL, "create_node: node bind failed"
204 		    " for %s %d\n", name, (int)inst);
205 	}
206 
207 	return (cnode);
208 }
209 
210 static int
211 create_strand(topo_mod_t *mod, tnode_t *pnode, nvlist_t *cpu,
212     nvlist_t *auth, uint16_t chip_smbiosid)
213 {
214 	tnode_t *strand;
215 	int32_t strandid, cpuid;
216 	int err, perr, nerr = 0;
217 	nvlist_t *fmri;
218 	char *serial = NULL;
219 	char *part = NULL;
220 	char *rev = NULL;
221 
222 	if ((err = nvlist_lookup_int32(cpu, FM_PHYSCPU_INFO_STRAND_ID,
223 	    &strandid)) != 0) {
224 		whinge(mod, NULL, "create_strand: lookup strand_id failed: "
225 		    "%s\n", strerror(err));
226 		return (-1);
227 	}
228 
229 	if ((strand = topo_node_lookup(pnode, STRAND_NODE_NAME, strandid))
230 	    != NULL) {
231 		whinge(mod, NULL, "create_strand: duplicate tuple found\n");
232 		return (-1);
233 	}
234 
235 	if ((strand = create_node(mod, pnode, auth, STRAND_NODE_NAME,
236 	    strandid, cpu, chip_smbiosid)) == NULL)
237 		return (-1);
238 
239 	/*
240 	 * Inherit FRU from core node, in native use cpu scheme ASRU,
241 	 * in xpv, use hc scheme ASRU.
242 	 */
243 	(void) topo_node_fru_set(strand, NULL, 0, &perr);
244 	/*
245 	 * From the inherited FRU, extract the Serial
246 	 * number(if SMBIOS donates) and set it in the ASRU
247 	 */
248 	if (FM_AWARE_SMBIOS(mod)) {
249 		char *val = NULL;
250 
251 		if (topo_prop_get_fmri(strand, TOPO_PGROUP_PROTOCOL,
252 		    TOPO_PROP_RESOURCE, &fmri, &err) != 0)
253 			whinge(mod, NULL,
254 			    "create_strand: topo_prop_get_fmri failed\n");
255 		if (nvlist_lookup_string(fmri, FM_FMRI_HC_SERIAL_ID, &val) != 0)
256 			whinge(mod, NULL,
257 			    "create_strand: nvlist_lookup_string failed: \n");
258 		else
259 			serial = topo_mod_strdup(mod, val);
260 		nvlist_free(fmri);
261 	}
262 	if (is_xpv()) {
263 		if (topo_node_resource(strand, &fmri, &err) == -1) {
264 			whinge(mod, &nerr, "create_strand: "
265 			    "topo_node_resource failed\n");
266 		} else {
267 			if (FM_AWARE_SMBIOS(mod))
268 				(void) nvlist_add_string(fmri,
269 				    FM_FMRI_HC_SERIAL_ID, serial);
270 			(void) topo_node_asru_set(strand, fmri, 0, &err);
271 			nvlist_free(fmri);
272 		}
273 	} else {
274 		if (nvlist_lookup_int32(cpu, STRAND_CPU_ID, &cpuid) != 0) {
275 			whinge(mod, &nerr, "create_strand: lookup cpuid "
276 			    "failed\n");
277 		} else {
278 			if ((fmri = cpu_fmri_create(mod, cpuid, serial, 0))
279 			    != NULL) {
280 				(void) topo_node_asru_set(strand, fmri,
281 				    0, &err);
282 				nvlist_free(fmri);
283 			} else {
284 				whinge(mod, &nerr, "create_strand: "
285 				    "cpu_fmri_create() failed\n");
286 			}
287 		}
288 	}
289 
290 	if (topo_method_register(mod, strand, strands_retire_methods) < 0)
291 		whinge(mod, &nerr, "create_strand: "
292 		    "topo_method_register failed\n");
293 
294 	(void) topo_pgroup_create(strand, &strand_pgroup, &err);
295 	nerr -= add_nvlist_longprops(mod, strand, cpu, PGNAME(STRAND), NULL,
296 	    STRAND_CHIP_ID, STRAND_PROCNODE_ID, STRAND_CORE_ID, STRAND_CPU_ID,
297 	    NULL);
298 
299 	if (FM_AWARE_SMBIOS(mod)) {
300 		(void) topo_node_label_set(strand, NULL, &perr);
301 
302 		if (topo_node_resource(strand, &fmri, &perr) != 0) {
303 			whinge(mod, &nerr, "create_strand: "
304 			    "topo_node_resource failed\n");
305 			perr = 0;
306 		}
307 
308 		perr += nvlist_lookup_string(fmri,
309 		    FM_FMRI_HC_PART, &part);
310 		perr += nvlist_lookup_string(fmri,
311 		    FM_FMRI_HC_REVISION, &rev);
312 
313 		if (perr != 0) {
314 			whinge(mod, NULL,
315 			    "create_strand: nvlist_lookup_string failed\n");
316 			perr = 0;
317 		}
318 
319 		perr += topo_prop_set_string(strand, PGNAME(STRAND),
320 		    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE, serial, &perr);
321 		perr += topo_prop_set_string(strand, PGNAME(STRAND),
322 		    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE, part, &perr);
323 		perr += topo_prop_set_string(strand, PGNAME(STRAND),
324 		    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE, rev, &perr);
325 
326 		if (perr != 0)
327 			whinge(mod, NULL, "create_strand: topo_prop_set_string"
328 			    "failed\n");
329 
330 		nvlist_free(fmri);
331 		topo_mod_strfree(mod, serial);
332 	}
333 
334 	return (err == 0 && nerr == 0 ? 0 : -1);
335 }
336 
337 static int
338 create_core(topo_mod_t *mod, tnode_t *pnode, nvlist_t *cpu,
339     nvlist_t *auth, uint16_t chip_smbiosid)
340 {
341 	tnode_t *core;
342 	int32_t coreid, cpuid;
343 	int err, perr, nerr = 0;
344 	nvlist_t *fmri;
345 	char *serial = NULL;
346 	char *part = NULL;
347 	char *rev = NULL;
348 
349 	if ((err = nvlist_lookup_int32(cpu, FM_PHYSCPU_INFO_CORE_ID, &coreid))
350 	    != 0) {
351 		whinge(mod, NULL, "create_core: lookup core_id failed: %s\n",
352 		    strerror(err));
353 		return (-1);
354 	}
355 	if ((core = topo_node_lookup(pnode, CORE_NODE_NAME, coreid)) == NULL) {
356 		if ((core = create_node(mod, pnode, auth, CORE_NODE_NAME,
357 		    coreid, cpu, chip_smbiosid)) == NULL)
358 			return (-1);
359 
360 		/*
361 		 * Inherit FRU from the chip node, for native, we use hc
362 		 * scheme ASRU for the core node.
363 		 */
364 		(void) topo_node_fru_set(core, NULL, 0, &perr);
365 		/*
366 		 * From the inherited FRU, extract the Serial
367 		 * number if SMBIOS donates and set it in the ASRU
368 		 */
369 		if (FM_AWARE_SMBIOS(mod)) {
370 			char *val = NULL;
371 
372 			if (topo_node_resource(core, &fmri, &err) != 0)
373 				whinge(mod, NULL,
374 				    "create_core: topo_prop_get_fmri failed\n");
375 			if (nvlist_lookup_string(fmri, FM_FMRI_HC_SERIAL_ID,
376 			    &val) != 0)
377 				whinge(mod, NULL, "create_core:"
378 				    "nvlist_lookup_string failed\n");
379 			else
380 				serial = topo_mod_strdup(mod, val);
381 			nvlist_free(fmri);
382 		}
383 		if (is_xpv()) {
384 			if (topo_node_resource(core, &fmri, &err) == -1) {
385 				whinge(mod, &nerr, "create_core: "
386 				    "topo_node_resource failed\n");
387 			} else {
388 				if (FM_AWARE_SMBIOS(mod))
389 					(void) nvlist_add_string(fmri,
390 					    FM_FMRI_HC_SERIAL_ID, serial);
391 				(void) topo_node_asru_set(core, fmri, 0, &err);
392 				nvlist_free(fmri);
393 			}
394 		}
395 		if (topo_method_register(mod, core, strands_retire_methods) < 0)
396 			whinge(mod, &nerr, "create_core: "
397 			    "topo_method_register failed\n");
398 
399 		(void) topo_pgroup_create(core, &core_pgroup, &err);
400 		nerr -= add_nvlist_longprops(mod, core, cpu, PGNAME(CORE), NULL,
401 		    CORE_CHIP_ID, CORE_PROCNODE_ID, NULL);
402 
403 		if (topo_node_range_create(mod, core, STRAND_NODE_NAME,
404 		    0, 255) != 0)
405 			return (-1);
406 
407 		/*
408 		 * Creating a temperature sensor may fail because the sensor
409 		 * doesn't exist or due to internal reasons. At the moment, we
410 		 * swallow any such errors that occur.
411 		 */
412 		(void) chip_create_core_temp_sensor(mod, core);
413 	}
414 
415 	if (!is_xpv()) {
416 		/*
417 		 * In native mode, we're in favor of cpu scheme ASRU for
418 		 * printing reason.  More work needs to be done to support
419 		 * multi-strand cpu: the ASRU will be a list of cpuid then.
420 		 */
421 		if (nvlist_lookup_int32(cpu, STRAND_CPU_ID, &cpuid) != 0) {
422 			whinge(mod, &nerr, "create_core: lookup cpuid "
423 			    "failed\n");
424 		} else {
425 			if ((fmri = cpu_fmri_create(mod, cpuid, serial, 0))
426 			    != NULL) {
427 				(void) topo_node_asru_set(core, fmri, 0, &err);
428 				nvlist_free(fmri);
429 			} else {
430 				whinge(mod, &nerr, "create_core: "
431 				    "cpu_fmri_create() failed\n");
432 			}
433 		}
434 	}
435 
436 	if (FM_AWARE_SMBIOS(mod)) {
437 		(void) topo_node_label_set(core, NULL, &perr);
438 
439 		if (topo_node_resource(core, &fmri, &perr) != 0) {
440 			whinge(mod, &nerr, "create_core: "
441 			    "topo_node_resource failed\n");
442 			perr = 0;
443 		}
444 
445 		perr += nvlist_lookup_string(fmri,
446 		    FM_FMRI_HC_PART, &part);
447 		perr += nvlist_lookup_string(fmri,
448 		    FM_FMRI_HC_REVISION, &rev);
449 
450 		if (perr != 0) {
451 			whinge(mod, NULL,
452 			    "create_core: nvlist_lookup_string failed\n");
453 			perr = 0;
454 		}
455 
456 		perr += topo_prop_set_string(core, PGNAME(CORE),
457 		    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE, serial, &perr);
458 		perr += topo_prop_set_string(core, PGNAME(CORE),
459 		    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE, part, &perr);
460 		perr += topo_prop_set_string(core, PGNAME(CORE),
461 		    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE, rev, &perr);
462 
463 		if (perr != 0)
464 			whinge(mod, NULL, "create_core: topo_prop_set_string"
465 			    "failed\n");
466 
467 		nvlist_free(fmri);
468 		topo_mod_strfree(mod, serial);
469 	}
470 
471 	err = create_strand(mod, core, cpu, auth, chip_smbiosid);
472 
473 	return (err == 0 && nerr == 0 ? 0 : -1);
474 }
475 
476 static int
477 create_chip(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
478     topo_instance_t max, nvlist_t *cpu, nvlist_t *auth,
479     int mc_offchip, kstat_ctl_t *kc)
480 {
481 	tnode_t *chip;
482 	nvlist_t *fmri = NULL;
483 	int err, perr, nerr = 0;
484 	int32_t chipid, procnodeid, procnodes_per_pkg;
485 	const char *vendor, *brand;
486 	int32_t family, model;
487 	boolean_t create_mc = B_FALSE;
488 	uint16_t smbios_id;
489 
490 	/*
491 	 * /dev/fm will export the chipid based on SMBIOS' ordering
492 	 * of Type-4 structures, if SMBIOS meets FMA needs
493 	 */
494 	err = nvlist_lookup_pairs(cpu, 0,
495 	    FM_PHYSCPU_INFO_CHIP_ID, DATA_TYPE_INT32, &chipid,
496 	    FM_PHYSCPU_INFO_NPROCNODES, DATA_TYPE_INT32, &procnodes_per_pkg,
497 	    FM_PHYSCPU_INFO_PROCNODE_ID, DATA_TYPE_INT32, &procnodeid,
498 	    FM_PHYSCPU_INFO_VENDOR_ID, DATA_TYPE_STRING, &vendor,
499 	    FM_PHYSCPU_INFO_FAMILY, DATA_TYPE_INT32, &family,
500 	    FM_PHYSCPU_INFO_MODEL, DATA_TYPE_INT32, &model,
501 	    NULL);
502 
503 	if (err) {
504 		whinge(mod, NULL, "create_chip: lookup failed: %s\n",
505 		    strerror(err));
506 		return (-1);
507 	}
508 
509 	if (chipid < min || chipid > max)
510 		return (-1);
511 
512 	if (FM_AWARE_SMBIOS(mod)) {
513 		if ((err = nvlist_lookup_uint16(cpu,
514 		    FM_PHYSCPU_INFO_SMBIOS_ID, &smbios_id)) != 0) {
515 			whinge(mod, NULL,
516 			    "create_chip: lookup smbios_id failed"
517 			    ": enumerating x86pi & chip topology, but"
518 			    " no Chip properties from SMBIOS"
519 			    " - err msg : %s\n", strerror(err));
520 			/*
521 			 * Lets reset the module specific
522 			 * data to NULL, overriding any
523 			 * SMBIOS capability encoded earlier.
524 			 * This will fail all subsequent
525 			 * FM_AWARE_SMBIOS checks.
526 			 */
527 			topo_mod_setspecific(mod, NULL);
528 		}
529 	}
530 
531 	if ((chip = topo_node_lookup(pnode, CHIP_NODE_NAME, chipid)) == NULL) {
532 		if ((chip = create_node(mod, pnode, auth, CHIP_NODE_NAME,
533 		    chipid, cpu, smbios_id)) == NULL)
534 			return (-1);
535 		/*
536 		 * Do not register XML map methods if SMBIOS can provide
537 		 * serial, part, revision & label
538 		 */
539 		if (!FM_AWARE_SMBIOS(mod)) {
540 			if (topo_method_register(mod, chip, chip_methods) < 0)
541 				whinge(mod, &nerr, "create_chip: "
542 				    "topo_method_register failed\n");
543 		}
544 
545 		(void) topo_pgroup_create(chip, &chip_pgroup, &err);
546 		nerr -= add_nvlist_strprop(mod, chip, cpu, PGNAME(CHIP),
547 		    CHIP_VENDOR_ID, NULL);
548 		nerr -= add_nvlist_longprops(mod, chip, cpu, PGNAME(CHIP),
549 		    NULL, CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL);
550 
551 		/*
552 		 * Attempt to lookup the processor brand string in kstats.
553 		 * and add it as a prop, if found.
554 		 */
555 		brand = get_chip_brand(mod, kc, chipid);
556 		if (brand != NULL && topo_prop_set_string(chip, PGNAME(CHIP),
557 		    CHIP_BRAND, TOPO_PROP_IMMUTABLE, brand, &perr) != 0) {
558 			whinge(mod, &nerr, "failed to set prop %s/%s",
559 			    PGNAME(CHIP), CHIP_BRAND);
560 		}
561 		topo_mod_strfree(mod, (char *)brand);
562 
563 		if (FM_AWARE_SMBIOS(mod)) {
564 			int fru = 0;
565 			char *serial = NULL;
566 			char *part = NULL;
567 			char *rev = NULL;
568 			char *label;
569 
570 			fru = chip_fru_smbios_get(mod, smbios_id);
571 			/*
572 			 * Chip is not a FRU, set the FRU fmri of parent node
573 			 */
574 			if (topo_node_resource(chip, &fmri, &perr) != 0)
575 				whinge(mod, &nerr, "create_chip: "
576 				    "topo_node_resource failed\n");
577 			if (!fru) {
578 				(void) topo_node_fru_set(chip, NULL, 0, &perr);
579 				label = NULL;
580 			} else {
581 				label = (char *)chip_label_smbios_get(mod,
582 				    pnode, smbios_id, NULL);
583 
584 				if (topo_node_fru_set(chip, fmri, 0, &perr)
585 				    != 0) {
586 					whinge(mod, NULL, "create_chip: "
587 					    "topo_node_fru_set failed\n");
588 					perr = 0;
589 				}
590 			}
591 
592 			perr += nvlist_lookup_string(fmri,
593 			    FM_FMRI_HC_SERIAL_ID, &serial);
594 			perr += nvlist_lookup_string(fmri,
595 			    FM_FMRI_HC_PART, &part);
596 			perr += nvlist_lookup_string(fmri,
597 			    FM_FMRI_HC_REVISION, &rev);
598 
599 			if (perr != 0) {
600 				whinge(mod, NULL,
601 				    "create_chip: nvlist_lookup_string"
602 				    "failed\n");
603 				perr = 0;
604 			}
605 
606 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
607 			    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
608 			    serial, &perr);
609 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
610 			    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
611 			    part, &perr);
612 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
613 			    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
614 			    rev, &perr);
615 
616 			if (perr != 0)
617 				whinge(mod, NULL,
618 				    "create_chip: topo_prop_set_string"
619 				    "failed\n");
620 
621 			nvlist_free(fmri);
622 
623 			if (topo_node_label_set(chip, label, &perr)
624 			    == -1) {
625 				whinge(mod, NULL, "create_chip: "
626 				    "topo_node_label_set failed\n");
627 			}
628 			topo_mod_strfree(mod, label);
629 
630 		} else {
631 			if (topo_node_resource(chip, &fmri, &err) == -1) {
632 				whinge(mod, &nerr, "create_chip: "
633 				    "topo_node_resource failed\n");
634 			} else {
635 				(void) topo_node_fru_set(chip, fmri, 0, &perr);
636 				nvlist_free(fmri);
637 			}
638 		}
639 
640 		if (topo_method_register(mod, chip, strands_retire_methods) < 0)
641 			whinge(mod, &nerr, "create_chip: "
642 			    "topo_method_register failed\n");
643 
644 		if (topo_node_range_create(mod, chip, CORE_NODE_NAME, 0, 255))
645 			return (-1);
646 
647 		if (strcmp(vendor, "AuthenticAMD") == 0) {
648 			if (topo_node_range_create(mod, chip, MCT_NODE_NAME,
649 			    0, 255))
650 				return (-1);
651 		}
652 
653 		create_mc = B_TRUE;
654 
655 		/*
656 		 * Creating a temperature sensor may fail because the sensor
657 		 * doesn't exist or due to internal reasons. At the moment, we
658 		 * swallow any such errors that occur.
659 		 */
660 		(void) chip_create_chip_temp_sensor(mod, chip);
661 	}
662 
663 	if (FM_AWARE_SMBIOS(mod)) {
664 		int status = 0;
665 		/*
666 		 * STATUS
667 		 * CPU Socket Populated
668 		 * CPU Socket Unpopulated
669 		 * Populated : Enabled
670 		 * Populated : Disabled by BIOS (Setup)
671 		 * Populated : Disabled by BIOS (Error)
672 		 * Populated : Idle
673 		 *
674 		 * Enumerate core & strand only for Populated : Enabled
675 		 * Enumerate Off-Chip Memory Controller only for
676 		 * Populated : Enabled
677 		 */
678 
679 		status = chip_status_smbios_get(mod, (id_t)smbios_id);
680 		if (!status) {
681 			whinge(mod, NULL, "create_chip: "
682 			    "CPU Socket is not populated or is disabled\n");
683 			return (0);
684 		}
685 	}
686 
687 	err = create_core(mod, chip, cpu, auth, smbios_id);
688 
689 	/*
690 	 * Create memory-controller node under a chip for architectures
691 	 * that may have on-chip memory-controller(s).
692 	 * If SMBIOS meets FMA needs, when Multi-Chip-Module is
693 	 * addressed, mc instances should be derived from SMBIOS
694 	 */
695 	if (strcmp(vendor, "AuthenticAMD") == 0) {
696 		amd_mc_create(mod, smbios_id, chip, MCT_NODE_NAME, auth,
697 		    procnodeid, procnodes_per_pkg, family, model, &nerr);
698 	} else if (create_mc && !mc_offchip)
699 		onchip_mc_create(mod, smbios_id, chip, MCT_NODE_NAME, auth);
700 
701 	return (err == 0 && nerr == 0 ? 0 : -1);
702 }
703 
704 /*ARGSUSED*/
705 static int
706 create_chips(topo_mod_t *mod, tnode_t *pnode, const char *name,
707     topo_instance_t min, topo_instance_t max, void *arg, nvlist_t *auth,
708     int mc_offchip)
709 {
710 	fmd_agent_hdl_t *hdl;
711 	nvlist_t **cpus;
712 	int nerr = 0;
713 	uint_t i, ncpu;
714 	kstat_ctl_t *kc;
715 
716 	if (strcmp(name, CHIP_NODE_NAME) != 0)
717 		return (0);
718 
719 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
720 		return (-1);
721 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) {
722 		whinge(mod, NULL, "create_chip: fmd_agent_physcpu_info "
723 		    "failed: %s\n", fmd_agent_errmsg(hdl));
724 		fmd_agent_close(hdl);
725 		return (-1);
726 	}
727 	fmd_agent_close(hdl);
728 
729 	if ((kc = kstat_open()) == NULL) {
730 		whinge(mod, NULL, "kstat_open() failed");
731 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
732 	}
733 
734 	for (i = 0; i < ncpu; i++) {
735 		nerr -= create_chip(mod, pnode, min, max, cpus[i], auth,
736 		    mc_offchip, kc);
737 		nvlist_free(cpus[i]);
738 	}
739 	(void) kstat_close(kc);
740 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
741 
742 	if (nerr == 0) {
743 		return (0);
744 	} else {
745 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
746 		return (-1);
747 	}
748 }
749 
750 /*ARGSUSED*/
751 static int
752 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
753     topo_instance_t min, topo_instance_t max, void *arg, void *smbios_enabled)
754 {
755 	int rv = 0;
756 	nvlist_t *auth = NULL;
757 	int offchip_mc;
758 	char buf[BUFSIZ];
759 	const char *dom0 = "control_d";
760 
761 	/*
762 	 * Create nothing if we're running in domU.
763 	 */
764 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) == -1)
765 		return (-1);
766 
767 	if (strncmp(buf, "i86pc", sizeof (buf)) != 0 &&
768 	    strncmp(buf, "i86xpv", sizeof (buf)) != 0)
769 		return (0);
770 
771 	if (strncmp(buf, "i86xpv", sizeof (buf)) == 0) {
772 		int fd = open("/dev/xen/domcaps", O_RDONLY);
773 
774 		if (fd != -1) {
775 			if (read(fd, buf, sizeof (buf)) <= 0 ||
776 			    strncmp(buf, dom0, strlen(dom0)) != 0) {
777 				(void) close(fd);
778 				return (0);
779 			}
780 			(void) close(fd);
781 		}
782 	}
783 
784 	/*
785 	 * Set Chip Enumerator Module's private data with the value passed by
786 	 * x86pi Enumerator, defining SMBIOS capabilities
787 	 */
788 	topo_mod_setspecific(mod, smbios_enabled);
789 
790 	if (FM_AWARE_SMBIOS(mod))
791 		if (init_chip_smbios(mod) != 0) {
792 			whinge(mod, NULL,
793 			    "init_chip_smbios() failed, "
794 			    " enumerating x86pi & chip topology, but no"
795 			    " CPU & Memory properties will be"
796 			    " derived from SMBIOS\n");
797 			/*
798 			 * Lets reset the module specific
799 			 * data to NULL, overriding any
800 			 * SMBIOS capability encoded earlier.
801 			 * This will fail all subsequent
802 			 * FM_AWARE_SMBIOS checks.
803 			 */
804 			topo_mod_setspecific(mod, NULL);
805 		}
806 
807 	auth = topo_mod_auth(mod, pnode);
808 
809 	offchip_mc = mc_offchip_open();
810 	if (strcmp(name, CHIP_NODE_NAME) == 0)
811 		rv = create_chips(mod, pnode, name, min, max, NULL, auth,
812 		    offchip_mc);
813 
814 	if (offchip_mc)
815 		(void) mc_offchip_create(mod, pnode, "memory-controller", auth);
816 
817 	nvlist_free(auth);
818 
819 	return (rv);
820 }
821