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 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <unistd.h>
30#include <stdio.h>
31#include <stdlib.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 <fm/topo_mod.h>
46
47/*
48 * Enumerates the processing chips, or sockets, (as distinct from cores) in a
49 * system.  For each chip found, the necessary nodes (one or more cores, and
50 * possibly a memory controller) are constructed underneath.
51 */
52
53#ifdef __cplusplus
54extern "C" {
55#endif
56
57#define	CHIP_VERSION	TOPO_VERSION
58#define	CPU_NODE_NAME	"cpu"
59#define	CHIP_NODE_NAME	"chip"
60
61typedef struct chip {
62	kstat_ctl_t *chip_kc;
63	kstat_t **chip_cpustats;
64	uint_t chip_ncpustats;
65} chip_t;
66
67static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
68    topo_instance_t, void *, void *);
69
70static const topo_modops_t chip_ops =
71	{ chip_enum, NULL};
72static const topo_modinfo_t chip_info =
73	{ "chip", FM_FMRI_SCHEME_HC, CHIP_VERSION, &chip_ops };
74
75int
76_topo_init(topo_mod_t *mod)
77{
78	chip_t *chip;
79
80	if (getenv("TOPOCHIPDBG"))
81		topo_mod_setdebug(mod);
82	topo_mod_dprintf(mod, "initializing chip enumerator\n");
83
84	if ((chip = topo_mod_zalloc(mod, sizeof (chip_t))) == NULL)
85		return (-1);
86
87	if ((chip->chip_kc = kstat_open()) == NULL) {
88		topo_mod_dprintf(mod, "kstat_open failed: %s\n",
89		    strerror(errno));
90		topo_mod_free(mod, chip, sizeof (chip_t));
91		return (-1);
92	}
93
94	chip->chip_ncpustats = sysconf(_SC_CPUID_MAX);
95	if ((chip->chip_cpustats = topo_mod_zalloc(mod, (
96	    chip->chip_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
97		(void) kstat_close(chip->chip_kc);
98		topo_mod_free(mod, chip, sizeof (chip_t));
99		return (-1);
100	}
101
102	if (topo_mod_register(mod, &chip_info, TOPO_VERSION) != 0) {
103		topo_mod_dprintf(mod, "failed to register hc: "
104		    "%s\n", topo_mod_errmsg(mod));
105		topo_mod_free(mod, chip->chip_cpustats,
106		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
107		(void) kstat_close(chip->chip_kc);
108		topo_mod_free(mod, chip, sizeof (chip_t));
109		return (-1);
110	}
111	topo_mod_setspecific(mod, (void *)chip);
112
113	return (0);
114}
115
116void
117_topo_fini(topo_mod_t *mod)
118{
119	chip_t *chip;
120
121	chip = topo_mod_getspecific(mod);
122
123	if (chip->chip_cpustats != NULL)
124		topo_mod_free(mod, chip->chip_cpustats,
125		    (chip->chip_ncpustats + 1) * sizeof (kstat_t *));
126
127	(void) kstat_close(chip->chip_kc);
128	topo_mod_free(mod, chip, sizeof (chip_t));
129
130	topo_mod_unregister(mod);
131}
132
133static int
134cpu_kstat_init(chip_t *chip, int i)
135{
136	kstat_t *ksp;
137
138	if (chip->chip_cpustats[i] == NULL) {
139		if ((ksp = kstat_lookup(chip->chip_kc, "cpu_info", i, NULL)) ==
140		    NULL || kstat_read(chip->chip_kc, ksp, NULL) < 0)
141			return (-1);
142
143		chip->chip_cpustats[i] = ksp;
144	} else {
145		ksp = chip->chip_cpustats[i];
146	}
147
148	return (ksp->ks_instance);
149}
150
151static nvlist_t *
152cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
153{
154	int err;
155	nvlist_t *asru;
156
157	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
158		return (NULL);
159
160	err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
161	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
162	err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
163	err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
164	if (s != NULL)
165		err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
166	if (err != 0) {
167		nvlist_free(asru);
168		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
169		return (NULL);
170	}
171
172	return (asru);
173}
174
175/*ARGSUSED*/
176static int
177cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name,
178    topo_instance_t min, topo_instance_t max, chip_t *chip)
179{
180	int i, err, chip_id, nerr = 0;
181	char *s, sbuf[21];
182	tnode_t *cnode;
183	kstat_named_t *ks, *kf;
184	nvlist_t *fmri, *asru;
185	nvlist_t *auth = topo_mod_auth(mod, rnode);
186
187	/*
188	 * Override what was created for us
189	 */
190	topo_node_range_destroy(rnode, name);
191	if (topo_node_range_create(mod, rnode, name, 0, chip->chip_ncpustats)
192	    < 0)
193		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
194
195	for (i = 0; i <= chip->chip_ncpustats; i++) {
196
197		if ((chip_id = cpu_kstat_init(chip, i)) < 0)
198			continue;
199
200		if ((ks = kstat_data_lookup(chip->chip_cpustats[i],
201		    "device_ID")) != NULL) {
202			(void) snprintf(sbuf, 21, "%llX", ks->value.ui64);
203			s = sbuf;
204		} else {
205			s = NULL;
206		}
207
208		fmri = topo_mod_hcfmri(mod, rnode, FM_HC_SCHEME_VERSION, name,
209		    (topo_instance_t)chip_id, NULL, auth, NULL, NULL, s);
210		if (fmri == NULL || (cnode = topo_node_bind(mod,
211		    rnode, name, i, fmri)) == NULL) {
212			++nerr;
213			nvlist_free(fmri);
214			continue;
215		}
216		nvlist_free(fmri);
217
218		if ((asru = cpu_fmri_create(mod, i, s, 0)) != NULL) {
219			(void) topo_node_asru_set(cnode, asru, 0, &err);
220			nvlist_free(asru);
221		} else {
222			++nerr;
223		}
224
225		/*
226		 * We look for a cpu_fru kstat.  If one is available and
227		 * it contains something useful, use it as the label and
228		 * and the FRU.
229		 *
230		 * This is a problem for platforms that do not properly
231		 * support the cpu_fru kstat like Ontario or if
232		 * we start exporting a different type of FRU label
233		 */
234		if ((kf = kstat_data_lookup(chip->chip_cpustats[i], "cpu_fru"))
235		    != NULL && strcmp(KSTAT_NAMED_STR_PTR(kf),
236		    "hc:///component=") != 0) {
237			nvlist_t *fru;
238			char *lp;
239
240			if (topo_mod_str2nvl(mod, KSTAT_NAMED_STR_PTR(kf),
241			    &fru) == 0) {
242				(void) topo_node_fru_set(cnode, fru, 0, &err);
243				nvlist_free(fru);
244			}
245
246			if ((lp = strchr(KSTAT_NAMED_STR_PTR(kf), '='))
247			    == NULL) {
248				(void) topo_node_label_set(cnode, NULL, &err);
249			} else {
250				++lp;
251				(void) topo_node_label_set(cnode, lp, &err);
252			}
253		} else {
254			(void) topo_node_label_set(cnode, NULL, &err);
255		}
256	}
257
258	nvlist_free(auth);
259
260	if (nerr != 0)
261		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
262	else
263		return (0);
264}
265
266/*ARGSUSED*/
267static int
268chip_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
269    topo_instance_t min, topo_instance_t max, void *arg, void *notused)
270{
271	chip_t *chip = (chip_t *)arg;
272
273	if (strcmp(name, CPU_NODE_NAME) == 0)
274		return (cpu_create(mod, rnode, name, min, max, chip));
275
276	return (0);
277}
278