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 (c) 2018, 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 	if (!is_xpv()) {
409 		/*
410 		 * In native mode, we're in favor of cpu scheme ASRU for
411 		 * printing reason.  More work needs to be done to support
412 		 * multi-strand cpu: the ASRU will be a list of cpuid then.
413 		 */
414 		if (nvlist_lookup_int32(cpu, STRAND_CPU_ID, &cpuid) != 0) {
415 			whinge(mod, &nerr, "create_core: lookup cpuid "
416 			    "failed\n");
417 		} else {
418 			if ((fmri = cpu_fmri_create(mod, cpuid, serial, 0))
419 			    != NULL) {
420 				(void) topo_node_asru_set(core, fmri, 0, &err);
421 				nvlist_free(fmri);
422 			} else {
423 				whinge(mod, &nerr, "create_core: "
424 				    "cpu_fmri_create() failed\n");
425 			}
426 		}
427 	}
428 
429 	if (FM_AWARE_SMBIOS(mod)) {
430 		(void) topo_node_label_set(core, NULL, &perr);
431 
432 		if (topo_node_resource(core, &fmri, &perr) != 0) {
433 			whinge(mod, &nerr, "create_core: "
434 			    "topo_node_resource failed\n");
435 			perr = 0;
436 		}
437 
438 		perr += nvlist_lookup_string(fmri,
439 		    FM_FMRI_HC_PART, &part);
440 		perr += nvlist_lookup_string(fmri,
441 		    FM_FMRI_HC_REVISION, &rev);
442 
443 		if (perr != 0) {
444 			whinge(mod, NULL,
445 			    "create_core: nvlist_lookup_string failed\n");
446 			perr = 0;
447 		}
448 
449 		perr += topo_prop_set_string(core, PGNAME(CORE),
450 		    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE, serial, &perr);
451 		perr += topo_prop_set_string(core, PGNAME(CORE),
452 		    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE, part, &perr);
453 		perr += topo_prop_set_string(core, PGNAME(CORE),
454 		    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE, rev, &perr);
455 
456 		if (perr != 0)
457 			whinge(mod, NULL, "create_core: topo_prop_set_string"
458 			    "failed\n");
459 
460 		nvlist_free(fmri);
461 		topo_mod_strfree(mod, serial);
462 	}
463 
464 	err = create_strand(mod, core, cpu, auth, chip_smbiosid);
465 
466 	return (err == 0 && nerr == 0 ? 0 : -1);
467 }
468 
469 static int
470 create_chip(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
471     topo_instance_t max, nvlist_t *cpu, nvlist_t *auth,
472     int mc_offchip)
473 {
474 	tnode_t *chip;
475 	nvlist_t *fmri = NULL;
476 	int err, perr, nerr = 0;
477 	int32_t chipid, procnodeid, procnodes_per_pkg;
478 	const char *vendor;
479 	int32_t family, model;
480 	boolean_t create_mc = B_FALSE;
481 	uint16_t smbios_id;
482 
483 	/*
484 	 * /dev/fm will export the chipid based on SMBIOS' ordering
485 	 * of Type-4 structures, if SMBIOS meets FMA needs
486 	 */
487 	err = nvlist_lookup_pairs(cpu, 0,
488 	    FM_PHYSCPU_INFO_CHIP_ID, DATA_TYPE_INT32, &chipid,
489 	    FM_PHYSCPU_INFO_NPROCNODES, DATA_TYPE_INT32, &procnodes_per_pkg,
490 	    FM_PHYSCPU_INFO_PROCNODE_ID, DATA_TYPE_INT32, &procnodeid,
491 	    FM_PHYSCPU_INFO_VENDOR_ID, DATA_TYPE_STRING, &vendor,
492 	    FM_PHYSCPU_INFO_FAMILY, DATA_TYPE_INT32, &family,
493 	    FM_PHYSCPU_INFO_MODEL, DATA_TYPE_INT32, &model,
494 	    NULL);
495 
496 	if (err) {
497 		whinge(mod, NULL, "create_chip: lookup failed: %s\n",
498 		    strerror(err));
499 		return (-1);
500 	}
501 
502 	if (chipid < min || chipid > max)
503 		return (-1);
504 
505 	if (FM_AWARE_SMBIOS(mod)) {
506 		if ((err = nvlist_lookup_uint16(cpu,
507 		    FM_PHYSCPU_INFO_SMBIOS_ID, &smbios_id)) != 0) {
508 			whinge(mod, NULL,
509 			    "create_chip: lookup smbios_id failed"
510 			    ": enumerating x86pi & chip topology, but"
511 			    " no Chip properties from SMBIOS"
512 			    " - err msg : %s\n", strerror(err));
513 			/*
514 			 * Lets reset the module specific
515 			 * data to NULL, overriding any
516 			 * SMBIOS capability encoded earlier.
517 			 * This will fail all subsequent
518 			 * FM_AWARE_SMBIOS checks.
519 			 */
520 			topo_mod_setspecific(mod, NULL);
521 		}
522 	}
523 
524 	if ((chip = topo_node_lookup(pnode, CHIP_NODE_NAME, chipid)) == NULL) {
525 		if ((chip = create_node(mod, pnode, auth, CHIP_NODE_NAME,
526 		    chipid, cpu, smbios_id)) == NULL)
527 			return (-1);
528 		/*
529 		 * Do not register XML map methods if SMBIOS can provide
530 		 * serial, part, revision & label
531 		 */
532 		if (!FM_AWARE_SMBIOS(mod)) {
533 			if (topo_method_register(mod, chip, chip_methods) < 0)
534 				whinge(mod, &nerr, "create_chip: "
535 				    "topo_method_register failed\n");
536 		}
537 
538 		(void) topo_pgroup_create(chip, &chip_pgroup, &err);
539 		nerr -= add_nvlist_strprop(mod, chip, cpu, PGNAME(CHIP),
540 		    CHIP_VENDOR_ID, NULL);
541 		nerr -= add_nvlist_longprops(mod, chip, cpu, PGNAME(CHIP),
542 		    NULL, CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL);
543 
544 		if (FM_AWARE_SMBIOS(mod)) {
545 			int fru = 0;
546 			char *serial = NULL;
547 			char *part = NULL;
548 			char *rev = NULL;
549 			char *label;
550 
551 			fru = chip_fru_smbios_get(mod, smbios_id);
552 			/*
553 			 * Chip is not a FRU, set the FRU fmri of parent node
554 			 */
555 			if (topo_node_resource(chip, &fmri, &perr) != 0)
556 				whinge(mod, &nerr, "create_chip: "
557 				    "topo_node_resource failed\n");
558 			if (!fru) {
559 				(void) topo_node_fru_set(chip, NULL, 0, &perr);
560 				label = NULL;
561 			} else {
562 				label = (char *)chip_label_smbios_get(mod,
563 				    pnode, smbios_id, NULL);
564 
565 				if (topo_node_fru_set(chip, fmri, 0, &perr)
566 				    != 0) {
567 					whinge(mod, NULL, "create_chip: "
568 					    "topo_node_fru_set failed\n");
569 					perr = 0;
570 				}
571 			}
572 
573 			perr += nvlist_lookup_string(fmri,
574 			    FM_FMRI_HC_SERIAL_ID, &serial);
575 			perr += nvlist_lookup_string(fmri,
576 			    FM_FMRI_HC_PART, &part);
577 			perr += nvlist_lookup_string(fmri,
578 			    FM_FMRI_HC_REVISION, &rev);
579 
580 			if (perr != 0) {
581 				whinge(mod, NULL,
582 				    "create_chip: nvlist_lookup_string"
583 				    "failed\n");
584 				perr = 0;
585 			}
586 
587 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
588 			    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
589 			    serial, &perr);
590 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
591 			    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
592 			    part, &perr);
593 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
594 			    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
595 			    rev, &perr);
596 
597 			if (perr != 0)
598 				whinge(mod, NULL,
599 				    "create_chip: topo_prop_set_string"
600 				    "failed\n");
601 
602 			nvlist_free(fmri);
603 
604 			if (topo_node_label_set(chip, label, &perr)
605 			    == -1) {
606 				whinge(mod, NULL, "create_chip: "
607 				    "topo_node_label_set failed\n");
608 			}
609 			topo_mod_strfree(mod, label);
610 
611 		} else {
612 			if (topo_node_resource(chip, &fmri, &err) == -1) {
613 				whinge(mod, &nerr, "create_chip: "
614 				    "topo_node_resource failed\n");
615 			} else {
616 				(void) topo_node_fru_set(chip, fmri, 0, &perr);
617 				nvlist_free(fmri);
618 			}
619 		}
620 
621 		if (topo_method_register(mod, chip, strands_retire_methods) < 0)
622 			whinge(mod, &nerr, "create_chip: "
623 			    "topo_method_register failed\n");
624 
625 		if (topo_node_range_create(mod, chip, CORE_NODE_NAME, 0, 255))
626 			return (-1);
627 
628 		if (strcmp(vendor, "AuthenticAMD") == 0) {
629 			if (topo_node_range_create(mod, chip, MCT_NODE_NAME,
630 			    0, 255))
631 				return (-1);
632 		}
633 
634 		create_mc = B_TRUE;
635 	}
636 
637 	if (FM_AWARE_SMBIOS(mod)) {
638 		int status = 0;
639 		/*
640 		 * STATUS
641 		 * CPU Socket Populated
642 		 * CPU Socket Unpopulated
643 		 * Populated : Enabled
644 		 * Populated : Disabled by BIOS (Setup)
645 		 * Populated : Disabled by BIOS (Error)
646 		 * Populated : Idle
647 		 *
648 		 * Enumerate core & strand only for Populated : Enabled
649 		 * Enumerate Off-Chip Memory Controller only for
650 		 * Populated : Enabled
651 		 */
652 
653 		status = chip_status_smbios_get(mod, (id_t)smbios_id);
654 		if (!status) {
655 			whinge(mod, NULL, "create_chip: "
656 			    "CPU Socket is not populated or is disabled\n");
657 			return (0);
658 		}
659 	}
660 
661 	err = create_core(mod, chip, cpu, auth, smbios_id);
662 
663 	/*
664 	 * Create memory-controller node under a chip for architectures
665 	 * that may have on-chip memory-controller(s).
666 	 * If SMBIOS meets FMA needs, when Multi-Chip-Module is
667 	 * addressed, mc instances should be derived from SMBIOS
668 	 */
669 	if (strcmp(vendor, "AuthenticAMD") == 0) {
670 		amd_mc_create(mod, smbios_id, chip, MCT_NODE_NAME, auth,
671 		    procnodeid, procnodes_per_pkg, family, model, &nerr);
672 	} else if (create_mc && !mc_offchip)
673 		onchip_mc_create(mod, smbios_id, chip, MCT_NODE_NAME, auth);
674 
675 	return (err == 0 && nerr == 0 ? 0 : -1);
676 }
677 
678 /*ARGSUSED*/
679 static int
680 create_chips(topo_mod_t *mod, tnode_t *pnode, const char *name,
681     topo_instance_t min, topo_instance_t max, void *arg, nvlist_t *auth,
682     int mc_offchip)
683 {
684 	fmd_agent_hdl_t *hdl;
685 	nvlist_t **cpus;
686 	int nerr = 0;
687 	uint_t i, ncpu;
688 
689 	if (strcmp(name, CHIP_NODE_NAME) != 0)
690 		return (0);
691 
692 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
693 		return (-1);
694 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) {
695 		whinge(mod, NULL, "create_chip: fmd_agent_physcpu_info "
696 		    "failed: %s\n", fmd_agent_errmsg(hdl));
697 		fmd_agent_close(hdl);
698 		return (-1);
699 	}
700 	fmd_agent_close(hdl);
701 
702 	for (i = 0; i < ncpu; i++) {
703 		nerr -= create_chip(mod, pnode, min, max, cpus[i], auth,
704 		    mc_offchip);
705 		nvlist_free(cpus[i]);
706 	}
707 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
708 
709 	if (nerr == 0) {
710 		return (0);
711 	} else {
712 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
713 		return (-1);
714 	}
715 }
716 
717 /*ARGSUSED*/
718 static int
719 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
720     topo_instance_t min, topo_instance_t max, void *arg, void *smbios_enabled)
721 {
722 	int rv = 0;
723 	nvlist_t *auth = NULL;
724 	int offchip_mc;
725 	char buf[BUFSIZ];
726 	const char *dom0 = "control_d";
727 
728 	/*
729 	 * Create nothing if we're running in domU.
730 	 */
731 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) == -1)
732 		return (-1);
733 
734 	if (strncmp(buf, "i86pc", sizeof (buf)) != 0 &&
735 	    strncmp(buf, "i86xpv", sizeof (buf)) != 0)
736 		return (0);
737 
738 	if (strncmp(buf, "i86xpv", sizeof (buf)) == 0) {
739 		int fd = open("/dev/xen/domcaps", O_RDONLY);
740 
741 		if (fd != -1) {
742 			if (read(fd, buf, sizeof (buf)) <= 0 ||
743 			    strncmp(buf, dom0, strlen(dom0)) != 0) {
744 				(void) close(fd);
745 				return (0);
746 			}
747 			(void) close(fd);
748 		}
749 	}
750 
751 	/*
752 	 * Set Chip Enumerator Module's private data with the value passed by
753 	 * x86pi Enumerator, defining SMBIOS capabilities
754 	 */
755 	topo_mod_setspecific(mod, smbios_enabled);
756 
757 	if (FM_AWARE_SMBIOS(mod))
758 		if (init_chip_smbios(mod) != 0) {
759 			whinge(mod, NULL,
760 			    "init_chip_smbios() failed, "
761 			    " enumerating x86pi & chip topology, but no"
762 			    " CPU & Memory properties will be"
763 			    " derived from SMBIOS\n");
764 			/*
765 			 * Lets reset the module specific
766 			 * data to NULL, overriding any
767 			 * SMBIOS capability encoded earlier.
768 			 * This will fail all subsequent
769 			 * FM_AWARE_SMBIOS checks.
770 			 */
771 			topo_mod_setspecific(mod, NULL);
772 		}
773 
774 	auth = topo_mod_auth(mod, pnode);
775 
776 	offchip_mc = mc_offchip_open();
777 	if (strcmp(name, CHIP_NODE_NAME) == 0)
778 		rv = create_chips(mod, pnode, name, min, max, NULL, auth,
779 		    offchip_mc);
780 
781 	if (offchip_mc)
782 		(void) mc_offchip_create(mod, pnode, "memory-controller", auth);
783 
784 	nvlist_free(auth);
785 
786 	return (rv);
787 }
788