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, kstat_ctl_t *kc)
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, *brand;
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 		/*
545 		 * Attempt to lookup the processor brand string in kstats.
546 		 * and add it as a prop, if found.
547 		 */
548 		brand = get_chip_brand(mod, kc, chipid);
549 		if (brand != NULL && topo_prop_set_string(chip, PGNAME(CHIP),
550 		    CHIP_BRAND, TOPO_PROP_IMMUTABLE, brand, &perr) != 0) {
551 			whinge(mod, &nerr, "failed to set prop %s/%s",
552 			    PGNAME(CHIP), CHIP_BRAND);
553 		}
554 		topo_mod_strfree(mod, (char *)brand);
555 
556 		if (FM_AWARE_SMBIOS(mod)) {
557 			int fru = 0;
558 			char *serial = NULL;
559 			char *part = NULL;
560 			char *rev = NULL;
561 			char *label;
562 
563 			fru = chip_fru_smbios_get(mod, smbios_id);
564 			/*
565 			 * Chip is not a FRU, set the FRU fmri of parent node
566 			 */
567 			if (topo_node_resource(chip, &fmri, &perr) != 0)
568 				whinge(mod, &nerr, "create_chip: "
569 				    "topo_node_resource failed\n");
570 			if (!fru) {
571 				(void) topo_node_fru_set(chip, NULL, 0, &perr);
572 				label = NULL;
573 			} else {
574 				label = (char *)chip_label_smbios_get(mod,
575 				    pnode, smbios_id, NULL);
576 
577 				if (topo_node_fru_set(chip, fmri, 0, &perr)
578 				    != 0) {
579 					whinge(mod, NULL, "create_chip: "
580 					    "topo_node_fru_set failed\n");
581 					perr = 0;
582 				}
583 			}
584 
585 			perr += nvlist_lookup_string(fmri,
586 			    FM_FMRI_HC_SERIAL_ID, &serial);
587 			perr += nvlist_lookup_string(fmri,
588 			    FM_FMRI_HC_PART, &part);
589 			perr += nvlist_lookup_string(fmri,
590 			    FM_FMRI_HC_REVISION, &rev);
591 
592 			if (perr != 0) {
593 				whinge(mod, NULL,
594 				    "create_chip: nvlist_lookup_string"
595 				    "failed\n");
596 				perr = 0;
597 			}
598 
599 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
600 			    FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE,
601 			    serial, &perr);
602 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
603 			    FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE,
604 			    part, &perr);
605 			perr += topo_prop_set_string(chip, PGNAME(CHIP),
606 			    FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE,
607 			    rev, &perr);
608 
609 			if (perr != 0)
610 				whinge(mod, NULL,
611 				    "create_chip: topo_prop_set_string"
612 				    "failed\n");
613 
614 			nvlist_free(fmri);
615 
616 			if (topo_node_label_set(chip, label, &perr)
617 			    == -1) {
618 				whinge(mod, NULL, "create_chip: "
619 				    "topo_node_label_set failed\n");
620 			}
621 			topo_mod_strfree(mod, label);
622 
623 		} else {
624 			if (topo_node_resource(chip, &fmri, &err) == -1) {
625 				whinge(mod, &nerr, "create_chip: "
626 				    "topo_node_resource failed\n");
627 			} else {
628 				(void) topo_node_fru_set(chip, fmri, 0, &perr);
629 				nvlist_free(fmri);
630 			}
631 		}
632 
633 		if (topo_method_register(mod, chip, strands_retire_methods) < 0)
634 			whinge(mod, &nerr, "create_chip: "
635 			    "topo_method_register failed\n");
636 
637 		if (topo_node_range_create(mod, chip, CORE_NODE_NAME, 0, 255))
638 			return (-1);
639 
640 		if (strcmp(vendor, "AuthenticAMD") == 0) {
641 			if (topo_node_range_create(mod, chip, MCT_NODE_NAME,
642 			    0, 255))
643 				return (-1);
644 		}
645 
646 		create_mc = B_TRUE;
647 	}
648 
649 	if (FM_AWARE_SMBIOS(mod)) {
650 		int status = 0;
651 		/*
652 		 * STATUS
653 		 * CPU Socket Populated
654 		 * CPU Socket Unpopulated
655 		 * Populated : Enabled
656 		 * Populated : Disabled by BIOS (Setup)
657 		 * Populated : Disabled by BIOS (Error)
658 		 * Populated : Idle
659 		 *
660 		 * Enumerate core & strand only for Populated : Enabled
661 		 * Enumerate Off-Chip Memory Controller only for
662 		 * Populated : Enabled
663 		 */
664 
665 		status = chip_status_smbios_get(mod, (id_t)smbios_id);
666 		if (!status) {
667 			whinge(mod, NULL, "create_chip: "
668 			    "CPU Socket is not populated or is disabled\n");
669 			return (0);
670 		}
671 	}
672 
673 	err = create_core(mod, chip, cpu, auth, smbios_id);
674 
675 	/*
676 	 * Create memory-controller node under a chip for architectures
677 	 * that may have on-chip memory-controller(s).
678 	 * If SMBIOS meets FMA needs, when Multi-Chip-Module is
679 	 * addressed, mc instances should be derived from SMBIOS
680 	 */
681 	if (strcmp(vendor, "AuthenticAMD") == 0) {
682 		amd_mc_create(mod, smbios_id, chip, MCT_NODE_NAME, auth,
683 		    procnodeid, procnodes_per_pkg, family, model, &nerr);
684 	} else if (create_mc && !mc_offchip)
685 		onchip_mc_create(mod, smbios_id, chip, MCT_NODE_NAME, auth);
686 
687 	return (err == 0 && nerr == 0 ? 0 : -1);
688 }
689 
690 /*ARGSUSED*/
691 static int
692 create_chips(topo_mod_t *mod, tnode_t *pnode, const char *name,
693     topo_instance_t min, topo_instance_t max, void *arg, nvlist_t *auth,
694     int mc_offchip)
695 {
696 	fmd_agent_hdl_t *hdl;
697 	nvlist_t **cpus;
698 	int nerr = 0;
699 	uint_t i, ncpu;
700 	kstat_ctl_t *kc;
701 
702 	if (strcmp(name, CHIP_NODE_NAME) != 0)
703 		return (0);
704 
705 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
706 		return (-1);
707 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) {
708 		whinge(mod, NULL, "create_chip: fmd_agent_physcpu_info "
709 		    "failed: %s\n", fmd_agent_errmsg(hdl));
710 		fmd_agent_close(hdl);
711 		return (-1);
712 	}
713 	fmd_agent_close(hdl);
714 
715 	if ((kc = kstat_open()) == NULL) {
716 		whinge(mod, NULL, "kstat_open() failed");
717 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
718 	}
719 
720 	for (i = 0; i < ncpu; i++) {
721 		nerr -= create_chip(mod, pnode, min, max, cpus[i], auth,
722 		    mc_offchip, kc);
723 		nvlist_free(cpus[i]);
724 	}
725 	(void) kstat_close(kc);
726 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
727 
728 	if (nerr == 0) {
729 		return (0);
730 	} else {
731 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
732 		return (-1);
733 	}
734 }
735 
736 /*ARGSUSED*/
737 static int
738 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
739     topo_instance_t min, topo_instance_t max, void *arg, void *smbios_enabled)
740 {
741 	int rv = 0;
742 	nvlist_t *auth = NULL;
743 	int offchip_mc;
744 	char buf[BUFSIZ];
745 	const char *dom0 = "control_d";
746 
747 	/*
748 	 * Create nothing if we're running in domU.
749 	 */
750 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) == -1)
751 		return (-1);
752 
753 	if (strncmp(buf, "i86pc", sizeof (buf)) != 0 &&
754 	    strncmp(buf, "i86xpv", sizeof (buf)) != 0)
755 		return (0);
756 
757 	if (strncmp(buf, "i86xpv", sizeof (buf)) == 0) {
758 		int fd = open("/dev/xen/domcaps", O_RDONLY);
759 
760 		if (fd != -1) {
761 			if (read(fd, buf, sizeof (buf)) <= 0 ||
762 			    strncmp(buf, dom0, strlen(dom0)) != 0) {
763 				(void) close(fd);
764 				return (0);
765 			}
766 			(void) close(fd);
767 		}
768 	}
769 
770 	/*
771 	 * Set Chip Enumerator Module's private data with the value passed by
772 	 * x86pi Enumerator, defining SMBIOS capabilities
773 	 */
774 	topo_mod_setspecific(mod, smbios_enabled);
775 
776 	if (FM_AWARE_SMBIOS(mod))
777 		if (init_chip_smbios(mod) != 0) {
778 			whinge(mod, NULL,
779 			    "init_chip_smbios() failed, "
780 			    " enumerating x86pi & chip topology, but no"
781 			    " CPU & Memory properties will be"
782 			    " derived from SMBIOS\n");
783 			/*
784 			 * Lets reset the module specific
785 			 * data to NULL, overriding any
786 			 * SMBIOS capability encoded earlier.
787 			 * This will fail all subsequent
788 			 * FM_AWARE_SMBIOS checks.
789 			 */
790 			topo_mod_setspecific(mod, NULL);
791 		}
792 
793 	auth = topo_mod_auth(mod, pnode);
794 
795 	offchip_mc = mc_offchip_open();
796 	if (strcmp(name, CHIP_NODE_NAME) == 0)
797 		rv = create_chips(mod, pnode, name, min, max, NULL, auth,
798 		    offchip_mc);
799 
800 	if (offchip_mc)
801 		(void) mc_offchip_create(mod, pnode, "memory-controller", auth);
802 
803 	nvlist_free(auth);
804 
805 	return (rv);
806 }
807