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
28 #include <errno.h>
29 #include <limits.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <topo_error.h>
33 #include <fm/topo_mod.h>
34 #include <sys/fm/protocol.h>
35
36 #include <topo_method.h>
37 #include <cpu.h>
38
39 /*
40 * platform specific cpu module
41 */
42 #define PLATFORM_CPU_VERSION CPU_VERSION
43 #define PLATFORM_CPU_NAME "platform-cpu"
44
45 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
46 topo_instance_t, void *, void *);
47 static void cpu_release(topo_mod_t *, tnode_t *);
48 static int cpu_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
49 nvlist_t **);
50 static int cpu_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
51 nvlist_t **);
52 static int cpu_fmri_asru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
53 nvlist_t **);
54 static int cpu_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
55 nvlist_t *, nvlist_t **);
56 static nvlist_t *fmri_create(topo_mod_t *, uint32_t, uint8_t, char *);
57
58 static const topo_method_t cpu_methods[] = {
59 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
60 TOPO_STABILITY_INTERNAL, cpu_nvl2str },
61 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
62 TOPO_STABILITY_INTERNAL, cpu_str2nvl },
63 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
64 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
65 cpu_fmri_asru },
66 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
67 TOPO_STABILITY_INTERNAL, cpu_fmri_create_meth },
68 { NULL }
69 };
70
71 static const topo_modops_t cpu_ops =
72 { cpu_enum, cpu_release };
73
74 static const topo_modinfo_t cpu_info =
75 { "cpu", FM_FMRI_SCHEME_CPU, CPU_VERSION, &cpu_ops };
76
77 int
cpu_init(topo_mod_t * mod,topo_version_t version)78 cpu_init(topo_mod_t *mod, topo_version_t version)
79 {
80 cpu_node_t *cpuip;
81
82 if (getenv("TOPOCPUDEBUG"))
83 topo_mod_setdebug(mod);
84 topo_mod_dprintf(mod, "initializing cpu builtin\n");
85
86 if (version != CPU_VERSION)
87 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
88
89 if ((cpuip = topo_mod_zalloc(mod, sizeof (cpu_node_t))) == NULL)
90 return (topo_mod_seterrno(mod, EMOD_NOMEM));
91
92 if ((cpuip->cn_kc = kstat_open()) == NULL) {
93 topo_mod_dprintf(mod, "kstat_open failed: %s\n",
94 strerror(errno));
95 topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
96 return (-1);
97 }
98
99 cpuip->cn_ncpustats = sysconf(_SC_CPUID_MAX);
100 if ((cpuip->cn_cpustats = topo_mod_zalloc(mod, (
101 cpuip->cn_ncpustats + 1) * sizeof (kstat_t *))) == NULL) {
102 (void) kstat_close(cpuip->cn_kc);
103 topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
104 return (-1);
105 }
106
107 if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) {
108 topo_mod_dprintf(mod, "failed to register cpu_info: "
109 "%s\n", topo_mod_errmsg(mod));
110 topo_mod_free(mod, cpuip->cn_cpustats,
111 (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
112 (void) kstat_close(cpuip->cn_kc);
113 topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
114 return (-1);
115 }
116
117 topo_mod_setspecific(mod, (void *)cpuip);
118
119 return (0);
120 }
121
122 void
cpu_fini(topo_mod_t * mod)123 cpu_fini(topo_mod_t *mod)
124 {
125 cpu_node_t *cpuip;
126
127 cpuip = topo_mod_getspecific(mod);
128
129 if (cpuip->cn_cpustats != NULL)
130 topo_mod_free(mod, cpuip->cn_cpustats,
131 (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *));
132
133 (void) kstat_close(cpuip->cn_kc);
134 topo_mod_free(mod, cpuip, sizeof (cpu_node_t));
135
136 topo_mod_unregister(mod);
137 }
138
139 static int
cpu_kstat_init(cpu_node_t * cpuip,int i)140 cpu_kstat_init(cpu_node_t *cpuip, int i)
141 {
142 kstat_t *ksp;
143
144 if (cpuip->cn_cpustats[i] == NULL) {
145 if ((ksp = kstat_lookup(cpuip->cn_kc, "cpu_info", i, NULL)) ==
146 NULL || kstat_read(cpuip->cn_kc, ksp, NULL) < 0)
147 return (-1);
148
149 cpuip->cn_cpustats[i] = ksp;
150 } else {
151 ksp = cpuip->cn_cpustats[i];
152 }
153
154 return (ksp->ks_instance);
155 }
156
157 /*ARGSUSED*/
158 static int
cpu_create(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,cpu_node_t * cpuip)159 cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name,
160 topo_instance_t min, topo_instance_t max, cpu_node_t *cpuip)
161 {
162 int i;
163 processorid_t cpu_id;
164 char *s, sbuf[21];
165 kstat_named_t *ks;
166 nvlist_t *fmri;
167
168 for (i = 0; i <= cpuip->cn_ncpustats; i++) {
169
170 if ((cpu_id = cpu_kstat_init(cpuip, i)) < 0)
171 continue;
172
173 if ((ks = kstat_data_lookup(cpuip->cn_cpustats[i],
174 "device_ID")) != NULL) {
175 (void) snprintf(sbuf, 21, "%llX", ks->value.ui64);
176 s = sbuf;
177 } else {
178 s = NULL;
179 }
180
181 if ((fmri = fmri_create(mod, cpu_id, 0, s)) == NULL)
182 continue;
183 (void) topo_node_bind(mod, rnode, name, cpu_id, fmri);
184 nvlist_free(fmri);
185 }
186
187 return (0);
188 }
189
190
191 /*ARGSUSED*/
192 static int
cpu_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused2)193 cpu_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
194 topo_instance_t min, topo_instance_t max, void *arg, void *notused2)
195 {
196 topo_mod_t *nmp;
197 cpu_node_t *cpuip = (cpu_node_t *)arg;
198
199 if ((nmp = topo_mod_load(mod, PLATFORM_CPU_NAME,
200 PLATFORM_CPU_VERSION)) == NULL) {
201 if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) {
202 /*
203 * There is no platform specific cpu module, so use
204 * the default enumeration with kstats of this builtin
205 * cpu module.
206 */
207 if (topo_node_range_create(mod, pnode, name, 0,
208 cpuip->cn_ncpustats + 1) < 0) {
209 topo_mod_dprintf(mod,
210 "cpu enumeration failed to create "
211 "cpu range [0-%d]: %s\n",
212 cpuip->cn_ncpustats + 1,
213 topo_mod_errmsg(mod));
214 return (-1); /* mod_errno set */
215 }
216 (void) topo_method_register(mod, pnode, cpu_methods);
217 return (cpu_create(mod, pnode, name, min, max, cpuip));
218
219 } else {
220 /* Fail to load the module */
221 topo_mod_dprintf(mod,
222 "Failed to load module %s: %s",
223 PLATFORM_CPU_NAME,
224 topo_mod_errmsg(mod));
225 return (-1);
226 }
227 }
228
229 if (topo_mod_enumerate(nmp, pnode, PLATFORM_CPU_NAME, name,
230 min, max, NULL) < 0) {
231 topo_mod_dprintf(mod,
232 "%s failed to enumerate: %s",
233 PLATFORM_CPU_NAME,
234 topo_mod_errmsg(mod));
235 return (-1);
236 }
237 (void) topo_method_register(mod, pnode, cpu_methods);
238
239 return (0);
240 }
241
242 static void
cpu_release(topo_mod_t * mod,tnode_t * node)243 cpu_release(topo_mod_t *mod, tnode_t *node)
244 {
245 topo_method_unregister_all(mod, node);
246 }
247
248 ssize_t
fmri_nvl2str(nvlist_t * nvl,uint8_t version,char * buf,size_t buflen)249 fmri_nvl2str(nvlist_t *nvl, uint8_t version, char *buf, size_t buflen)
250 {
251 int rc;
252 uint8_t type;
253 uint32_t cpuid, way;
254 uint32_t index;
255 uint16_t bit;
256 uint64_t serint;
257 char *serstr = NULL;
258
259 if (version == CPU_SCHEME_VERSION0) {
260 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 ||
261 nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint)
262 != 0)
263 return (0);
264
265 return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX",
266 FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID,
267 (u_longlong_t)serint));
268
269 } else if (version == CPU_SCHEME_VERSION1) {
270 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0)
271 return (0);
272
273 /*
274 * Serial number is an optional element
275 */
276 if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID,
277 &serstr)) != 0)
278
279 if (rc != ENOENT)
280 return (0);
281
282 /*
283 * Cache index, way and type are optional elements
284 * But if we have one of them, we must have them all.
285 */
286 rc = nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_INDEX,
287 &index);
288 rc |= nvlist_lookup_uint32(nvl, FM_FMRI_CPU_CACHE_WAY, &way);
289 rc |= nvlist_lookup_uint16(nvl, FM_FMRI_CPU_CACHE_BIT, &bit);
290 rc |= nvlist_lookup_uint8(nvl, FM_FMRI_CPU_CACHE_TYPE, &type);
291
292 /* Insure there were no errors accessing the nvl */
293 if (rc != 0 && rc != ENOENT)
294 return (0);
295
296 if (serstr == NULL) {
297 /* If we have a serial string and no cache info */
298 if (rc == ENOENT)
299 return (snprintf(buf, buflen, "cpu:///%s=%u",
300 FM_FMRI_CPU_ID, cpuid));
301 else {
302 return (snprintf(buf, buflen,
303 "cpu:///%s=%u/%s=%u/%s=%u/%s=%d/%s=%d",
304 FM_FMRI_CPU_ID, cpuid,
305 FM_FMRI_CPU_CACHE_INDEX, index,
306 FM_FMRI_CPU_CACHE_WAY, way,
307 FM_FMRI_CPU_CACHE_BIT, bit,
308 FM_FMRI_CPU_CACHE_TYPE, type));
309 }
310 } else {
311 if (rc == ENOENT) {
312 return (snprintf(buf, buflen,
313 "cpu:///%s=%u/%s=%s",
314 FM_FMRI_CPU_ID, cpuid,
315 FM_FMRI_CPU_SERIAL_ID, serstr));
316 } else {
317 return (snprintf(buf, buflen,
318 "cpu:///%s=%u/%s=%s/%s=%u/%s=%u/%s=%d/%s=%d",
319 FM_FMRI_CPU_ID, cpuid,
320 FM_FMRI_CPU_SERIAL_ID, serstr,
321 FM_FMRI_CPU_CACHE_INDEX, index,
322 FM_FMRI_CPU_CACHE_WAY, way,
323 FM_FMRI_CPU_CACHE_BIT, bit,
324 FM_FMRI_CPU_CACHE_TYPE, type));
325 }
326 }
327 } else
328 return (0);
329 }
330
331 /*ARGSUSED*/
332 static int
cpu_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)333 cpu_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
334 nvlist_t *in, nvlist_t **out)
335 {
336 uint8_t fver;
337 ssize_t len;
338 char *name;
339
340 if (version > TOPO_METH_NVL2STR_VERSION)
341 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
342
343 if (nvlist_lookup_uint8(in, FM_VERSION, &fver) != 0)
344 return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
345
346 if ((len = fmri_nvl2str(in, fver, NULL, 0)) == 0 ||
347 (name = topo_mod_alloc(mod, len + 1)) == NULL ||
348 fmri_nvl2str(in, fver, name, len + 1) == 0)
349 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
350
351 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
352 topo_mod_free(mod, name, len + 1);
353 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
354 }
355
356 if (nvlist_add_string(*out, "fmri-string", name) != 0) {
357 topo_mod_free(mod, name, len + 1);
358 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
359 }
360 topo_mod_free(mod, name, len + 1);
361
362 return (0);
363 }
364
365 /*ARGSUSED*/
366 static int
cpu_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)367 cpu_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
368 nvlist_t *in, nvlist_t **out)
369 {
370 int err;
371 ulong_t cpuid;
372 uint8_t type = 0;
373 uint32_t way = 0;
374 uint32_t index = 0;
375 int index_present = 0;
376 uint16_t bit = 0;
377 char *str, *s, *end, *serial_end;
378 char *serial = NULL;
379 nvlist_t *fmri;
380
381 if (version > TOPO_METH_STR2NVL_VERSION)
382 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
383
384 if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
385 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
386
387 /* We're expecting a string version of a cpu scheme FMRI */
388 if (strncmp(str, "cpu:///", 7) != 0)
389 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
390
391 s = strchr(str + 7, '=');
392 if (s == NULL)
393 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
394
395 ++s;
396 cpuid = strtoul(s, &end, 0);
397
398 if (cpuid == ULONG_MAX && errno == ERANGE)
399 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
400
401 /* If there is a serial #, then there might also be cache data */
402 if (*(s = end) == '/') {
403 s = strchr(s, '=');
404 ++s;
405 serial = s;
406 serial_end = strchr(s, '/');
407 /* If there is cache data, all must be present */
408 if (serial_end != NULL) {
409 /* Now terminate the serial string */
410 *serial_end = '\0';
411 index_present = 1;
412 s = serial_end + 1;
413 s = strchr(s, '=');
414 ++s;
415 index = strtoul(s, &end, 0);
416 if (*(s = end) != '/') {
417 return (topo_mod_seterrno(mod,
418 EMOD_FMRI_MALFORM));
419 }
420 s = strchr(s, '=');
421 if (s == NULL) {
422 return (topo_mod_seterrno(mod,
423 EMOD_FMRI_MALFORM));
424 }
425 ++s;
426 way = strtoul(s, &end, 0);
427 if (*(s = end) != '/') {
428 return (topo_mod_seterrno(mod,
429 EMOD_FMRI_MALFORM));
430 }
431 s = strchr(s, '=');
432 if (s == NULL) {
433 return (topo_mod_seterrno(mod,
434 EMOD_FMRI_MALFORM));
435 }
436 ++s;
437 bit = strtoul(s, &end, 0);
438 if (*(s = end) != '/') {
439 return (topo_mod_seterrno(mod,
440 EMOD_FMRI_MALFORM));
441 }
442 s = strchr(s, '=');
443 if (s == NULL) {
444 return (topo_mod_seterrno(mod,
445 EMOD_FMRI_MALFORM));
446 }
447 ++s;
448 type = strtoul(s, &end, 0);
449 }
450
451 }
452 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
453 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
454
455 err = nvlist_add_uint8(fmri, FM_VERSION, CPU_SCHEME_VERSION1);
456 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
457 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, (uint32_t)cpuid);
458 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, 0);
459 if (serial != NULL)
460 err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID,
461 serial);
462
463 if (index_present) {
464 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_CACHE_INDEX,
465 index);
466 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_CACHE_WAY,
467 way);
468 err |= nvlist_add_uint16(fmri, FM_FMRI_CPU_CACHE_BIT,
469 bit);
470 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_CACHE_TYPE,
471 type);
472 }
473 if (err != 0) {
474 nvlist_free(fmri);
475 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
476 }
477 *out = fmri;
478
479 return (0);
480 }
481
482 static nvlist_t *
fmri_create(topo_mod_t * mod,uint32_t cpu_id,uint8_t cpumask,char * s)483 fmri_create(topo_mod_t *mod, uint32_t cpu_id, uint8_t cpumask, char *s)
484 {
485 int err;
486 nvlist_t *fmri;
487
488 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) {
489 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
490 return (NULL);
491 }
492
493 err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION);
494 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
495 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpu_id);
496 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask);
497 if (s != NULL)
498 err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, s);
499 if (err != 0) {
500 nvlist_free(fmri);
501 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
502 return (NULL);
503 }
504
505 return (fmri);
506 }
507
508 /*ARGSUSED*/
509 static int
cpu_fmri_asru(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)510 cpu_fmri_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version,
511 nvlist_t *in, nvlist_t **out)
512 {
513 int rc;
514 uint32_t cpu_id;
515 uint8_t cpumask = 0;
516 char *serial = NULL;
517
518 if ((rc = nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpu_id)) != 0) {
519 if (rc == ENOENT)
520 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
521 else
522 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
523 }
524
525 (void) nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &serial);
526 (void) nvlist_lookup_uint8(in, FM_FMRI_CPU_MASK, &cpumask);
527
528 *out = fmri_create(mod, cpu_id, cpumask, serial);
529
530 return (0);
531 }
532
533 /*ARGSUSED*/
534 static int
cpu_fmri_create_meth(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)535 cpu_fmri_create_meth(topo_mod_t *mod, tnode_t *node, topo_version_t version,
536 nvlist_t *in, nvlist_t **out)
537 {
538 int rc;
539 nvlist_t *args;
540 uint32_t cpu_id;
541 uint8_t cpumask = 0;
542 char *serial = NULL;
543
544 if (version > TOPO_METH_FMRI_VERSION) {
545 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
546 }
547
548 rc = nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args);
549 if (rc != 0) {
550 /*
551 * This routine requires arguments to be packed in the
552 * format used in topo_fmri_create()
553 */
554 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
555 }
556
557 if (nvlist_lookup_string(args, FM_FMRI_CPU_SERIAL_ID, &serial) != 0 ||
558 nvlist_lookup_uint32(args, FM_FMRI_CPU_ID, &cpu_id) != 0 ||
559 nvlist_lookup_uint8(args, FM_FMRI_CPU_MASK, &cpumask) != 0) {
560 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
561 }
562
563 *out = fmri_create(mod, cpu_id, cpumask, serial);
564 if (*out == NULL) {
565 return (-1);
566 }
567
568 return (0);
569 }
570