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