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