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 #include <strings.h> 28 #include <umem.h> 29 #include <fm/topo_mod.h> 30 #include <fm/fmd_fmri.h> 31 #include <sys/fm/ldom.h> 32 #include <sys/fm/protocol.h> 33 34 #include <cpu_mdesc.h> 35 36 /* 37 * This enumerator creates cpu-schemed nodes for each strand found in the 38 * sun4v Physical Rource Inventory (PRI). 39 * Each node export four methods present(), expand() replaced() and unusable(). 40 * 41 */ 42 43 #define PLATFORM_CPU_NAME "platform-cpu" 44 #define PLATFORM_CPU_VERSION TOPO_VERSION 45 #define CPU_NODE_NAME "cpu" 46 47 48 /* Forward declaration */ 49 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 50 topo_instance_t, void *, void *); 51 static void cpu_release(topo_mod_t *, tnode_t *); 52 static int cpu_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 53 nvlist_t **); 54 static int cpu_replaced(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 55 nvlist_t **); 56 static int cpu_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 57 nvlist_t **); 58 static int cpu_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 59 nvlist_t **); 60 61 static const topo_modops_t cpu_ops = 62 { cpu_enum, cpu_release }; 63 static const topo_modinfo_t cpu_info = 64 { PLATFORM_CPU_NAME, FM_FMRI_SCHEME_CPU, PLATFORM_CPU_VERSION, 65 &cpu_ops }; 66 67 static const topo_method_t cpu_methods[] = { 68 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 69 TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL, cpu_present }, 70 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 71 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, cpu_replaced }, 72 { TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC, 73 TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, cpu_expand }, 74 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 75 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, cpu_unusable }, 76 { NULL } 77 }; 78 79 static void * 80 cpu_alloc(size_t size) 81 { 82 return (umem_alloc(size, UMEM_DEFAULT)); 83 } 84 85 static void 86 cpu_free(void *data, size_t size) 87 { 88 umem_free(data, size); 89 } 90 91 static int 92 cpu_read_serial(nvlist_t *in, uint64_t *serial) 93 { 94 uint8_t version; 95 uint64_t int_serial; 96 char *str_serial, *end; 97 int rc = 0; 98 99 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0) 100 return (1); 101 102 if (version == CPU_SCHEME_VERSION0) { 103 if ((rc = nvlist_lookup_uint64(in, FM_FMRI_CPU_SERIAL_ID, 104 &int_serial)) == 0) { 105 *serial = int_serial; 106 } 107 } else { 108 if ((rc = nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, 109 &str_serial)) == 0) { 110 *serial = (uint64_t)strtoull(str_serial, &end, 16); 111 if (str_serial == end) 112 rc = 1; 113 } 114 } 115 return (rc); 116 } 117 118 int 119 _topo_init(topo_mod_t *mod) 120 { 121 md_info_t *chip; 122 123 if (getenv("TOPOPLATFORMCPUDBG")) 124 topo_mod_setdebug(mod); 125 topo_mod_dprintf(mod, "initializing %s enumerator\n", 126 PLATFORM_CPU_NAME); 127 128 if ((chip = topo_mod_zalloc(mod, sizeof (md_info_t))) == NULL) 129 return (-1); 130 131 if (cpu_mdesc_init(mod, chip) != 0) { 132 topo_mod_dprintf(mod, "failed to get cpus from the PRI/MD\n"); 133 topo_mod_free(mod, chip, sizeof (md_info_t)); 134 return (-1); 135 } 136 137 topo_mod_setspecific(mod, (void *)chip); 138 139 if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) { 140 topo_mod_dprintf(mod, "failed to register %s: %s\n", 141 PLATFORM_CPU_NAME, topo_mod_errmsg(mod)); 142 cpu_mdesc_fini(mod, chip); 143 topo_mod_free(mod, chip, sizeof (md_info_t)); 144 return (-1); 145 } 146 147 topo_mod_dprintf(mod, "%s enumerator inited\n", PLATFORM_CPU_NAME); 148 149 return (0); 150 } 151 152 void 153 _topo_fini(topo_mod_t *mod) 154 { 155 md_info_t *chip; 156 157 chip = (md_info_t *)topo_mod_getspecific(mod); 158 159 cpu_mdesc_fini(mod, chip); 160 161 topo_mod_free(mod, chip, sizeof (md_info_t)); 162 163 topo_mod_unregister(mod); 164 165 } 166 167 /*ARGSUSED*/ 168 static int 169 cpu_present(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 170 nvlist_t *in, nvlist_t **out) 171 { 172 uint8_t version; 173 uint32_t cpuid; 174 uint64_t nvlserid; 175 uint32_t present = 0; 176 md_cpumap_t *mcmp; 177 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); 178 179 /* 180 * Get the physical cpuid 181 */ 182 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 183 version > FM_CPU_SCHEME_VERSION || 184 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { 185 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 186 } 187 188 /* 189 * Find the cpuid entry 190 * If the input nvl contains a serial number, the cpu is identified 191 * by a tuple <cpuid, cpuserial> 192 * Otherwise, the cpu is identified by the <cpuid>. 193 */ 194 if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) { 195 if (cpu_read_serial(in, &nvlserid) == 0) 196 present = nvlserid == mcmp->cpumap_serialno; 197 else 198 present = 1; 199 } 200 201 /* return the present status */ 202 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 203 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 204 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 205 nvlist_free(*out); 206 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 207 } 208 209 return (0); 210 } 211 212 /*ARGSUSED*/ 213 static int 214 cpu_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 215 nvlist_t *in, nvlist_t **out) 216 { 217 uint8_t version; 218 uint32_t cpuid; 219 uint64_t nvlserid; 220 uint32_t rval = FMD_OBJ_STATE_NOT_PRESENT; 221 md_cpumap_t *mcmp; 222 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); 223 224 /* 225 * Get the physical cpuid 226 */ 227 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 228 version > FM_CPU_SCHEME_VERSION || 229 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { 230 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 231 } 232 233 /* 234 * Find the cpuid entry 235 * If the input nvl contains a serial number, the cpu is identified 236 * by a tuple <cpuid, cpuserial> 237 * Otherwise, the cpu is identified by the <cpuid>. 238 */ 239 if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) { 240 if (cpu_read_serial(in, &nvlserid) == 0) 241 rval = (nvlserid == mcmp->cpumap_serialno) ? 242 FMD_OBJ_STATE_STILL_PRESENT : 243 FMD_OBJ_STATE_REPLACED; 244 else 245 rval = FMD_OBJ_STATE_UNKNOWN; 246 } 247 248 /* return the replaced status */ 249 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 250 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 251 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 252 nvlist_free(*out); 253 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 254 } 255 256 return (0); 257 } 258 259 /*ARGSUSED*/ 260 static int 261 cpu_expand(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 262 nvlist_t *in, nvlist_t **out) 263 { 264 int rc; 265 uint8_t version; 266 uint32_t cpuid; 267 uint64_t nvlserid; 268 md_cpumap_t *mcmp = NULL; 269 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); 270 271 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 272 version > FM_CPU_SCHEME_VERSION || 273 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { 274 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 275 } 276 277 /* Find the cpuid entry */ 278 if ((mcmp = cpu_find_cpumap(chip, cpuid)) == NULL) 279 return (-1); 280 281 if ((rc = cpu_read_serial(in, &nvlserid)) == 0) { 282 if (nvlserid != mcmp->cpumap_serialno) 283 return (-1); 284 } else if (rc != ENOENT) 285 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 286 else { 287 if ((rc = nvlist_add_uint64(in, FM_FMRI_CPU_SERIAL_ID, 288 mcmp->cpumap_serialno)) != 0) { 289 return (topo_mod_seterrno(mod, rc)); 290 } 291 } 292 293 topo_mod_dprintf(mod, "nvlserid=%llX\n", nvlserid); 294 295 if (mcmp != NULL && 296 mcmp->cpumap_chipidx >= 0 && 297 mcmp->cpumap_chipidx < chip->nprocs && 298 chip->procs && 299 chip->procs[mcmp->cpumap_chipidx].fru) { 300 int len; 301 char *str; 302 md_fru_t *frup = chip->procs[mcmp->cpumap_chipidx].fru; 303 304 /* part number + dash number */ 305 len = (frup->part ? strlen(frup->part) : 0) + 306 (frup->dash ? strlen(frup->dash) : 0) + 1; 307 str = cpu_alloc(len); 308 (void) snprintf(str, len, "%s%s", 309 frup->part ? frup->part : MD_STR_BLANK, 310 frup->dash ? frup->dash : MD_STR_BLANK); 311 (void) nvlist_add_string(in, FM_FMRI_HC_PART, str); 312 cpu_free(str, len); 313 314 /* fru name */ 315 (void) nvlist_add_string(in, FM_FMRI_CPU_CPUFRU, 316 frup->nac ? frup->nac : MD_STR_BLANK); 317 318 /* fru serial */ 319 in->nvl_nvflag = NV_UNIQUE_NAME_TYPE; 320 (void) nvlist_add_string(in, FM_FMRI_HC_SERIAL_ID, 321 frup->serial ? frup->serial : MD_STR_BLANK); 322 } 323 324 return (0); 325 } 326 327 /*ARGSUSED*/ 328 static int 329 cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 330 nvlist_t *in, nvlist_t **out) 331 { 332 int rc = -1; 333 uint8_t version; 334 int status; 335 uint32_t cpuid; 336 ldom_hdl_t *lhp; 337 uint64_t nvlserid; 338 uint32_t present = 0; 339 md_cpumap_t *mcmp; 340 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); 341 uint32_t type = 0; 342 343 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 344 version > FM_CPU_SCHEME_VERSION || 345 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { 346 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 347 } 348 349 /* 350 * Check the cpu presence 351 */ 352 if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) { 353 if (cpu_read_serial(in, &nvlserid) == 0) 354 present = nvlserid == mcmp->cpumap_serialno; 355 else 356 present = 1; 357 } 358 if (present == 0) { 359 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 360 } 361 362 lhp = ldom_init(cpu_alloc, cpu_free); 363 if (lhp == NULL) { 364 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 365 } 366 (void) ldom_get_type(lhp, &type); 367 status = ldom_fmri_status(lhp, in); 368 rc = (status == P_FAULTED || 369 (status == P_OFFLINE && ((type & LDOM_TYPE_CONTROL) != 0))); 370 ldom_fini(lhp); 371 372 /* return the unusable status */ 373 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 374 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 375 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, rc) != 0) { 376 nvlist_free(*out); 377 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 378 } 379 380 return (0); 381 } 382 383 384 static nvlist_t * 385 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *serial, uint8_t cpumask) 386 { 387 int err; 388 nvlist_t *fmri; 389 390 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 391 return (NULL); 392 err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION); 393 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); 394 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpuid); 395 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask); 396 if (serial != NULL) 397 err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, serial); 398 if (err != 0) { 399 nvlist_free(fmri); 400 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 401 return (NULL); 402 } 403 404 return (fmri); 405 } 406 407 static tnode_t * 408 cpu_tnode_create(topo_mod_t *mod, tnode_t *parent, 409 const char *name, topo_instance_t i, char *serial, void *priv) 410 { 411 int cpu_mask = 0; 412 nvlist_t *fmri; 413 tnode_t *ntn; 414 415 fmri = cpu_fmri_create(mod, i, serial, cpu_mask); 416 if (fmri == NULL) { 417 topo_mod_dprintf(mod, 418 "Unable to make nvlist for %s bind: %s.\n", 419 name, topo_mod_errmsg(mod)); 420 return (NULL); 421 } 422 423 ntn = topo_node_bind(mod, parent, name, i, fmri); 424 if (ntn == NULL) { 425 topo_mod_dprintf(mod, 426 "topo_node_bind (%s%d/%s%d) failed: %s\n", 427 topo_node_name(parent), topo_node_instance(parent), 428 name, i, 429 topo_strerror(topo_mod_errno(mod))); 430 nvlist_free(fmri); 431 return (NULL); 432 } 433 nvlist_free(fmri); 434 topo_node_setspecific(ntn, priv); 435 436 return (ntn); 437 } 438 439 /*ARGSUSED*/ 440 static int 441 cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name, md_info_t *chip) 442 { 443 int i; 444 int min = -1; 445 int max = -1; 446 int nerr = 0; 447 int pid; 448 char sbuf[32]; 449 tnode_t *cnode; 450 451 topo_mod_dprintf(mod, "enumerating cpus\n"); 452 453 /* 454 * find the min/max id of cpus per this cmp and create a cpu range 455 */ 456 for (i = 0; i < chip->ncpus; i++) { 457 if ((min < 0) || (chip->cpus[i].cpumap_pid < min)) 458 min = chip->cpus[i].cpumap_pid; 459 if ((max < 0) || (chip->cpus[i].cpumap_pid > max)) 460 max = chip->cpus[i].cpumap_pid; 461 } 462 if (min < 0 || max < 0) 463 return (-1); 464 topo_node_range_destroy(rnode, name); 465 if (topo_node_range_create(mod, rnode, name, 0, max+1) < 0) { 466 topo_mod_dprintf(mod, "failed to create cpu range[0,%d]: %s\n", 467 max, topo_mod_errmsg(mod)); 468 return (-1); 469 } 470 471 /* 472 * Create the cpu nodes 473 */ 474 for (i = 0; i < chip->ncpus; i++) { 475 476 (void) snprintf(sbuf, sizeof (sbuf), "%llx", 477 chip->cpus[i].cpumap_serialno); 478 479 /* physical cpuid */ 480 pid = chip->cpus[i].cpumap_pid; 481 cnode = cpu_tnode_create(mod, rnode, name, 482 (topo_instance_t)pid, sbuf, NULL); 483 if (cnode == NULL) { 484 topo_mod_dprintf(mod, 485 "failed to create a cpu=%d node: %s\n", 486 pid, topo_mod_errmsg(mod)); 487 nerr++; 488 continue; 489 } 490 491 } 492 493 if (nerr != 0) 494 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 495 496 return (0); 497 } 498 499 /*ARGSUSED*/ 500 static int 501 cpu_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 502 topo_instance_t min, topo_instance_t max, void *arg, void *notused) 503 { 504 topo_mod_dprintf(mod, "%s enumerating %s\n", PLATFORM_CPU_NAME, name); 505 506 if (topo_method_register(mod, rnode, cpu_methods) < 0) { 507 topo_mod_dprintf(mod, "topo_method_register failed: %s\n", 508 topo_strerror(topo_mod_errno(mod))); 509 return (-1); 510 } 511 512 if (strcmp(name, CPU_NODE_NAME) == 0) 513 return (cpu_create(mod, rnode, name, (md_info_t *)arg)); 514 515 return (0); 516 } 517 518 /*ARGSUSED*/ 519 static void 520 cpu_release(topo_mod_t *mod, tnode_t *node) 521 { 522 topo_method_unregister_all(mod, node); 523 } 524