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 * Copyright (c) 2018, Joyent, Inc. 26 */ 27 28 /* 29 * Support function for the i86pc chip enumerator 30 */ 31 32 #include <sys/types.h> 33 #include <stdarg.h> 34 #include <strings.h> 35 #include <fm/fmd_fmri.h> 36 #include <sys/systeminfo.h> 37 #include <sys/fm/protocol.h> 38 #include <fm/topo_mod.h> 39 #include <fm/fmd_agent.h> 40 41 #include "chip.h" 42 43 static void fmri_dprint(topo_mod_t *, const char *, uint32_t, nvlist_t *); 44 static boolean_t is_page_fmri(nvlist_t *); 45 46 /* 47 * Whinge a debug message via topo_mod_dprintf and increment the 48 * given error counter. 49 */ 50 void 51 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...) 52 { 53 va_list ap; 54 char buf[160]; 55 56 if (nerr != NULL) 57 ++*nerr; 58 59 va_start(ap, fmt); 60 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 61 va_end(ap); 62 63 topo_mod_dprintf(mod, "%s", buf); 64 } 65 66 /* 67 * Given an nvpair of a limited number of data types, extract the property 68 * name and value and add that combination to the given node in the 69 * specified property group using the corresponding topo_prop_set_* function 70 * for the data type. Return 1 on success, otherwise 0. 71 */ 72 int 73 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node) 74 { 75 int success = 0; 76 int err; 77 char *pname = nvpair_name(nvp); 78 79 switch (nvpair_type(nvp)) { 80 case DATA_TYPE_BOOLEAN_VALUE: { 81 boolean_t val; 82 83 if (nvpair_value_boolean_value(nvp, &val) == 0 && 84 topo_prop_set_string(node, pgname, pname, 85 TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0) 86 success = 1; 87 break; 88 } 89 90 case DATA_TYPE_UINT32: { 91 uint32_t val; 92 93 if (nvpair_value_uint32(nvp, &val) == 0 && 94 topo_prop_set_uint32(node, pgname, pname, 95 TOPO_PROP_IMMUTABLE, val, &err) == 0) 96 success = 1; 97 break; 98 } 99 100 case DATA_TYPE_UINT64: { 101 uint64_t val; 102 103 if (nvpair_value_uint64(nvp, &val) == 0 && 104 topo_prop_set_uint64(node, pgname, pname, 105 TOPO_PROP_IMMUTABLE, val, &err) == 0) 106 success = 1; 107 break; 108 } 109 110 case DATA_TYPE_UINT32_ARRAY: { 111 uint32_t *arrp; 112 uint_t nelem; 113 114 if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 && 115 nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname, 116 TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0) 117 success = 1; 118 break; 119 } 120 121 case DATA_TYPE_STRING: { 122 char *str; 123 124 if (nvpair_value_string(nvp, &str) == 0 && 125 topo_prop_set_string(node, pgname, pname, 126 TOPO_PROP_IMMUTABLE, str, &err) == 0) 127 success = 1; 128 break; 129 } 130 131 default: 132 whinge(mod, &err, "nvprop_add: Can't handle type %d for " 133 "'%s' in property group %s of %s node\n", 134 nvpair_type(nvp), pname, pgname, topo_node_name(node)); 135 break; 136 } 137 138 return (success ? 0 : 1); 139 } 140 141 /* 142 * Lookup string data named pname in the given nvlist and add that 143 * as property named pname in the given property group pgname on the indicated 144 * topo node. Fill pvalp with a pointer to the string value, valid until 145 * nvlist_free is called. 146 */ 147 int 148 add_nvlist_strprop(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl, 149 const char *pgname, const char *pname, const char **pvalp) 150 { 151 char *pval; 152 int err = 0; 153 154 if (nvlist_lookup_string(nvl, pname, &pval) != 0) 155 return (-1); 156 157 if (topo_prop_set_string(node, pgname, pname, 158 TOPO_PROP_IMMUTABLE, pval, &err) == 0) { 159 if (pvalp) 160 *pvalp = pval; 161 return (0); 162 } else { 163 whinge(mod, &err, "add_nvlist_strprop: failed to add '%s'\n", 164 pname); 165 return (-1); 166 } 167 } 168 169 /* 170 * Lookup an int32 item named pname in the given nvlist and add that 171 * as property named pname in the given property group pgname on the indicated 172 * topo node. Fill pvalp with the property value. 173 */ 174 int 175 add_nvlist_longprop(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl, 176 const char *pgname, const char *pname, int32_t *pvalp) 177 { 178 int32_t pval; 179 int err; 180 181 if ((nvlist_lookup_int32(nvl, pname, &pval)) != 0) 182 return (-1); 183 184 if (topo_prop_set_int32(node, pgname, pname, 185 TOPO_PROP_IMMUTABLE, pval, &err) == 0) { 186 if (pvalp) 187 *pvalp = pval; 188 return (0); 189 } else { 190 whinge(mod, &err, "add_nvlist_longprop: failed to add '%s'\n", 191 pname); 192 return (-1); 193 } 194 } 195 196 /* 197 * In a given nvlist lookup a variable number of int32 properties named in 198 * const char * varargs and each each in the given property group on the 199 * node. Fill an array of the retrieved values. 200 */ 201 int 202 add_nvlist_longprops(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl, 203 const char *pgname, int32_t *pvalap, ...) 204 { 205 const char *pname; 206 va_list ap; 207 int nerr = 0; 208 209 va_start(ap, pvalap); 210 while ((pname = va_arg(ap, const char *)) != NULL) { 211 if (add_nvlist_longprop(mod, node, nvl, pgname, pname, 212 pvalap) != 0) 213 nerr++; /* have whinged elsewhere */ 214 215 if (pvalap != NULL) 216 ++pvalap; 217 } 218 va_end(ap); 219 220 return (nerr == 0 ? 0 : -1); 221 } 222 223 /* 224 * Construct an hc scheme resource FMRI for a node named name with 225 * instance number inst, parented by the given parent node pnode. 226 */ 227 int 228 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst, 229 nvlist_t *auth, nvlist_t **nvl) 230 { 231 *nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name, 232 inst, NULL, auth, NULL, NULL, NULL); 233 return (nvl != NULL ? 0 : -1); /* caller must free nvlist */ 234 } 235 236 /* 237 * Construct a cpu scheme FMRI with the given data; the caller must free 238 * the allocated nvlist with nvlist_free(). 239 */ 240 nvlist_t * 241 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask) 242 { 243 int err; 244 nvlist_t *asru; 245 246 if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) 247 return (NULL); 248 249 err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION); 250 err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); 251 err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid); 252 err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask); 253 if (s != NULL) 254 err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s); 255 if (err != 0) { 256 nvlist_free(asru); 257 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 258 return (NULL); 259 } 260 261 return (asru); 262 } 263 264 /*ARGSUSED*/ 265 int 266 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version, 267 nvlist_t *in, nvlist_t **out) 268 { 269 nvlist_t *asru, *args, *pargs, *hcsp; 270 int err; 271 uint64_t pa, offset; 272 273 if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 && 274 strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 && 275 strcmp(topo_node_name(node), CS_NODE_NAME) != 0) 276 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 277 278 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) 279 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 280 281 if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) { 282 if (err == ENOENT) { 283 pargs = args; 284 } else { 285 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 286 } 287 } 288 289 if (topo_mod_nvdup(mod, pargs, &asru) != 0) 290 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 291 292 err = 0; 293 294 /* 295 * if 'in' includes an hc-specific member which specifies asru-physaddr 296 * or asru-offset then rename them to asru and physaddr respectively. 297 */ 298 if (nvlist_lookup_nvlist(asru, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { 299 if (nvlist_lookup_uint64(hcsp, 300 "asru-"FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0) { 301 err += nvlist_remove(hcsp, 302 "asru-"FM_FMRI_HC_SPECIFIC_PHYSADDR, 303 DATA_TYPE_UINT64); 304 err += nvlist_add_uint64(hcsp, 305 FM_FMRI_HC_SPECIFIC_PHYSADDR, 306 pa); 307 } 308 309 if (nvlist_lookup_uint64(hcsp, 310 "asru-"FM_FMRI_HC_SPECIFIC_OFFSET, &offset) == 0) { 311 err += nvlist_remove(hcsp, 312 "asru-"FM_FMRI_HC_SPECIFIC_OFFSET, 313 DATA_TYPE_UINT64); 314 err += nvlist_add_uint64(hcsp, 315 FM_FMRI_HC_SPECIFIC_OFFSET, 316 offset); 317 } 318 } 319 320 if (err != 0 || topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { 321 nvlist_free(asru); 322 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 323 } 324 325 err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU); 326 err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI); 327 err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru); 328 if (err != 0) { 329 nvlist_free(asru); 330 nvlist_free(*out); 331 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 332 } 333 334 nvlist_free(asru); 335 336 return (0); 337 } 338 339 static int 340 set_retnvl(topo_mod_t *mod, nvlist_t **out, const char *retname, uint32_t ret) 341 { 342 nvlist_t *nvl; 343 344 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) < 0) 345 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 346 347 if (nvlist_add_uint32(nvl, retname, ret) != 0) { 348 nvlist_free(nvl); 349 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 350 } 351 352 *out = nvl; 353 return (0); 354 } 355 356 /* 357 * If we're getting called then the question of whether this dimm is plugged 358 * in has already been answered. What we don't know for sure is whether it's 359 * the same dimm or a different one plugged in the same slot. To check, we 360 * try and compare the serial numbers on the dimm in the current topology with 361 * the serial num from the unum fmri that got passed into this function as the 362 * argument. 363 * 364 */ 365 static int 366 fmri_replaced(topo_mod_t *mod, tnode_t *node, nvlist_t *unum, int *errp) 367 { 368 tnode_t *dimmnode; 369 nvlist_t *resource; 370 int rc, err; 371 char *old_serial, *curr_serial; 372 fmd_agent_hdl_t *hdl; 373 374 /* 375 * If input is a page, return "replaced" if the offset is invalid. 376 */ 377 if (is_page_fmri(unum) && 378 (hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { 379 rc = fmd_agent_page_isretired(hdl, unum); 380 err = fmd_agent_errno(hdl); 381 fmd_agent_close(hdl); 382 383 if (rc == FMD_AGENT_RETIRE_DONE && 384 err == EINVAL) 385 return (FMD_OBJ_STATE_NOT_PRESENT); 386 } 387 388 /* 389 * If a serial number for the dimm was available at the time of the 390 * fault, it will have been added as a string to the unum nvlist 391 */ 392 if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial)) 393 return (FMD_OBJ_STATE_UNKNOWN); 394 395 /* 396 * If the current serial number is available for the DIMM that this rank 397 * belongs to, it will be accessible as a property on the parent (dimm) 398 * node. If there is a serial id in the resource fmri, then use that. 399 * Otherwise fall back to looking for a serial id property in the 400 * protocol group. 401 */ 402 dimmnode = topo_node_parent(node); 403 if (topo_node_resource(dimmnode, &resource, &err) != -1) { 404 if (nvlist_lookup_string(resource, FM_FMRI_HC_SERIAL_ID, 405 &curr_serial) == 0) { 406 if (strcmp(old_serial, curr_serial) != 0) { 407 nvlist_free(resource); 408 return (FMD_OBJ_STATE_REPLACED); 409 } else { 410 nvlist_free(resource); 411 return (FMD_OBJ_STATE_STILL_PRESENT); 412 } 413 } 414 nvlist_free(resource); 415 } 416 if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL, 417 FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) { 418 if (err == ETOPO_PROP_NOENT) { 419 return (FMD_OBJ_STATE_UNKNOWN); 420 } else { 421 *errp = EMOD_NVL_INVAL; 422 whinge(mod, NULL, "rank_fmri_present: Unexpected " 423 "error retrieving serial from node"); 424 return (-1); 425 } 426 } 427 428 if (strcmp(old_serial, curr_serial) != 0) { 429 topo_mod_strfree(mod, curr_serial); 430 return (FMD_OBJ_STATE_REPLACED); 431 } 432 433 topo_mod_strfree(mod, curr_serial); 434 435 return (FMD_OBJ_STATE_STILL_PRESENT); 436 } 437 438 /* 439 * In the event we encounter problems comparing serials or if a comparison isn't 440 * possible, we err on the side of caution and set is_present to TRUE. 441 */ 442 int 443 rank_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 444 nvlist_t *in, nvlist_t **out) 445 { 446 int is_present, err; 447 448 if (version > TOPO_METH_PRESENT_VERSION) 449 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 450 451 switch (fmri_replaced(mod, node, in, &err)) { 452 case FMD_OBJ_STATE_REPLACED: 453 case FMD_OBJ_STATE_NOT_PRESENT: 454 is_present = 0; 455 break; 456 457 case FMD_OBJ_STATE_UNKNOWN: 458 case FMD_OBJ_STATE_STILL_PRESENT: 459 is_present = 1; 460 break; 461 462 default: 463 return (topo_mod_seterrno(mod, err)); 464 } 465 466 fmri_dprint(mod, "rank_fmri_present", is_present, in); 467 468 return (set_retnvl(mod, out, TOPO_METH_PRESENT_RET, is_present)); 469 } 470 471 int 472 rank_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 473 nvlist_t *in, nvlist_t **out) 474 { 475 int is_replaced, err; 476 477 if (version > TOPO_METH_REPLACED_VERSION) 478 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 479 480 is_replaced = fmri_replaced(mod, node, in, &err); 481 if (is_replaced == -1) 482 return (topo_mod_seterrno(mod, err)); 483 484 fmri_dprint(mod, "rank_fmri_replaced", is_replaced, in); 485 486 return (set_retnvl(mod, out, TOPO_METH_REPLACED_RET, is_replaced)); 487 } 488 489 static void 490 fmri_dprint(topo_mod_t *mod, const char *op, uint32_t rc, nvlist_t *fmri) 491 { 492 char *fmristr; 493 const char *status; 494 495 if (getenv("TOPOCHIPDBG") == NULL) 496 return; 497 498 switch (rc) { 499 case FMD_AGENT_RETIRE_DONE: 500 status = "sync success"; 501 break; 502 case FMD_AGENT_RETIRE_ASYNC: 503 status = "async retiring"; 504 break; 505 case FMD_AGENT_RETIRE_FAIL: 506 status = "not retired"; 507 break; 508 default: 509 status = "unknown status"; 510 } 511 if (fmri != NULL && topo_mod_nvl2str(mod, fmri, &fmristr) == 0) { 512 topo_mod_dprintf(mod, "[%s]: %s => %d (\"%s\")\n", fmristr, 513 op, rc, status); 514 topo_mod_strfree(mod, fmristr); 515 } 516 } 517 518 struct strand_walk_data { 519 tnode_t *parent; 520 fmd_agent_hdl_t *hdl; 521 int (*func)(fmd_agent_hdl_t *, int, int, int); 522 int err; 523 int done; 524 int fail; 525 int async; 526 }; 527 528 static int 529 strand_walker(topo_mod_t *mod, tnode_t *node, void *pdata) 530 { 531 struct strand_walk_data *swdp = pdata; 532 int32_t chipid, coreid, strandid; 533 int err, rc; 534 535 /* 536 * Terminate the walk if we reach start-node's sibling 537 */ 538 if (node != swdp->parent && 539 topo_node_parent(node) == topo_node_parent(swdp->parent)) 540 return (TOPO_WALK_TERMINATE); 541 542 if (strcmp(topo_node_name(node), STRAND) != 0) 543 return (TOPO_WALK_NEXT); 544 545 if (topo_prop_get_int32(node, PGNAME(STRAND), STRAND_CHIP_ID, 546 &chipid, &err) < 0 || 547 topo_prop_get_int32(node, PGNAME(STRAND), STRAND_CORE_ID, 548 &coreid, &err) < 0) { 549 swdp->err++; 550 return (TOPO_WALK_NEXT); 551 } 552 strandid = topo_node_instance(node); 553 rc = swdp->func(swdp->hdl, chipid, coreid, strandid); 554 555 if (rc == FMD_AGENT_RETIRE_DONE) 556 swdp->done++; 557 else if (rc == FMD_AGENT_RETIRE_FAIL) 558 swdp->fail++; 559 else if (rc == FMD_AGENT_RETIRE_ASYNC) 560 swdp->async++; 561 else 562 swdp->err++; 563 564 if (getenv("TOPOCHIPDBG") != NULL) { 565 const char *op; 566 567 if (swdp->func == fmd_agent_cpu_retire) 568 op = "retire"; 569 else if (swdp->func == fmd_agent_cpu_unretire) 570 op = "unretire"; 571 else if (swdp->func == fmd_agent_cpu_isretired) 572 op = "check status"; 573 else 574 op = "unknown op"; 575 576 topo_mod_dprintf(mod, "%s cpu (%d:%d:%d): rc = %d, err = %s\n", 577 op, (int)chipid, (int)coreid, (int)strandid, rc, 578 fmd_agent_errmsg(swdp->hdl)); 579 } 580 581 return (TOPO_WALK_NEXT); 582 } 583 584 static int 585 walk_strands(topo_mod_t *mod, struct strand_walk_data *swdp, tnode_t *parent, 586 int (*func)(fmd_agent_hdl_t *, int, int, int)) 587 { 588 topo_walk_t *twp; 589 int err; 590 591 swdp->parent = parent; 592 swdp->func = func; 593 swdp->err = swdp->done = swdp->fail = swdp->async = 0; 594 if ((swdp->hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) { 595 swdp->fail++; 596 return (0); 597 } 598 599 twp = topo_mod_walk_init(mod, parent, strand_walker, swdp, &err); 600 if (twp == NULL) { 601 fmd_agent_close(swdp->hdl); 602 return (-1); 603 } 604 605 err = topo_walk_step(twp, TOPO_WALK_CHILD); 606 topo_walk_fini(twp); 607 fmd_agent_close(swdp->hdl); 608 609 if (err == TOPO_WALK_ERR || swdp->err > 0) 610 return (-1); 611 612 return (0); 613 } 614 615 /* ARGSUSED */ 616 int 617 retire_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, 618 nvlist_t *in, nvlist_t **out) 619 { 620 struct strand_walk_data swd; 621 uint32_t rc; 622 623 if (version > TOPO_METH_RETIRE_VERSION) 624 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 625 626 if (walk_strands(mod, &swd, node, fmd_agent_cpu_retire) == -1) 627 return (-1); 628 629 if (swd.fail > 0) 630 rc = FMD_AGENT_RETIRE_FAIL; 631 else if (swd.async > 0) 632 rc = FMD_AGENT_RETIRE_ASYNC; 633 else 634 rc = FMD_AGENT_RETIRE_DONE; 635 636 return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc)); 637 } 638 639 /* ARGSUSED */ 640 int 641 unretire_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, 642 nvlist_t *in, nvlist_t **out) 643 { 644 struct strand_walk_data swd; 645 uint32_t rc; 646 647 if (version > TOPO_METH_UNRETIRE_VERSION) 648 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 649 650 if (walk_strands(mod, &swd, node, fmd_agent_cpu_unretire) == -1) 651 return (-1); 652 653 if (swd.fail > 0) 654 rc = FMD_AGENT_RETIRE_FAIL; 655 else if (swd.async > 0) 656 rc = FMD_AGENT_RETIRE_ASYNC; 657 else 658 rc = FMD_AGENT_RETIRE_DONE; 659 660 return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc)); 661 } 662 663 /* ARGSUSED */ 664 int 665 service_state_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, 666 nvlist_t *in, nvlist_t **out) 667 { 668 struct strand_walk_data swd; 669 uint32_t rc; 670 671 if (version > TOPO_METH_SERVICE_STATE_VERSION) 672 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 673 674 if (walk_strands(mod, &swd, node, fmd_agent_cpu_isretired) == -1) 675 return (-1); 676 677 if (swd.done > 0) 678 rc = (swd.fail + swd.async > 0) ? FMD_SERVICE_STATE_DEGRADED : 679 FMD_SERVICE_STATE_UNUSABLE; 680 else if (swd.async > 0) 681 rc = FMD_SERVICE_STATE_ISOLATE_PENDING; 682 else if (swd.fail > 0) 683 rc = FMD_SERVICE_STATE_OK; 684 else 685 rc = FMD_SERVICE_STATE_UNKNOWN; 686 687 return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc)); 688 } 689 690 /* ARGSUSED */ 691 int 692 unusable_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version, 693 nvlist_t *in, nvlist_t **out) 694 { 695 struct strand_walk_data swd; 696 uint32_t rc; 697 698 if (version > TOPO_METH_UNUSABLE_VERSION) 699 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 700 701 if (walk_strands(mod, &swd, node, fmd_agent_cpu_isretired) == -1) 702 return (-1); 703 704 rc = (swd.fail + swd.async > 0 || swd.done == 0) ? 0 : 1; 705 706 return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, rc)); 707 } 708 709 static boolean_t 710 is_page_fmri(nvlist_t *nvl) 711 { 712 nvlist_t *hcsp; 713 uint64_t val; 714 715 if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) == 0 && 716 (nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET, 717 &val) == 0 || 718 nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET, 719 &val) == 0 || 720 nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR, 721 &val) == 0 || 722 nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, 723 &val) == 0)) 724 return (B_TRUE); 725 726 return (B_FALSE); 727 } 728 729 /* ARGSUSED */ 730 int 731 ntv_page_retire(topo_mod_t *mod, tnode_t *node, topo_version_t version, 732 nvlist_t *in, nvlist_t **out) 733 { 734 fmd_agent_hdl_t *hdl; 735 uint32_t rc = FMD_AGENT_RETIRE_FAIL; 736 737 if (version > TOPO_METH_RETIRE_VERSION) 738 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 739 if (is_page_fmri(in)) { 740 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { 741 rc = fmd_agent_page_retire(hdl, in); 742 fmd_agent_close(hdl); 743 } 744 } 745 fmri_dprint(mod, "ntv_page_retire", rc, in); 746 return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc)); 747 } 748 749 /* ARGSUSED */ 750 int 751 ntv_page_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version, 752 nvlist_t *in, nvlist_t **out) 753 { 754 fmd_agent_hdl_t *hdl; 755 uint32_t rc = FMD_AGENT_RETIRE_FAIL; 756 757 if (version > TOPO_METH_UNRETIRE_VERSION) 758 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 759 if (is_page_fmri(in)) { 760 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { 761 rc = fmd_agent_page_unretire(hdl, in); 762 fmd_agent_close(hdl); 763 } 764 } 765 fmri_dprint(mod, "ntv_page_unretire", rc, in); 766 return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc)); 767 } 768 769 /* ARGSUSED */ 770 int 771 ntv_page_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 772 nvlist_t *in, nvlist_t **out) 773 { 774 fmd_agent_hdl_t *hdl; 775 uint32_t rc = FMD_SERVICE_STATE_UNKNOWN; 776 777 if (version > TOPO_METH_SERVICE_STATE_VERSION) 778 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 779 if (is_page_fmri(in)) { 780 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { 781 rc = fmd_agent_page_isretired(hdl, in); 782 fmd_agent_close(hdl); 783 if (rc == FMD_AGENT_RETIRE_DONE) 784 rc = FMD_SERVICE_STATE_UNUSABLE; 785 else if (rc == FMD_AGENT_RETIRE_FAIL) 786 rc = FMD_SERVICE_STATE_OK; 787 else if (rc == FMD_AGENT_RETIRE_ASYNC) 788 rc = FMD_SERVICE_STATE_ISOLATE_PENDING; 789 } 790 } 791 792 topo_mod_dprintf(mod, "ntv_page_service_state: rc = %u\n", rc); 793 return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc)); 794 } 795 796 /* ARGSUSED */ 797 int 798 ntv_page_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 799 nvlist_t *in, nvlist_t **out) 800 { 801 fmd_agent_hdl_t *hdl; 802 uint32_t rc = FMD_AGENT_RETIRE_FAIL; 803 804 if (version > TOPO_METH_UNUSABLE_VERSION) 805 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 806 if (is_page_fmri(in)) { 807 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) { 808 rc = fmd_agent_page_isretired(hdl, in); 809 fmd_agent_close(hdl); 810 } 811 } 812 topo_mod_dprintf(mod, "ntv_page_unusable: rc = %u\n", rc); 813 return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, 814 rc == FMD_AGENT_RETIRE_DONE ? 1 : 0)); 815 } 816 817 /* 818 * Determine whether or not we believe a chip has been replaced. While it's 819 * tempting to just do a straight up comparison of the FMRI and its serial 820 * number, things are not that straightforward. 821 * 822 * The presence of a serial number on the CPU is not always guaranteed. It is 823 * possible that systems firmware can hide the information required to generate 824 * a synthesized serial number or that it is strictly not present. As such, we 825 * will only declare something replaced when both the old and current resource 826 * have a serial number present. If it is missing for whatever reason, then we 827 * cannot assume anything about a replacement having occurred. 828 * 829 * This logic applies regardless of whether or not we have an FM-aware SMBIOS. 830 */ 831 int 832 chip_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 833 nvlist_t *in, nvlist_t **out) 834 { 835 nvlist_t *rsrc = NULL; 836 int err, ret; 837 char *old_serial, *new_serial; 838 839 if (version > TOPO_METH_REPLACED_VERSION) 840 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 841 842 if (topo_node_resource(node, &rsrc, &err) == -1) { 843 return (topo_mod_seterrno(mod, err)); 844 } 845 846 if (nvlist_lookup_string(rsrc, FM_FMRI_HC_SERIAL_ID, 847 &new_serial) != 0) { 848 ret = FMD_OBJ_STATE_UNKNOWN; 849 goto out; 850 } 851 852 if (nvlist_lookup_string(in, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0) { 853 ret = FMD_OBJ_STATE_UNKNOWN; 854 goto out; 855 } 856 857 if (strcmp(old_serial, new_serial) == 0) { 858 ret = FMD_OBJ_STATE_STILL_PRESENT; 859 } else { 860 ret = FMD_OBJ_STATE_REPLACED; 861 } 862 863 out: 864 nvlist_free(rsrc); 865 return (set_retnvl(mod, out, TOPO_METH_REPLACED_RET, ret)); 866 } 867 868 const char * 869 get_chip_brand(topo_mod_t *mod, kstat_ctl_t *kc, int32_t chipid) 870 { 871 kstat_t *ksp; 872 kstat_named_t *ks; 873 874 if ((ksp = kstat_lookup(kc, "cpu_info", chipid, NULL)) == NULL || 875 kstat_read(kc, ksp, NULL) == -1 || 876 (ks = kstat_data_lookup(ksp, "brand")) == NULL) { 877 topo_mod_dprintf(mod, "failed to read stat cpu_info:%d:brand", 878 chipid); 879 return (NULL); 880 } 881 return (topo_mod_strdup(mod, ks->value.str.addr.ptr)); 882 } 883