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 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 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 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 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 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 243 cpu_release(topo_mod_t *mod, tnode_t *node) 244 { 245 topo_method_unregister_all(mod, node); 246 } 247 248 ssize_t 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 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 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 * 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 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 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