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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include <fm/fmd_api.h> 28 #include <fm/libtopo.h> 29 #include <sys/fm/protocol.h> 30 #include <cmd.h> 31 #include <string.h> 32 #include <cmd_hc_sun4v.h> 33 34 /* Using a global variable is safe because the DE is single threaded */ 35 36 nvlist_t *dimm_nvl; 37 nvlist_t *mb_nvl; 38 nvlist_t *rsc_nvl; 39 40 nvlist_t * 41 cmd_fault_add_location(fmd_hdl_t *hdl, nvlist_t *flt, const char *locstr) { 42 43 char *t, *s; 44 45 if (nvlist_lookup_string(flt, FM_FAULT_LOCATION, &t) == 0) 46 return (flt); /* already has location value */ 47 48 /* Replace occurrence of ": " with "/" to avoid confusing ILOM. */ 49 t = fmd_hdl_zalloc(hdl, strlen(locstr) + 1, FMD_SLEEP); 50 s = strstr(locstr, ": "); 51 if (s != NULL) { 52 (void) strncpy(t, locstr, s - locstr); 53 (void) strcat(t, "/"); 54 (void) strcat(t, s + 2); 55 } else { 56 (void) strcpy(t, locstr); 57 } 58 59 /* Also, remove any J number from end of this string. */ 60 s = strstr(t, "/J"); 61 if (s != NULL) 62 *s = '\0'; 63 64 if (nvlist_add_string(flt, FM_FAULT_LOCATION, t) != 0) 65 fmd_hdl_error(hdl, "unable to alloc location for fault\n"); 66 fmd_hdl_free(hdl, t, strlen(locstr) + 1); 67 return (flt); 68 } 69 70 typedef struct tr_ent { 71 const char *nac_component; 72 const char *hc_component; 73 } tr_ent_t; 74 75 static tr_ent_t tr_tbl[] = { 76 { "MB", "motherboard" }, 77 { "CPU", "cpuboard" }, 78 { "MEM", "memboard" }, 79 { "CMP", "chip" }, 80 { "BR", "branch" }, 81 { "CH", "dram-channel" }, 82 { "R", "rank" }, 83 { "D", "dimm" } 84 }; 85 86 #define tr_tbl_n sizeof (tr_tbl) / sizeof (tr_ent_t) 87 88 int 89 map_name(const char *p) { 90 int i; 91 92 for (i = 0; i < tr_tbl_n; i++) { 93 if (strncmp(p, tr_tbl[i].nac_component, 94 strlen(tr_tbl[i].nac_component)) == 0) 95 return (i); 96 } 97 return (-1); 98 } 99 100 int 101 cmd_count_components(const char *str, char sep) 102 { 103 int num = 0; 104 const char *cptr = str; 105 106 if (*cptr == sep) cptr++; /* skip initial sep */ 107 if (strlen(cptr) > 0) num = 1; 108 while ((cptr = strchr(cptr, sep)) != NULL) { 109 cptr++; 110 if (cptr == NULL || strcmp(cptr, "") == 0) break; 111 if (map_name(cptr) >= 0) num++; 112 } 113 return (num); 114 } 115 116 /* 117 * This version of breakup_components assumes that all component names which 118 * it sees are of the form: <nonnumeric piece><numeric piece> 119 * i.e. no embedded numerals in component name which have to be spelled out. 120 */ 121 122 int 123 cmd_breakup_components(char *str, char *sep, nvlist_t **hc_nvl) 124 { 125 char namebuf[64], instbuf[64]; 126 char *token, *tokbuf; 127 int i, j, namelen, instlen; 128 129 i = 0; 130 for (token = strtok_r(str, sep, &tokbuf); 131 token != NULL; 132 token = strtok_r(NULL, sep, &tokbuf)) { 133 namelen = strcspn(token, "0123456789"); 134 instlen = strspn(token+namelen, "0123456789"); 135 (void) strncpy(namebuf, token, namelen); 136 namebuf[namelen] = '\0'; 137 138 if ((j = map_name(namebuf)) < 0) 139 continue; /* skip names that don't map */ 140 141 if (instlen == 0) { 142 (void) strncpy(instbuf, "0", 2); 143 } else { 144 (void) strncpy(instbuf, token+namelen, instlen); 145 instbuf[instlen] = '\0'; 146 } 147 if (nvlist_add_string(hc_nvl[i], FM_FMRI_HC_NAME, 148 tr_tbl[j].hc_component) != 0 || 149 nvlist_add_string(hc_nvl[i], FM_FMRI_HC_ID, instbuf) != 0) 150 return (-1); 151 i++; 152 } 153 return (1); 154 } 155 156 char * 157 cmd_getfru_loc(fmd_hdl_t *hdl, nvlist_t *asru) { 158 159 char *fru_loc, *cpufru; 160 if (nvlist_lookup_string(asru, FM_FMRI_CPU_CPUFRU, &cpufru) == 0) { 161 fru_loc = strstr(cpufru, "MB"); 162 if (fru_loc != NULL) { 163 fmd_hdl_debug(hdl, "cmd_getfru_loc: fruloc=%s\n", 164 fru_loc); 165 return (fmd_hdl_strdup(hdl, fru_loc, FMD_SLEEP)); 166 } 167 } 168 fmd_hdl_debug(hdl, "cmd_getfru_loc: Default fruloc=empty string\n"); 169 return (fmd_hdl_strdup(hdl, EMPTY_STR, FMD_SLEEP)); 170 } 171 172 nvlist_t * 173 cmd_mkboard_fru(fmd_hdl_t *hdl, char *frustr, char *serialstr, char *partstr) { 174 175 char *nac, *nac_name; 176 int n, i, len; 177 nvlist_t *fru, **hc_list; 178 179 if (frustr == NULL) 180 return (NULL); 181 182 if ((nac_name = strstr(frustr, "MB")) == NULL) 183 return (NULL); 184 185 len = strlen(nac_name) + 1; 186 187 nac = fmd_hdl_zalloc(hdl, len, FMD_SLEEP); 188 (void) strcpy(nac, nac_name); 189 190 n = cmd_count_components(nac, '/'); 191 192 fmd_hdl_debug(hdl, "cmd_mkboard_fru: nac=%s components=%d\n", nac, n); 193 194 hc_list = fmd_hdl_zalloc(hdl, sizeof (nvlist_t *)*n, FMD_SLEEP); 195 196 for (i = 0; i < n; i++) { 197 (void) nvlist_alloc(&hc_list[i], 198 NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE, 0); 199 } 200 201 if (cmd_breakup_components(nac, "/", hc_list) < 0) { 202 for (i = 0; i < n; i++) { 203 if (hc_list[i] != NULL) 204 nvlist_free(hc_list[i]); 205 } 206 fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n); 207 fmd_hdl_free(hdl, nac, len); 208 return (NULL); 209 } 210 211 if (nvlist_alloc(&fru, NV_UNIQUE_NAME, 0) != 0) { 212 for (i = 0; i < n; i++) { 213 if (hc_list[i] != NULL) 214 nvlist_free(hc_list[i]); 215 } 216 fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n); 217 fmd_hdl_free(hdl, nac, len); 218 return (NULL); 219 } 220 221 if (nvlist_add_uint8(fru, FM_VERSION, FM_HC_SCHEME_VERSION) != 0 || 222 nvlist_add_string(fru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0 || 223 nvlist_add_string(fru, FM_FMRI_HC_ROOT, "") != 0 || 224 nvlist_add_uint32(fru, FM_FMRI_HC_LIST_SZ, n) != 0 || 225 nvlist_add_nvlist_array(fru, FM_FMRI_HC_LIST, hc_list, n) != 0) { 226 for (i = 0; i < n; i++) { 227 if (hc_list[i] != NULL) 228 nvlist_free(hc_list[i]); 229 } 230 fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n); 231 fmd_hdl_free(hdl, nac, len); 232 nvlist_free(fru); 233 return (NULL); 234 } 235 236 for (i = 0; i < n; i++) { 237 if (hc_list[i] != NULL) 238 nvlist_free(hc_list[i]); 239 } 240 fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n); 241 fmd_hdl_free(hdl, nac, len); 242 243 if ((serialstr != NULL && 244 nvlist_add_string(fru, FM_FMRI_HC_SERIAL_ID, serialstr) != 0) || 245 (partstr != NULL && 246 nvlist_add_string(fru, FM_FMRI_HC_PART, partstr) != 0)) { 247 nvlist_free(fru); 248 return (NULL); 249 } 250 251 return (fru); 252 } 253 254 nvlist_t * 255 cmd_boardfru_create_fault(fmd_hdl_t *hdl, nvlist_t *asru, const char *fltnm, 256 uint_t cert, char *loc) 257 { 258 nvlist_t *flt, *nvlfru; 259 char *serialstr, *partstr; 260 261 if ((loc == NULL) || (strcmp(loc, EMPTY_STR) == 0)) 262 return (NULL); 263 264 if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serialstr) != 0) 265 serialstr = NULL; 266 if (nvlist_lookup_string(asru, FM_FMRI_HC_PART, &partstr) != 0) 267 partstr = NULL; 268 269 nvlfru = cmd_mkboard_fru(hdl, loc, serialstr, partstr); 270 if (nvlfru == NULL) 271 return (NULL); 272 273 flt = cmd_nvl_create_fault(hdl, fltnm, cert, nvlfru, nvlfru, NULL); 274 flt = cmd_fault_add_location(hdl, flt, loc); 275 if (nvlfru != NULL) 276 nvlist_free(nvlfru); 277 return (flt); 278 } 279 280 /* find_mb -- find hardware platform motherboard within libtopo */ 281 282 /* ARGSUSED */ 283 static int 284 find_mb(topo_hdl_t *thp, tnode_t *node, void *arg) 285 { 286 int err; 287 nvlist_t *rsrc, **hcl; 288 char *name; 289 uint_t n; 290 291 if (topo_node_resource(node, &rsrc, &err) < 0) { 292 return (TOPO_WALK_NEXT); /* no resource, try next */ 293 } 294 295 if (nvlist_lookup_nvlist_array(rsrc, FM_FMRI_HC_LIST, &hcl, &n) < 0) { 296 nvlist_free(rsrc); 297 return (TOPO_WALK_NEXT); 298 } 299 300 if (nvlist_lookup_string(hcl[0], FM_FMRI_HC_NAME, &name) != 0) { 301 nvlist_free(rsrc); 302 return (TOPO_WALK_NEXT); 303 } 304 305 if (strcmp(name, "motherboard") != 0) { 306 nvlist_free(rsrc); 307 return (TOPO_WALK_NEXT); /* not MB hc list, try next */ 308 } 309 310 (void) nvlist_dup(rsrc, &mb_nvl, NV_UNIQUE_NAME); 311 312 nvlist_free(rsrc); 313 return (TOPO_WALK_TERMINATE); /* if no space, give up */ 314 } 315 316 /* init_mb -- read hardware platform motherboard from libtopo */ 317 318 nvlist_t * 319 init_mb(fmd_hdl_t *hdl) 320 { 321 topo_hdl_t *thp; 322 topo_walk_t *twp; 323 int err; 324 325 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) 326 return (NULL); 327 if ((twp = topo_walk_init(thp, 328 FM_FMRI_SCHEME_HC, find_mb, NULL, &err)) 329 == NULL) { 330 fmd_hdl_topo_rele(hdl, thp); 331 return (NULL); 332 } 333 (void) topo_walk_step(twp, TOPO_WALK_CHILD); 334 topo_walk_fini(twp); 335 fmd_hdl_topo_rele(hdl, thp); 336 return (mb_nvl); 337 } 338 339 /*ARGSUSED*/ 340 static int 341 find_dimm_sn_mem(topo_hdl_t *thp, tnode_t *node, void *arg) 342 { 343 int err; 344 uint_t n; 345 nvlist_t *rsrc; 346 char **sn; 347 348 if (topo_node_resource(node, &rsrc, &err) < 0) { 349 return (TOPO_WALK_NEXT); /* no resource, try next */ 350 } 351 if (nvlist_lookup_string_array(rsrc, 352 FM_FMRI_HC_SERIAL_ID, &sn, &n) != 0) { 353 nvlist_free(rsrc); 354 return (TOPO_WALK_NEXT); 355 } 356 if (strcmp(*sn, (char *)arg) != 0) { 357 nvlist_free(rsrc); 358 return (TOPO_WALK_NEXT); 359 } 360 (void) nvlist_dup(rsrc, &dimm_nvl, NV_UNIQUE_NAME); 361 nvlist_free(rsrc); 362 return (TOPO_WALK_TERMINATE); /* if no space, give up */ 363 } 364 365 /*ARGSUSED*/ 366 static int 367 find_dimm_sn_hc(topo_hdl_t *thp, tnode_t *node, void *arg) 368 { 369 int err; 370 nvlist_t *fru; 371 char *sn; 372 373 if (topo_node_fru(node, &fru, 0, &err) < 0) { 374 return (TOPO_WALK_NEXT); /* no fru, try next */ 375 } 376 if (nvlist_lookup_string(fru, FM_FMRI_HC_SERIAL_ID, &sn) != 0) { 377 nvlist_free(fru); 378 return (TOPO_WALK_NEXT); 379 } 380 if (strcmp(sn, (char *)arg) != 0) { 381 nvlist_free(fru); 382 return (TOPO_WALK_NEXT); 383 } 384 (void) nvlist_dup(fru, &dimm_nvl, NV_UNIQUE_NAME); 385 nvlist_free(fru); 386 return (TOPO_WALK_TERMINATE); /* if no space, give up */ 387 } 388 389 /* cmd_find_dimm_by_sn -- find fmri by sn from libtopo */ 390 391 nvlist_t * 392 cmd_find_dimm_by_sn(fmd_hdl_t *hdl, char *schemename, char *sn) 393 { 394 topo_hdl_t *thp; 395 topo_walk_t *twp; 396 int err; 397 398 dimm_nvl = NULL; 399 400 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) 401 return (NULL); 402 if (strcmp(schemename, FM_FMRI_SCHEME_MEM) == 0) { 403 if ((twp = topo_walk_init(thp, 404 schemename, find_dimm_sn_mem, sn, &err)) == NULL) { 405 fmd_hdl_topo_rele(hdl, thp); 406 return (NULL); 407 } 408 } else { 409 if ((twp = topo_walk_init(thp, 410 schemename, find_dimm_sn_hc, sn, &err)) == NULL) { 411 fmd_hdl_topo_rele(hdl, thp); 412 return (NULL); 413 } 414 } 415 (void) topo_walk_step(twp, TOPO_WALK_CHILD); 416 topo_walk_fini(twp); 417 fmd_hdl_topo_rele(hdl, thp); 418 return (dimm_nvl); 419 } 420 421 typedef struct cpuid { 422 char serial[100]; 423 char id[10]; 424 } cpuid_t; 425 426 /*ARGSUSED*/ 427 static int 428 find_cpu_rsc_by_sn(topo_hdl_t *thp, tnode_t *node, void *arg) 429 { 430 int err; 431 nvlist_t *rsc; 432 cpuid_t *rscid = (cpuid_t *)arg; 433 char *sn, *name, *id; 434 nvlist_t **hcl; 435 uint_t n; 436 437 if (topo_node_resource(node, &rsc, &err) < 0) { 438 return (TOPO_WALK_NEXT); /* no rsc, try next */ 439 } 440 441 if (nvlist_lookup_string(rsc, FM_FMRI_HC_SERIAL_ID, &sn) != 0) { 442 nvlist_free(rsc); 443 return (TOPO_WALK_NEXT); 444 } 445 if (strcmp(rscid->serial, sn) != 0) { 446 nvlist_free(rsc); 447 return (TOPO_WALK_NEXT); 448 } 449 450 if (nvlist_lookup_nvlist_array(rsc, FM_FMRI_HC_LIST, &hcl, &n) != 0) { 451 nvlist_free(rsc); 452 return (TOPO_WALK_NEXT); 453 } 454 455 if ((nvlist_lookup_string(hcl[n - 1], FM_FMRI_HC_NAME, &name) != 0) || 456 (nvlist_lookup_string(hcl[n - 1], FM_FMRI_HC_ID, &id) != 0)) { 457 nvlist_free(rsc); 458 return (TOPO_WALK_NEXT); 459 } 460 461 if ((strcmp(name, "cpu") != 0) || (strcmp(rscid->id, id) != 0)) { 462 nvlist_free(rsc); 463 return (TOPO_WALK_NEXT); 464 } 465 466 (void) nvlist_dup(rsc, &rsc_nvl, NV_UNIQUE_NAME); 467 468 nvlist_free(rsc); 469 return (TOPO_WALK_TERMINATE); /* if no space, give up */ 470 } 471 472 nvlist_t * 473 cmd_find_cpu_rsc_by_sn(fmd_hdl_t *hdl, cpuid_t *cpuid) 474 { 475 topo_hdl_t *thp; 476 topo_walk_t *twp; 477 int err; 478 479 rsc_nvl = NULL; 480 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) 481 return (NULL); 482 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, 483 find_cpu_rsc_by_sn, cpuid, &err)) == NULL) { 484 fmd_hdl_topo_rele(hdl, thp); 485 return (NULL); 486 } 487 (void) topo_walk_step(twp, TOPO_WALK_CHILD); 488 topo_walk_fini(twp); 489 fmd_hdl_topo_rele(hdl, thp); 490 return (rsc_nvl); 491 } 492 493 nvlist_t * 494 get_cpu_fault_resource(fmd_hdl_t *hdl, nvlist_t *asru) 495 { 496 uint32_t cpu; 497 uint64_t serint; 498 char serial[64]; 499 nvlist_t *rsc = NULL; 500 cpuid_t cpuid; 501 char strid[10]; 502 503 if (nvlist_lookup_uint64(asru, FM_FMRI_CPU_SERIAL_ID, &serint) != 0 || 504 nvlist_lookup_uint32(asru, FM_FMRI_CPU_ID, &cpu) != 0) 505 return (rsc); 506 507 (void) snprintf(serial, sizeof (serial), "%llx", serint); 508 (void) snprintf(strid, sizeof (strid), "%d", cpu); 509 510 (void) strcpy(cpuid.serial, serial); 511 (void) strcpy(cpuid.id, strid); 512 513 rsc = cmd_find_cpu_rsc_by_sn(hdl, &cpuid); 514 return (rsc); 515 } 516 517 /*ARGSUSED*/ 518 static int 519 find_mem_rsc_hc(topo_hdl_t *thp, tnode_t *node, void *arg) 520 { 521 int err; 522 nvlist_t *rsc; 523 char *sn; 524 525 if (topo_node_resource(node, &rsc, &err) < 0) { 526 return (TOPO_WALK_NEXT); /* no rsc, try next */ 527 } 528 if (nvlist_lookup_string(rsc, FM_FMRI_HC_SERIAL_ID, &sn) != 0) { 529 nvlist_free(rsc); 530 return (TOPO_WALK_NEXT); 531 } 532 if (strcmp(sn, (char *)arg) != 0) { 533 nvlist_free(rsc); 534 return (TOPO_WALK_NEXT); 535 } 536 (void) nvlist_dup(rsc, &rsc_nvl, NV_UNIQUE_NAME); 537 nvlist_free(rsc); 538 return (TOPO_WALK_TERMINATE); /* if no space, give up */ 539 } 540 541 nvlist_t * 542 cmd_find_mem_rsc_by_sn(fmd_hdl_t *hdl, char *sn) 543 { 544 topo_hdl_t *thp; 545 topo_walk_t *twp; 546 int err; 547 548 rsc_nvl = NULL; 549 550 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) 551 return (NULL); 552 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, 553 find_mem_rsc_hc, sn, &err)) == NULL) { 554 fmd_hdl_topo_rele(hdl, thp); 555 return (NULL); 556 } 557 (void) topo_walk_step(twp, TOPO_WALK_CHILD); 558 topo_walk_fini(twp); 559 fmd_hdl_topo_rele(hdl, thp); 560 return (rsc_nvl); 561 } 562 563 nvlist_t * 564 get_mem_fault_resource(fmd_hdl_t *hdl, nvlist_t *fru) 565 { 566 char *sn; 567 uint_t n; 568 char **snarray; 569 570 if (nvlist_lookup_string(fru, FM_FMRI_HC_SERIAL_ID, &sn) == 0) 571 return (cmd_find_mem_rsc_by_sn(hdl, sn)); 572 573 /* 574 * T1 platform fru is in mem scheme 575 */ 576 if (nvlist_lookup_string_array(fru, FM_FMRI_MEM_SERIAL_ID, 577 &snarray, &n) == 0) 578 return (cmd_find_mem_rsc_by_sn(hdl, snarray[0])); 579 580 return (NULL); 581 } 582 583 int 584 is_T1_platform(nvlist_t *asru) 585 { 586 char *unum; 587 if (nvlist_lookup_string(asru, FM_FMRI_MEM_UNUM, &unum) == 0) { 588 if (strstr(unum, "BR") == NULL) 589 return (1); 590 } 591 return (0); 592 } 593 594 nvlist_t * 595 cmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t cert, 596 nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc) 597 { 598 nvlist_t *fllist; 599 uint64_t offset, phyaddr; 600 nvlist_t *hsp = NULL; 601 602 rsrc = NULL; 603 (void) nvlist_add_nvlist(fru, FM_FMRI_AUTHORITY, 604 cmd.cmd_auth); /* not an error if this fails */ 605 606 if (strstr(class, "fault.memory.") != NULL) { 607 /* 608 * For T1 platform fault.memory.bank and fault.memory.dimm, 609 * do not issue the hc schmem for resource and fru 610 */ 611 if (is_T1_platform(asru) && (strstr(class, ".page") == NULL)) { 612 fllist = fmd_nvl_create_fault(hdl, class, cert, asru, 613 fru, fru); 614 return (fllist); 615 } 616 617 rsrc = get_mem_fault_resource(hdl, fru); 618 /* 619 * Need to append the phyaddr & offset into the 620 * hc-specific of the fault.memory.page resource 621 */ 622 if ((rsrc != NULL) && strstr(class, ".page") != NULL) { 623 if (nvlist_alloc(&hsp, NV_UNIQUE_NAME, 0) == 0) { 624 if (nvlist_lookup_uint64(asru, 625 FM_FMRI_MEM_PHYSADDR, &phyaddr) == 0) 626 (void) (nvlist_add_uint64(hsp, 627 FM_FMRI_MEM_PHYSADDR, 628 phyaddr)); 629 630 if (nvlist_lookup_uint64(asru, 631 FM_FMRI_MEM_OFFSET, &offset) == 0) 632 (void) nvlist_add_uint64(hsp, 633 FM_FMRI_HC_SPECIFIC_OFFSET, offset); 634 635 (void) nvlist_add_nvlist(rsrc, 636 FM_FMRI_HC_SPECIFIC, hsp); 637 } 638 } 639 fllist = fmd_nvl_create_fault(hdl, class, cert, asru, 640 fru, rsrc); 641 if (hsp != NULL) 642 nvlist_free(hsp); 643 } else { 644 rsrc = get_cpu_fault_resource(hdl, asru); 645 fllist = fmd_nvl_create_fault(hdl, class, cert, asru, 646 fru, rsrc); 647 } 648 649 if (rsrc != NULL) 650 nvlist_free(rsrc); 651 652 return (fllist); 653 }