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
54 extern "C" {
55 #endif
56 
57 #define	CHIP_VERSION	TOPO_VERSION
58 #define	CPU_NODE_NAME	"cpu"
59 #define	CHIP_NODE_NAME	"chip"
60 
61 typedef struct chip {
62 	kstat_ctl_t *chip_kc;
63 	kstat_t **chip_cpustats;
64 	uint_t chip_ncpustats;
65 } chip_t;
66 
67 static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
68     topo_instance_t, void *, void *);
69 
70 static const topo_modops_t chip_ops =
71 	{ chip_enum, NULL};
72 static const topo_modinfo_t chip_info =
73 	{ "chip", FM_FMRI_SCHEME_HC, CHIP_VERSION, &chip_ops };
74 
75 int
_topo_init(topo_mod_t * mod)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 
116 void
_topo_fini(topo_mod_t * mod)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 
133 static int
cpu_kstat_init(chip_t * chip,int i)134 cpu_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 
151 static nvlist_t *
cpu_fmri_create(topo_mod_t * mod,uint32_t cpuid,char * s,uint8_t cpumask)152 cpu_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*/
176 static int
cpu_create(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,chip_t * chip)177 cpu_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*/
267 static int
chip_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused)268 chip_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