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 2008 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 = NULL;
313 
314 	if ((err = nvlist_lookup_int32(cpu, FM_PHYSCPU_INFO_CHIP_ID, &chipid))
315 	    != 0) {
316 		whinge(mod, NULL, "create_chip: lookup chip_id failed: %s\n",
317 		    strerror(err));
318 		return (-1);
319 	}
320 
321 	if (chipid < min || chipid > max)
322 		return (-1);
323 
324 	if ((chip = topo_node_lookup(pnode, CHIP_NODE_NAME, chipid)) == NULL) {
325 		if ((chip = create_node(mod, pnode, auth, CHIP_NODE_NAME,
326 		    chipid)) == NULL)
327 			return (-1);
328 
329 		if (topo_method_register(mod, chip, chip_methods) < 0)
330 			whinge(mod, &nerr, "create_chip: "
331 			    "topo_method_register failed\n");
332 
333 		if (topo_node_resource(chip, &fmri, &err) == -1) {
334 			whinge(mod, &nerr, "create_chip: "
335 			    "topo_node_resource failed\n");
336 		} else {
337 			(void) topo_node_fru_set(chip, fmri, 0, &err);
338 			nvlist_free(fmri);
339 		}
340 
341 		(void) topo_pgroup_create(chip, &chip_pgroup, &err);
342 		nerr -= add_nvlist_strprop(mod, chip, cpu, PGNAME(CHIP),
343 		    CHIP_VENDOR_ID, &vendor);
344 		nerr -= add_nvlist_longprops(mod, chip, cpu, PGNAME(CHIP),
345 		    fms, CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL);
346 
347 		if (topo_method_register(mod, chip, strands_retire_methods) < 0)
348 			whinge(mod, &nerr, "create_chip: "
349 			    "topo_method_register failed\n");
350 
351 		if (topo_node_range_create(mod, chip, CORE_NODE_NAME,
352 		    0, 255) != 0)
353 			return (-1);
354 	}
355 
356 	err = create_core(mod, chip, cpu, auth);
357 
358 	/*
359 	 * Create memory-controller node under a chip for architectures
360 	 * that may have on-chip memory-controller(s).
361 	 */
362 	if (vendor != NULL && strcmp(vendor, "AuthenticAMD") == 0)
363 		amd_mc_create(mod, chip, MCT_NODE_NAME, auth,
364 		    fms[0], fms[1], fms[2], &nerr);
365 	else if (!mc_offchip)
366 		onchip_mc_create(mod, chip, MCT_NODE_NAME, auth);
367 
368 	return (err == 0 && nerr == 0 ? 0 : -1);
369 }
370 
371 /*ARGSUSED*/
372 static int
373 create_chips(topo_mod_t *mod, tnode_t *pnode, const char *name,
374     topo_instance_t min, topo_instance_t max, void *arg, nvlist_t *auth,
375     int mc_offchip)
376 {
377 	fmd_agent_hdl_t *hdl;
378 	nvlist_t **cpus;
379 	int nerr = 0;
380 	uint_t i, ncpu;
381 
382 	if (strcmp(name, CHIP_NODE_NAME) != 0)
383 		return (0);
384 
385 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
386 		return (-1);
387 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) {
388 		whinge(mod, NULL, "create_chip: fmd_agent_physcpu_info "
389 		    "failed: %s\n", fmd_agent_errmsg(hdl));
390 		fmd_agent_close(hdl);
391 		return (-1);
392 	}
393 	fmd_agent_close(hdl);
394 
395 	for (i = 0; i < ncpu; i++) {
396 		nerr -= create_chip(mod, pnode, min, max, cpus[i], auth,
397 		    mc_offchip);
398 		nvlist_free(cpus[i]);
399 	}
400 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
401 
402 	if (nerr == 0) {
403 		return (0);
404 	} else {
405 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
406 		return (-1);
407 	}
408 }
409 
410 /*ARGSUSED*/
411 static int
412 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
413     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
414 {
415 	int rv = 0;
416 	nvlist_t *auth = NULL;
417 	int offchip_mc;
418 	char buf[BUFSIZ];
419 	const char *dom0 = "control_d";
420 
421 	/*
422 	 * Create nothing if we're running in domU.
423 	 */
424 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) == -1)
425 		return (-1);
426 
427 	if (strncmp(buf, "i86pc", sizeof (buf)) != 0 &&
428 	    strncmp(buf, "i86xpv", sizeof (buf)) != 0)
429 		return (0);
430 
431 	if (strncmp(buf, "i86xpv", sizeof (buf)) == 0) {
432 		int fd = open("/dev/xen/domcaps", O_RDONLY);
433 
434 		if (fd != -1) {
435 			if (read(fd, buf, sizeof (buf)) <= 0 ||
436 			    strncmp(buf, dom0, strlen(dom0)) != 0) {
437 				(void) close(fd);
438 				return (0);
439 			}
440 			(void) close(fd);
441 		}
442 	}
443 
444 	auth = topo_mod_auth(mod, pnode);
445 
446 	offchip_mc = mc_offchip_open();
447 	if (strcmp(name, CHIP_NODE_NAME) == 0)
448 		rv = create_chips(mod, pnode, name, min, max, NULL, auth,
449 		    offchip_mc);
450 
451 	if (offchip_mc)
452 		(void) mc_offchip_create(mod, pnode, "memory-controller", auth);
453 
454 	nvlist_free(auth);
455 
456 	return (rv);
457 }
458