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