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  */
26 
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <limits.h>
35 #include <alloca.h>
36 #include <kstat.h>
37 #include <errno.h>
38 #include <libnvpair.h>
39 #include <sys/types.h>
40 #include <sys/bitmap.h>
41 #include <sys/processor.h>
42 #include <sys/param.h>
43 #include <sys/fm/protocol.h>
44 #include <sys/systeminfo.h>
45 #include <sys/mc.h>
46 #include <sys/mc_amd.h>
47 #include <sys/mc_intel.h>
48 #include <sys/devfm.h>
49 #include <fm/fmd_agent.h>
50 #include <fm/topo_mod.h>
51 
52 #include "chip.h"
53 
54 #define	MAX_DIMMNUM	7
55 #define	MAX_CSNUM	7
56 
57 /*
58  * Enumerates the processing chips, or sockets, (as distinct from cores) in a
59  * system.  For each chip found, the necessary nodes (one or more cores, and
60  * possibly a memory controller) are constructed underneath.
61  */
62 
63 static int chip_enum(topo_mod_t *, tnode_t *, const char *,
64     topo_instance_t, topo_instance_t, void *, void *);
65 
66 static const topo_modops_t chip_ops =
67 	{ chip_enum, NULL};
68 static const topo_modinfo_t chip_info =
69 	{ CHIP_NODE_NAME, FM_FMRI_SCHEME_HC, CHIP_VERSION, &chip_ops };
70 
71 static const topo_pgroup_info_t chip_pgroup =
72 	{ PGNAME(CHIP), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
73 
74 static const topo_pgroup_info_t core_pgroup =
75 	{ PGNAME(CORE), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
76 
77 static const topo_pgroup_info_t strand_pgroup =
78 	{ PGNAME(STRAND), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
79 
80 static const topo_method_t chip_methods[] = {
81 	{ SIMPLE_CHIP_LBL, "Property method", 0,
82 	    TOPO_STABILITY_INTERNAL, simple_chip_label},
83 	{ G4_CHIP_LBL, "Property method", 0,
84 	    TOPO_STABILITY_INTERNAL, g4_chip_label},
85 	{ A4FPLUS_CHIP_LBL, "Property method", 0,
86 	    TOPO_STABILITY_INTERNAL, a4fplus_chip_label},
87 	{ FSB2_CHIP_LBL, "Property method", 0,
88 	    TOPO_STABILITY_INTERNAL, fsb2_chip_label},
89 	{ NULL }
90 };
91 
92 static const topo_method_t strands_retire_methods[] = {
93 	{ TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC,
94 	    TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL,
95 	    retire_strands },
96 	{ TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC,
97 	    TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL,
98 	    unretire_strands },
99 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
100 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
101 	    service_state_strands },
102 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
103 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
104 	    unusable_strands },
105 	{ NULL }
106 };
107 
108 int
109 _topo_init(topo_mod_t *mod)
110 {
111 	if (getenv("TOPOCHIPDBG"))
112 		topo_mod_setdebug(mod);
113 	topo_mod_dprintf(mod, "initializing chip enumerator\n");
114 
115 	if (topo_mod_register(mod, &chip_info, TOPO_VERSION) != 0) {
116 		whinge(mod, NULL, "failed to register hc: "
117 		    "%s\n", topo_mod_errmsg(mod));
118 		return (-1); /* mod errno set */
119 	}
120 
121 	return (0);
122 }
123 
124 void
125 _topo_fini(topo_mod_t *mod)
126 {
127 	topo_mod_unregister(mod);
128 }
129 
130 boolean_t
131 is_xpv(void)
132 {
133 	static int r = -1;
134 	char platform[MAXNAMELEN];
135 
136 	if (r != -1)
137 		return (r == 0);
138 
139 	(void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
140 	r = strcmp(platform, "i86xpv");
141 	return (r == 0);
142 }
143 
144 static tnode_t *
145 create_node(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, char *name,
146     topo_instance_t inst)
147 {
148 	nvlist_t *fmri;
149 	tnode_t *cnode;
150 
151 	if (mkrsrc(mod, pnode, name, inst, auth, &fmri) != 0) {
152 		whinge(mod, NULL, "create_node: mkrsrc failed\n");
153 		return (NULL);
154 	}
155 	cnode = topo_node_bind(mod, pnode, name, inst, fmri);
156 	nvlist_free(fmri);
157 	if (cnode == NULL)
158 		whinge(mod, NULL, "create_node: node bind failed for %s %d\n",
159 		    name, (int)inst);
160 
161 	return (cnode);
162 }
163 
164 static int
165 create_strand(topo_mod_t *mod, tnode_t *pnode, nvlist_t *cpu, nvlist_t *auth)
166 {
167 	tnode_t *strand;
168 	int32_t strandid, cpuid;
169 	int err, nerr = 0;
170 	nvlist_t *fmri;
171 
172 	if ((err = nvlist_lookup_int32(cpu, FM_PHYSCPU_INFO_STRAND_ID,
173 	    &strandid)) != 0) {
174 		whinge(mod, NULL, "create_strand: lookup strand_id failed: "
175 		    "%s\n", strerror(err));
176 		return (-1);
177 	}
178 
179 	if ((strand = topo_node_lookup(pnode, STRAND_NODE_NAME, strandid))
180 	    != NULL) {
181 		whinge(mod, NULL, "create_strand: duplicate tuple found\n");
182 		return (-1);
183 	}
184 
185 	if ((strand = create_node(mod, pnode, auth, STRAND_NODE_NAME,
186 	    strandid)) == NULL)
187 		return (-1);
188 
189 	/*
190 	 * Inherit FRU from core node, in native use cpu scheme ASRU,
191 	 * in xpv, use hc scheme ASRU.
192 	 */
193 	(void) topo_node_fru_set(strand, NULL, 0, &err);
194 	if (is_xpv()) {
195 		if (topo_node_resource(strand, &fmri, &err) == -1) {
196 			whinge(mod, &nerr, "create_strand: "
197 			    "topo_node_resource failed\n");
198 		} else {
199 			(void) topo_node_asru_set(strand, fmri, 0, &err);
200 			nvlist_free(fmri);
201 		}
202 	} else {
203 		if (nvlist_lookup_int32(cpu, STRAND_CPU_ID, &cpuid) != 0) {
204 			whinge(mod, &nerr, "create_strand: lookup cpuid "
205 			    "failed\n");
206 		} else {
207 			if ((fmri = cpu_fmri_create(mod, cpuid, NULL, 0))
208 			    != NULL) {
209 				(void) topo_node_asru_set(strand, fmri,
210 				    0, &err);
211 				nvlist_free(fmri);
212 			} else {
213 				whinge(mod, &nerr, "create_strand: "
214 				    "cpu_fmri_create() failed\n");
215 			}
216 		}
217 	}
218 
219 	if (topo_method_register(mod, strand, strands_retire_methods) < 0)
220 		whinge(mod, &nerr, "create_strand: "
221 		    "topo_method_register failed\n");
222 
223 	(void) topo_pgroup_create(strand, &strand_pgroup, &err);
224 	nerr -= add_nvlist_longprops(mod, strand, cpu, PGNAME(STRAND), NULL,
225 	    STRAND_CHIP_ID, STRAND_CORE_ID, STRAND_CPU_ID, NULL);
226 
227 	return (err == 0 && nerr == 0 ? 0 : -1);
228 }
229 
230 static int
231 create_core(topo_mod_t *mod, tnode_t *pnode, nvlist_t *cpu, nvlist_t *auth)
232 {
233 	tnode_t *core;
234 	int32_t coreid, cpuid;
235 	int err, nerr = 0;
236 	nvlist_t *fmri;
237 
238 	if ((err = nvlist_lookup_int32(cpu, FM_PHYSCPU_INFO_CORE_ID, &coreid))
239 	    != 0) {
240 		whinge(mod, NULL, "create_core: lookup core_id failed: %s\n",
241 		    strerror(err));
242 		return (-1);
243 	}
244 	if ((core = topo_node_lookup(pnode, CORE_NODE_NAME, coreid)) == NULL) {
245 		if ((core = create_node(mod, pnode, auth, CORE_NODE_NAME,
246 		    coreid)) == NULL)
247 			return (-1);
248 
249 		/*
250 		 * Inherit FRU from the chip node, for native, we use hc
251 		 * scheme ASRU for the core node.
252 		 */
253 		(void) topo_node_fru_set(core, NULL, 0, &err);
254 		if (is_xpv()) {
255 			if (topo_node_resource(core, &fmri, &err) == -1) {
256 				whinge(mod, &nerr, "create_core: "
257 				    "topo_node_resource failed\n");
258 			} else {
259 				(void) topo_node_asru_set(core, fmri, 0, &err);
260 				nvlist_free(fmri);
261 			}
262 		}
263 		if (topo_method_register(mod, core, strands_retire_methods) < 0)
264 			whinge(mod, &nerr, "create_core: "
265 			    "topo_method_register failed\n");
266 
267 		(void) topo_pgroup_create(core, &core_pgroup, &err);
268 		nerr -= add_nvlist_longprop(mod, core, cpu, PGNAME(CORE),
269 		    CORE_CHIP_ID, NULL);
270 
271 		if (topo_node_range_create(mod, core, STRAND_NODE_NAME,
272 		    0, 255) != 0)
273 			return (-1);
274 	}
275 
276 	if (! is_xpv()) {
277 		/*
278 		 * In native mode, we're in favor of cpu scheme ASRU for
279 		 * printing reason.  More work needs to be done to support
280 		 * multi-strand cpu: the ASRU will be a list of cpuid then.
281 		 */
282 		if (nvlist_lookup_int32(cpu, STRAND_CPU_ID, &cpuid) != 0) {
283 			whinge(mod, &nerr, "create_core: lookup cpuid "
284 			    "failed\n");
285 		} else {
286 			if ((fmri = cpu_fmri_create(mod, cpuid, NULL, 0))
287 			    != NULL) {
288 				(void) topo_node_asru_set(core, fmri, 0, &err);
289 				nvlist_free(fmri);
290 			} else {
291 				whinge(mod, &nerr, "create_core: "
292 				    "cpu_fmri_create() failed\n");
293 			}
294 		}
295 	}
296 
297 	err = create_strand(mod, core, cpu, auth);
298 
299 	return (err == 0 && nerr == 0 ? 0 : -1);
300 }
301 
302 static int
303 create_chip(topo_mod_t *mod, tnode_t *pnode, topo_instance_t min,
304     topo_instance_t max, nvlist_t *cpu, nvlist_t *auth,
305     int mc_offchip)
306 {
307 	tnode_t *chip;
308 	int32_t chipid;
309 	nvlist_t *fmri = NULL;
310 	int err, nerr = 0;
311 	int32_t fms[3];
312 	const char *vendor;
313 	boolean_t create_mc = B_FALSE;
314 
315 	if ((err = nvlist_lookup_int32(cpu, FM_PHYSCPU_INFO_CHIP_ID, &chipid))
316 	    != 0) {
317 		whinge(mod, NULL, "create_chip: lookup chip_id failed: %s\n",
318 		    strerror(err));
319 		return (-1);
320 	}
321 
322 	if (chipid < min || chipid > max)
323 		return (-1);
324 
325 	if ((chip = topo_node_lookup(pnode, CHIP_NODE_NAME, chipid)) == NULL) {
326 		if ((chip = create_node(mod, pnode, auth, CHIP_NODE_NAME,
327 		    chipid)) == NULL)
328 			return (-1);
329 
330 		if (topo_method_register(mod, chip, chip_methods) < 0)
331 			whinge(mod, &nerr, "create_chip: "
332 			    "topo_method_register failed\n");
333 
334 		if (topo_node_resource(chip, &fmri, &err) == -1) {
335 			whinge(mod, &nerr, "create_chip: "
336 			    "topo_node_resource failed\n");
337 		} else {
338 			(void) topo_node_fru_set(chip, fmri, 0, &err);
339 			nvlist_free(fmri);
340 		}
341 
342 		(void) topo_pgroup_create(chip, &chip_pgroup, &err);
343 		nerr -= add_nvlist_strprop(mod, chip, cpu, PGNAME(CHIP),
344 		    CHIP_VENDOR_ID, &vendor);
345 		nerr -= add_nvlist_longprops(mod, chip, cpu, PGNAME(CHIP),
346 		    fms, CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL);
347 
348 		if (topo_method_register(mod, chip, strands_retire_methods) < 0)
349 			whinge(mod, &nerr, "create_chip: "
350 			    "topo_method_register failed\n");
351 
352 		if (topo_node_range_create(mod, chip, CORE_NODE_NAME,
353 		    0, 255) != 0)
354 			return (-1);
355 
356 		create_mc = B_TRUE;
357 	}
358 
359 	err = create_core(mod, chip, cpu, auth);
360 
361 	/*
362 	 * Create memory-controller node under a chip for architectures
363 	 * that may have on-chip memory-controller(s).
364 	 */
365 	if (create_mc) {
366 		if (strcmp(vendor, "AuthenticAMD") == 0)
367 			amd_mc_create(mod, chip, MCT_NODE_NAME, auth,
368 			    fms[0], fms[1], fms[2], &nerr);
369 		else if (!mc_offchip)
370 			onchip_mc_create(mod, chip, MCT_NODE_NAME, auth);
371 	}
372 
373 	return (err == 0 && nerr == 0 ? 0 : -1);
374 }
375 
376 /*ARGSUSED*/
377 static int
378 create_chips(topo_mod_t *mod, tnode_t *pnode, const char *name,
379     topo_instance_t min, topo_instance_t max, void *arg, nvlist_t *auth,
380     int mc_offchip)
381 {
382 	fmd_agent_hdl_t *hdl;
383 	nvlist_t **cpus;
384 	int nerr = 0;
385 	uint_t i, ncpu;
386 
387 	if (strcmp(name, CHIP_NODE_NAME) != 0)
388 		return (0);
389 
390 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
391 		return (-1);
392 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) {
393 		whinge(mod, NULL, "create_chip: fmd_agent_physcpu_info "
394 		    "failed: %s\n", fmd_agent_errmsg(hdl));
395 		fmd_agent_close(hdl);
396 		return (-1);
397 	}
398 	fmd_agent_close(hdl);
399 
400 	for (i = 0; i < ncpu; i++) {
401 		nerr -= create_chip(mod, pnode, min, max, cpus[i], auth,
402 		    mc_offchip);
403 		nvlist_free(cpus[i]);
404 	}
405 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
406 
407 	if (nerr == 0) {
408 		return (0);
409 	} else {
410 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
411 		return (-1);
412 	}
413 }
414 
415 /*ARGSUSED*/
416 static int
417 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
418     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
419 {
420 	int rv = 0;
421 	nvlist_t *auth = NULL;
422 	int offchip_mc;
423 	char buf[BUFSIZ];
424 	const char *dom0 = "control_d";
425 
426 	/*
427 	 * Create nothing if we're running in domU.
428 	 */
429 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) == -1)
430 		return (-1);
431 
432 	if (strncmp(buf, "i86pc", sizeof (buf)) != 0 &&
433 	    strncmp(buf, "i86xpv", sizeof (buf)) != 0)
434 		return (0);
435 
436 	if (strncmp(buf, "i86xpv", sizeof (buf)) == 0) {
437 		int fd = open("/dev/xen/domcaps", O_RDONLY);
438 
439 		if (fd != -1) {
440 			if (read(fd, buf, sizeof (buf)) <= 0 ||
441 			    strncmp(buf, dom0, strlen(dom0)) != 0) {
442 				(void) close(fd);
443 				return (0);
444 			}
445 			(void) close(fd);
446 		}
447 	}
448 
449 	auth = topo_mod_auth(mod, pnode);
450 
451 	offchip_mc = mc_offchip_open();
452 	if (strcmp(name, CHIP_NODE_NAME) == 0)
453 		rv = create_chips(mod, pnode, name, min, max, NULL, auth,
454 		    offchip_mc);
455 
456 	if (offchip_mc)
457 		(void) mc_offchip_create(mod, pnode, "memory-controller", auth);
458 
459 	nvlist_free(auth);
460 
461 	return (rv);
462 }
463