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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Fault Management Architecture (FMA) Resource and Protocol Support 31 * 32 * The routines contained herein provide services to support kernel subsystems 33 * in publishing fault management telemetry (see PSARC 2002/412 and 2003/089). 34 * 35 * Name-Value Pair Lists 36 * 37 * The embodiment of an FMA protocol element (event, fmri or authority) is a 38 * name-value pair list (nvlist_t). FMA-specific nvlist construtor and 39 * destructor functions, fm_nvlist_create() and fm_nvlist_destroy(), are used 40 * to create an nvpair list using custom allocators. Callers may choose to 41 * allocate either from the kernel memory allocator, or from a preallocated 42 * buffer, useful in constrained contexts like high-level interrupt routines. 43 * 44 * Protocol Event and FMRI Construction 45 * 46 * Convenience routines are provided to construct nvlist events according to 47 * the FMA Event Protocol and Naming Schema specification for ereports and 48 * FMRIs for the dev, cpu, hc, mem, legacy hc and de schemes. 49 * 50 * ENA Manipulation 51 * 52 * Routines to generate ENA formats 0, 1 and 2 are available as well as 53 * routines to increment formats 1 and 2. Individual fields within the 54 * ENA are extractable via fm_ena_time_get(), fm_ena_id_get(), 55 * fm_ena_format_get() and fm_ena_gen_get(). 56 */ 57 58 #include <sys/types.h> 59 #include <sys/time.h> 60 #include <sys/sysevent.h> 61 #include <sys/sysevent_impl.h> 62 #include <sys/nvpair.h> 63 #include <sys/cmn_err.h> 64 #include <sys/cpuvar.h> 65 #include <sys/sysmacros.h> 66 #include <sys/systm.h> 67 #include <sys/ddifm.h> 68 #include <sys/ddifm_impl.h> 69 #include <sys/spl.h> 70 #include <sys/dumphdr.h> 71 #include <sys/compress.h> 72 #include <sys/cpuvar.h> 73 #include <sys/console.h> 74 #include <sys/panic.h> 75 #include <sys/kobj.h> 76 #include <sys/sunddi.h> 77 #include <sys/systeminfo.h> 78 #include <sys/sysevent/eventdefs.h> 79 #include <sys/fm/util.h> 80 #include <sys/fm/protocol.h> 81 82 /* 83 * URL and SUNW-MSG-ID value to display for fm_panic(), defined below. These 84 * values must be kept in sync with the FMA source code in usr/src/cmd/fm. 85 */ 86 static const char *fm_url = "http://www.sun.com/msg"; 87 static const char *fm_msgid = "SUNOS-8000-0G"; 88 static char *volatile fm_panicstr = NULL; 89 90 errorq_t *ereport_errorq; 91 void *ereport_dumpbuf; 92 size_t ereport_dumplen; 93 94 static uint_t ereport_chanlen = ERPT_EVCH_MAX; 95 static evchan_t *ereport_chan = NULL; 96 static ulong_t ereport_qlen = 0; 97 static size_t ereport_size = 0; 98 static int ereport_cols = 80; 99 100 /* 101 * Common fault management kstats to record ereport generation 102 * failures 103 */ 104 105 struct erpt_kstat { 106 kstat_named_t erpt_dropped; /* num erpts dropped on post */ 107 kstat_named_t erpt_set_failed; /* num erpt set failures */ 108 kstat_named_t fmri_set_failed; /* num fmri set failures */ 109 kstat_named_t payload_set_failed; /* num payload set failures */ 110 }; 111 112 static struct erpt_kstat erpt_kstat_data = { 113 { "erpt-dropped", KSTAT_DATA_UINT64 }, 114 { "erpt-set-failed", KSTAT_DATA_UINT64 }, 115 { "fmri-set-failed", KSTAT_DATA_UINT64 }, 116 { "payload-set-failed", KSTAT_DATA_UINT64 } 117 }; 118 119 /*ARGSUSED*/ 120 static void 121 fm_drain(void *private, void *data, errorq_elem_t *eep) 122 { 123 nvlist_t *nvl = errorq_elem_nvl(ereport_errorq, eep); 124 125 if (!panicstr) 126 (void) fm_ereport_post(nvl, EVCH_TRYHARD); 127 else 128 fm_nvprint(nvl); 129 } 130 131 void 132 fm_init(void) 133 { 134 kstat_t *ksp; 135 136 (void) sysevent_evc_bind(FM_ERROR_CHAN, 137 &ereport_chan, EVCH_CREAT | EVCH_HOLD_PEND); 138 139 (void) sysevent_evc_control(ereport_chan, 140 EVCH_SET_CHAN_LEN, &ereport_chanlen); 141 142 if (ereport_qlen == 0) 143 ereport_qlen = ERPT_MAX_ERRS * MAX(max_ncpus, 4); 144 145 if (ereport_size == 0) 146 ereport_size = ERPT_DATA_SZ; 147 148 ereport_errorq = errorq_nvcreate("fm_ereport_queue", 149 (errorq_func_t)fm_drain, NULL, ereport_qlen, ereport_size, 150 FM_ERR_PIL, ERRORQ_VITAL); 151 if (ereport_errorq == NULL) 152 panic("failed to create required ereport error queue"); 153 154 ereport_dumpbuf = kmem_alloc(ereport_size, KM_SLEEP); 155 ereport_dumplen = ereport_size; 156 157 /* Initialize ereport allocation and generation kstats */ 158 ksp = kstat_create("unix", 0, "fm", "misc", KSTAT_TYPE_NAMED, 159 sizeof (struct erpt_kstat) / sizeof (kstat_named_t), 160 KSTAT_FLAG_VIRTUAL); 161 162 if (ksp != NULL) { 163 ksp->ks_data = &erpt_kstat_data; 164 kstat_install(ksp); 165 } else { 166 cmn_err(CE_NOTE, "failed to create fm/misc kstat\n"); 167 168 } 169 } 170 171 /* 172 * Formatting utility function for fm_nvprintr. We attempt to wrap chunks of 173 * output so they aren't split across console lines, and return the end column. 174 */ 175 /*PRINTFLIKE4*/ 176 static int 177 fm_printf(int depth, int c, int cols, const char *format, ...) 178 { 179 va_list ap; 180 int width; 181 char c1; 182 183 va_start(ap, format); 184 width = vsnprintf(&c1, sizeof (c1), format, ap); 185 va_end(ap); 186 187 if (c + width >= cols) { 188 console_printf("\n\r"); 189 c = 0; 190 if (format[0] != ' ' && depth > 0) { 191 console_printf(" "); 192 c++; 193 } 194 } 195 196 va_start(ap, format); 197 console_vprintf(format, ap); 198 va_end(ap); 199 200 return ((c + width) % cols); 201 } 202 203 /* 204 * Recursively print a nvlist in the specified column width and return the 205 * column we end up in. This function is called recursively by fm_nvprint(), 206 * below. We generically format the entire nvpair using hexadecimal 207 * integers and strings, and elide any integer arrays. Arrays are basically 208 * used for cache dumps right now, so we suppress them so as not to overwhelm 209 * the amount of console output we produce at panic time. This can be further 210 * enhanced as FMA technology grows based upon the needs of consumers. All 211 * FMA telemetry is logged using the dump device transport, so the console 212 * output serves only as a fallback in case this procedure is unsuccessful. 213 */ 214 static int 215 fm_nvprintr(nvlist_t *nvl, int d, int c, int cols) 216 { 217 nvpair_t *nvp; 218 219 for (nvp = nvlist_next_nvpair(nvl, NULL); 220 nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) { 221 222 data_type_t type = nvpair_type(nvp); 223 const char *name = nvpair_name(nvp); 224 225 boolean_t b; 226 uint8_t i8; 227 uint16_t i16; 228 uint32_t i32; 229 uint64_t i64; 230 char *str; 231 nvlist_t *cnv; 232 233 if (strcmp(name, FM_CLASS) == 0) 234 continue; /* already printed by caller */ 235 236 c = fm_printf(d, c, cols, " %s=", name); 237 238 switch (type) { 239 case DATA_TYPE_BOOLEAN: 240 c = fm_printf(d + 1, c, cols, " 1"); 241 break; 242 243 case DATA_TYPE_BOOLEAN_VALUE: 244 (void) nvpair_value_boolean_value(nvp, &b); 245 c = fm_printf(d + 1, c, cols, b ? "1" : "0"); 246 break; 247 248 case DATA_TYPE_BYTE: 249 (void) nvpair_value_byte(nvp, &i8); 250 c = fm_printf(d + 1, c, cols, "%x", i8); 251 break; 252 253 case DATA_TYPE_INT8: 254 (void) nvpair_value_int8(nvp, (void *)&i8); 255 c = fm_printf(d + 1, c, cols, "%x", i8); 256 break; 257 258 case DATA_TYPE_UINT8: 259 (void) nvpair_value_uint8(nvp, &i8); 260 c = fm_printf(d + 1, c, cols, "%x", i8); 261 break; 262 263 case DATA_TYPE_INT16: 264 (void) nvpair_value_int16(nvp, (void *)&i16); 265 c = fm_printf(d + 1, c, cols, "%x", i16); 266 break; 267 268 case DATA_TYPE_UINT16: 269 (void) nvpair_value_uint16(nvp, &i16); 270 c = fm_printf(d + 1, c, cols, "%x", i16); 271 break; 272 273 case DATA_TYPE_INT32: 274 (void) nvpair_value_int32(nvp, (void *)&i32); 275 c = fm_printf(d + 1, c, cols, "%x", i32); 276 break; 277 278 case DATA_TYPE_UINT32: 279 (void) nvpair_value_uint32(nvp, &i32); 280 c = fm_printf(d + 1, c, cols, "%x", i32); 281 break; 282 283 case DATA_TYPE_INT64: 284 (void) nvpair_value_int64(nvp, (void *)&i64); 285 c = fm_printf(d + 1, c, cols, "%llx", 286 (u_longlong_t)i64); 287 break; 288 289 case DATA_TYPE_UINT64: 290 (void) nvpair_value_uint64(nvp, &i64); 291 c = fm_printf(d + 1, c, cols, "%llx", 292 (u_longlong_t)i64); 293 break; 294 295 case DATA_TYPE_HRTIME: 296 (void) nvpair_value_hrtime(nvp, (void *)&i64); 297 c = fm_printf(d + 1, c, cols, "%llx", 298 (u_longlong_t)i64); 299 break; 300 301 case DATA_TYPE_STRING: 302 (void) nvpair_value_string(nvp, &str); 303 c = fm_printf(d + 1, c, cols, "\"%s\"", 304 str ? str : "<NULL>"); 305 break; 306 307 case DATA_TYPE_NVLIST: 308 c = fm_printf(d + 1, c, cols, "["); 309 (void) nvpair_value_nvlist(nvp, &cnv); 310 c = fm_nvprintr(cnv, d + 1, c, cols); 311 c = fm_printf(d + 1, c, cols, " ]"); 312 break; 313 314 case DATA_TYPE_BOOLEAN_ARRAY: 315 case DATA_TYPE_BYTE_ARRAY: 316 case DATA_TYPE_INT8_ARRAY: 317 case DATA_TYPE_UINT8_ARRAY: 318 case DATA_TYPE_INT16_ARRAY: 319 case DATA_TYPE_UINT16_ARRAY: 320 case DATA_TYPE_INT32_ARRAY: 321 case DATA_TYPE_UINT32_ARRAY: 322 case DATA_TYPE_INT64_ARRAY: 323 case DATA_TYPE_UINT64_ARRAY: 324 case DATA_TYPE_STRING_ARRAY: 325 case DATA_TYPE_NVLIST_ARRAY: 326 c = fm_printf(d + 1, c, cols, "[...]"); 327 break; 328 case DATA_TYPE_UNKNOWN: 329 c = fm_printf(d + 1, c, cols, "<unknown>"); 330 break; 331 } 332 } 333 334 return (c); 335 } 336 337 void 338 fm_nvprint(nvlist_t *nvl) 339 { 340 char *class; 341 int c = 0; 342 343 console_printf("\r"); 344 345 if (nvlist_lookup_string(nvl, FM_CLASS, &class) == 0) 346 c = fm_printf(0, c, ereport_cols, "%s", class); 347 348 if (fm_nvprintr(nvl, 0, c, ereport_cols) != 0) 349 console_printf("\n"); 350 351 console_printf("\n"); 352 } 353 354 /* 355 * Wrapper for panic() that first produces an FMA-style message for admins. 356 * Normally such messages are generated by fmd(1M)'s syslog-msgs agent: this 357 * is the one exception to that rule and the only error that gets messaged. 358 * This function is intended for use by subsystems that have detected a fatal 359 * error and enqueued appropriate ereports and wish to then force a panic. 360 */ 361 /*PRINTFLIKE1*/ 362 void 363 fm_panic(const char *format, ...) 364 { 365 va_list ap; 366 367 (void) casptr((void *)&fm_panicstr, NULL, (void *)format); 368 va_start(ap, format); 369 vpanic(format, ap); 370 va_end(ap); 371 } 372 373 /* 374 * Print any appropriate FMA banner message before the panic message. This 375 * function is called by panicsys() and prints the message for fm_panic(). 376 * We print the message here so that it comes after the system is quiesced. 377 * A one-line summary is recorded in the log only (cmn_err(9F) with "!" prefix). 378 * The rest of the message is for the console only and not needed in the log, 379 * so it is printed using console_printf(). We break it up into multiple 380 * chunks so as to avoid overflowing any small legacy prom_printf() buffers. 381 */ 382 void 383 fm_banner(void) 384 { 385 timespec_t tod; 386 hrtime_t now; 387 388 if (!fm_panicstr) 389 return; /* panic was not initiated by fm_panic(); do nothing */ 390 391 if (panicstr) { 392 tod = panic_hrestime; 393 now = panic_hrtime; 394 } else { 395 gethrestime(&tod); 396 now = gethrtime_waitfree(); 397 } 398 399 cmn_err(CE_NOTE, "!SUNW-MSG-ID: %s, " 400 "TYPE: Error, VER: 1, SEVERITY: Major\n", fm_msgid); 401 402 console_printf( 403 "\n\rSUNW-MSG-ID: %s, TYPE: Error, VER: 1, SEVERITY: Major\n" 404 "EVENT-TIME: 0x%lx.0x%lx (0x%llx)\n", 405 fm_msgid, tod.tv_sec, tod.tv_nsec, (u_longlong_t)now); 406 407 console_printf( 408 "PLATFORM: %s, CSN: -, HOSTNAME: %s\n" 409 "SOURCE: %s, REV: %s %s\n", 410 platform, utsname.nodename, utsname.sysname, 411 utsname.release, utsname.version); 412 413 console_printf( 414 "DESC: Errors have been detected that require a reboot to ensure system\n" 415 "integrity. See %s/%s for more information.\n", 416 fm_url, fm_msgid); 417 418 console_printf( 419 "AUTO-RESPONSE: Solaris will attempt to save and diagnose the error telemetry\n" 420 "IMPACT: The system will sync files, save a crash dump if needed, and reboot\n" 421 "REC-ACTION: Save the error summary below in case telemetry cannot be saved\n"); 422 423 console_printf("\n"); 424 } 425 426 /* 427 * Utility function to write all of the pending ereports to the dump device. 428 * This function is called at either normal reboot or panic time, and simply 429 * iterates over the in-transit messages in the ereport sysevent channel. 430 */ 431 void 432 fm_ereport_dump(void) 433 { 434 evchanq_t *chq; 435 sysevent_t *sep; 436 erpt_dump_t ed; 437 438 timespec_t tod; 439 hrtime_t now; 440 char *buf; 441 size_t len; 442 443 if (panicstr) { 444 tod = panic_hrestime; 445 now = panic_hrtime; 446 } else { 447 if (ereport_errorq != NULL) 448 errorq_drain(ereport_errorq); 449 gethrestime(&tod); 450 now = gethrtime_waitfree(); 451 } 452 453 /* 454 * In the panic case, sysevent_evc_walk_init() will return NULL. 455 */ 456 if ((chq = sysevent_evc_walk_init(ereport_chan, NULL)) == NULL && 457 !panicstr) 458 return; /* event channel isn't initialized yet */ 459 460 while ((sep = sysevent_evc_walk_step(chq)) != NULL) { 461 if ((buf = sysevent_evc_event_attr(sep, &len)) == NULL) 462 break; 463 464 ed.ed_magic = ERPT_MAGIC; 465 ed.ed_chksum = checksum32(buf, len); 466 ed.ed_size = (uint32_t)len; 467 ed.ed_pad = 0; 468 ed.ed_hrt_nsec = SE_TIME(sep); 469 ed.ed_hrt_base = now; 470 ed.ed_tod_base.sec = tod.tv_sec; 471 ed.ed_tod_base.nsec = tod.tv_nsec; 472 473 dumpvp_write(&ed, sizeof (ed)); 474 dumpvp_write(buf, len); 475 } 476 477 sysevent_evc_walk_fini(chq); 478 } 479 480 /* 481 * Post an error report (ereport) to the sysevent error channel. The error 482 * channel must be established with a prior call to sysevent_evc_create() 483 * before publication may occur. 484 */ 485 void 486 fm_ereport_post(nvlist_t *ereport, int evc_flag) 487 { 488 size_t nvl_size = 0; 489 evchan_t *error_chan; 490 491 (void) nvlist_size(ereport, &nvl_size, NV_ENCODE_NATIVE); 492 if (nvl_size > ERPT_DATA_SZ || nvl_size == 0) { 493 atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1); 494 return; 495 } 496 497 if (sysevent_evc_bind(FM_ERROR_CHAN, &error_chan, 498 EVCH_CREAT|EVCH_HOLD_PEND) != 0) { 499 atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1); 500 return; 501 } 502 503 if (sysevent_evc_publish(error_chan, EC_FM, ESC_FM_ERROR, 504 SUNW_VENDOR, FM_PUB, ereport, evc_flag) != 0) { 505 atomic_add_64(&erpt_kstat_data.erpt_dropped.value.ui64, 1); 506 sysevent_evc_unbind(error_chan); 507 return; 508 } 509 sysevent_evc_unbind(error_chan); 510 } 511 512 /* 513 * Wrapppers for FM nvlist allocators 514 */ 515 /* ARGSUSED */ 516 static void * 517 i_fm_alloc(nv_alloc_t *nva, size_t size) 518 { 519 return (kmem_zalloc(size, KM_SLEEP)); 520 } 521 522 /* ARGSUSED */ 523 static void 524 i_fm_free(nv_alloc_t *nva, void *buf, size_t size) 525 { 526 kmem_free(buf, size); 527 } 528 529 const nv_alloc_ops_t fm_mem_alloc_ops = { 530 NULL, 531 NULL, 532 i_fm_alloc, 533 i_fm_free, 534 NULL 535 }; 536 537 /* 538 * Create and initialize a new nv_alloc_t for a fixed buffer, buf. A pointer 539 * to the newly allocated nv_alloc_t structure is returned upon success or NULL 540 * is returned to indicate that the nv_alloc structure could not be created. 541 */ 542 nv_alloc_t * 543 fm_nva_xcreate(char *buf, size_t bufsz) 544 { 545 nv_alloc_t *nvhdl = kmem_zalloc(sizeof (nv_alloc_t), KM_SLEEP); 546 547 if (bufsz == 0 || nv_alloc_init(nvhdl, nv_fixed_ops, buf, bufsz) != 0) { 548 kmem_free(nvhdl, sizeof (nv_alloc_t)); 549 return (NULL); 550 } 551 552 return (nvhdl); 553 } 554 555 /* 556 * Destroy a previously allocated nv_alloc structure. The fixed buffer 557 * associated with nva must be freed by the caller. 558 */ 559 void 560 fm_nva_xdestroy(nv_alloc_t *nva) 561 { 562 nv_alloc_fini(nva); 563 kmem_free(nva, sizeof (nv_alloc_t)); 564 } 565 566 /* 567 * Create a new nv list. A pointer to a new nv list structure is returned 568 * upon success or NULL is returned to indicate that the structure could 569 * not be created. The newly created nv list is created and managed by the 570 * operations installed in nva. If nva is NULL, the default FMA nva 571 * operations are installed and used. 572 * 573 * When called from the kernel and nva == NULL, this function must be called 574 * from passive kernel context with no locks held that can prevent a 575 * sleeping memory allocation from occurring. Otherwise, this function may 576 * be called from other kernel contexts as long a valid nva created via 577 * fm_nva_create() is supplied. 578 */ 579 nvlist_t * 580 fm_nvlist_create(nv_alloc_t *nva) 581 { 582 int hdl_alloced = 0; 583 nvlist_t *nvl; 584 nv_alloc_t *nvhdl; 585 586 if (nva == NULL) { 587 nvhdl = kmem_zalloc(sizeof (nv_alloc_t), KM_SLEEP); 588 589 if (nv_alloc_init(nvhdl, &fm_mem_alloc_ops, NULL, 0) != 0) { 590 kmem_free(nvhdl, sizeof (nv_alloc_t)); 591 return (NULL); 592 } 593 hdl_alloced = 1; 594 } else { 595 nvhdl = nva; 596 } 597 598 if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, nvhdl) != 0) { 599 if (hdl_alloced) { 600 kmem_free(nvhdl, sizeof (nv_alloc_t)); 601 nv_alloc_fini(nvhdl); 602 } 603 return (NULL); 604 } 605 606 return (nvl); 607 } 608 609 /* 610 * Destroy a previously allocated nvlist structure. flag indicates whether 611 * or not the associated nva structure should be freed (FM_NVA_FREE) or 612 * retained (FM_NVA_RETAIN). Retaining the nv alloc structure allows 613 * it to be re-used for future nvlist creation operations. 614 */ 615 void 616 fm_nvlist_destroy(nvlist_t *nvl, int flag) 617 { 618 nv_alloc_t *nva = nvlist_lookup_nv_alloc(nvl); 619 620 nvlist_free(nvl); 621 622 if (nva != NULL) { 623 if (flag == FM_NVA_FREE) 624 fm_nva_xdestroy(nva); 625 } 626 } 627 628 int 629 i_fm_payload_set(nvlist_t *payload, const char *name, va_list ap) 630 { 631 int nelem, ret = 0; 632 data_type_t type; 633 634 while (ret == 0 && name != NULL) { 635 type = va_arg(ap, data_type_t); 636 switch (type) { 637 case DATA_TYPE_BYTE: 638 ret = nvlist_add_byte(payload, name, 639 va_arg(ap, uint_t)); 640 break; 641 case DATA_TYPE_BYTE_ARRAY: 642 nelem = va_arg(ap, int); 643 ret = nvlist_add_byte_array(payload, name, 644 va_arg(ap, uchar_t *), nelem); 645 break; 646 case DATA_TYPE_BOOLEAN_VALUE: 647 ret = nvlist_add_boolean_value(payload, name, 648 va_arg(ap, boolean_t)); 649 break; 650 case DATA_TYPE_BOOLEAN_ARRAY: 651 nelem = va_arg(ap, int); 652 ret = nvlist_add_boolean_array(payload, name, 653 va_arg(ap, boolean_t *), nelem); 654 break; 655 case DATA_TYPE_INT8: 656 ret = nvlist_add_int8(payload, name, 657 va_arg(ap, int)); 658 break; 659 case DATA_TYPE_INT8_ARRAY: 660 nelem = va_arg(ap, int); 661 ret = nvlist_add_int8_array(payload, name, 662 va_arg(ap, int8_t *), nelem); 663 break; 664 case DATA_TYPE_UINT8: 665 ret = nvlist_add_uint8(payload, name, 666 va_arg(ap, uint_t)); 667 break; 668 case DATA_TYPE_UINT8_ARRAY: 669 nelem = va_arg(ap, int); 670 ret = nvlist_add_uint8_array(payload, name, 671 va_arg(ap, uint8_t *), nelem); 672 break; 673 case DATA_TYPE_INT16: 674 ret = nvlist_add_int16(payload, name, 675 va_arg(ap, int)); 676 break; 677 case DATA_TYPE_INT16_ARRAY: 678 nelem = va_arg(ap, int); 679 ret = nvlist_add_int16_array(payload, name, 680 va_arg(ap, int16_t *), nelem); 681 break; 682 case DATA_TYPE_UINT16: 683 ret = nvlist_add_uint16(payload, name, 684 va_arg(ap, uint_t)); 685 break; 686 case DATA_TYPE_UINT16_ARRAY: 687 nelem = va_arg(ap, int); 688 ret = nvlist_add_uint16_array(payload, name, 689 va_arg(ap, uint16_t *), nelem); 690 break; 691 case DATA_TYPE_INT32: 692 ret = nvlist_add_int32(payload, name, 693 va_arg(ap, int32_t)); 694 break; 695 case DATA_TYPE_INT32_ARRAY: 696 nelem = va_arg(ap, int); 697 ret = nvlist_add_int32_array(payload, name, 698 va_arg(ap, int32_t *), nelem); 699 break; 700 case DATA_TYPE_UINT32: 701 ret = nvlist_add_uint32(payload, name, 702 va_arg(ap, uint32_t)); 703 break; 704 case DATA_TYPE_UINT32_ARRAY: 705 nelem = va_arg(ap, int); 706 ret = nvlist_add_uint32_array(payload, name, 707 va_arg(ap, uint32_t *), nelem); 708 break; 709 case DATA_TYPE_INT64: 710 ret = nvlist_add_int64(payload, name, 711 va_arg(ap, int64_t)); 712 break; 713 case DATA_TYPE_INT64_ARRAY: 714 nelem = va_arg(ap, int); 715 ret = nvlist_add_int64_array(payload, name, 716 va_arg(ap, int64_t *), nelem); 717 break; 718 case DATA_TYPE_UINT64: 719 ret = nvlist_add_uint64(payload, name, 720 va_arg(ap, uint64_t)); 721 break; 722 case DATA_TYPE_UINT64_ARRAY: 723 nelem = va_arg(ap, int); 724 ret = nvlist_add_uint64_array(payload, name, 725 va_arg(ap, uint64_t *), nelem); 726 break; 727 case DATA_TYPE_STRING: 728 ret = nvlist_add_string(payload, name, 729 va_arg(ap, char *)); 730 break; 731 case DATA_TYPE_STRING_ARRAY: 732 nelem = va_arg(ap, int); 733 ret = nvlist_add_string_array(payload, name, 734 va_arg(ap, char **), nelem); 735 break; 736 case DATA_TYPE_NVLIST: 737 ret = nvlist_add_nvlist(payload, name, 738 va_arg(ap, nvlist_t *)); 739 break; 740 case DATA_TYPE_NVLIST_ARRAY: 741 nelem = va_arg(ap, int); 742 ret = nvlist_add_nvlist_array(payload, name, 743 va_arg(ap, nvlist_t **), nelem); 744 break; 745 default: 746 ret = EINVAL; 747 } 748 749 name = va_arg(ap, char *); 750 } 751 return (ret); 752 } 753 754 void 755 fm_payload_set(nvlist_t *payload, ...) 756 { 757 int ret; 758 const char *name; 759 va_list ap; 760 761 va_start(ap, payload); 762 name = va_arg(ap, char *); 763 ret = i_fm_payload_set(payload, name, ap); 764 va_end(ap); 765 766 if (ret) 767 atomic_add_64( 768 &erpt_kstat_data.payload_set_failed.value.ui64, 1); 769 } 770 771 /* 772 * Set-up and validate the members of an ereport event according to: 773 * 774 * Member name Type Value 775 * ==================================================== 776 * class string ereport 777 * version uint8_t 0 778 * ena uint64_t <ena> 779 * detector nvlist_t <detector> 780 * ereport-payload nvlist_t <var args> 781 * 782 */ 783 void 784 fm_ereport_set(nvlist_t *ereport, int version, const char *erpt_class, 785 uint64_t ena, const nvlist_t *detector, ...) 786 { 787 char ereport_class[FM_MAX_CLASS]; 788 const char *name; 789 va_list ap; 790 int ret; 791 792 if (version != FM_EREPORT_VERS0) { 793 atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1); 794 return; 795 } 796 797 (void) snprintf(ereport_class, FM_MAX_CLASS, "%s.%s", 798 FM_EREPORT_CLASS, erpt_class); 799 if (nvlist_add_string(ereport, FM_CLASS, ereport_class) != 0) { 800 atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1); 801 return; 802 } 803 804 if (nvlist_add_uint64(ereport, FM_EREPORT_ENA, ena)) { 805 atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1); 806 } 807 808 if (nvlist_add_nvlist(ereport, FM_EREPORT_DETECTOR, 809 (nvlist_t *)detector) != 0) { 810 atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1); 811 } 812 813 va_start(ap, detector); 814 name = va_arg(ap, const char *); 815 ret = i_fm_payload_set(ereport, name, ap); 816 va_end(ap); 817 818 if (ret) 819 atomic_add_64(&erpt_kstat_data.erpt_set_failed.value.ui64, 1); 820 } 821 822 /* 823 * Set-up and validate the members of an hc fmri according to; 824 * 825 * Member name Type Value 826 * =================================================== 827 * version uint8_t 0 828 * auth nvlist_t <auth> 829 * hc-name string <name> 830 * hc-id string <id> 831 * 832 * Note that auth and hc-id are optional members. 833 */ 834 835 #define HC_MAXPAIRS 20 836 #define HC_MAXNAMELEN 50 837 838 static int 839 fm_fmri_hc_set_common(nvlist_t *fmri, int version, const nvlist_t *auth) 840 { 841 if (version != FM_HC_SCHEME_VERSION) { 842 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 843 return (0); 844 } 845 846 if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0 || 847 nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0) { 848 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 849 return (0); 850 } 851 852 if (auth != NULL && nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, 853 (nvlist_t *)auth) != 0) { 854 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 855 return (0); 856 } 857 858 return (1); 859 } 860 861 void 862 fm_fmri_hc_set(nvlist_t *fmri, int version, const nvlist_t *auth, 863 nvlist_t *snvl, int npairs, ...) 864 { 865 nv_alloc_t *nva = nvlist_lookup_nv_alloc(fmri); 866 nvlist_t *pairs[HC_MAXPAIRS]; 867 va_list ap; 868 int i; 869 870 if (!fm_fmri_hc_set_common(fmri, version, auth)) 871 return; 872 873 npairs = MIN(npairs, HC_MAXPAIRS); 874 875 va_start(ap, npairs); 876 for (i = 0; i < npairs; i++) { 877 const char *name = va_arg(ap, const char *); 878 uint32_t id = va_arg(ap, uint32_t); 879 char idstr[11]; 880 881 (void) snprintf(idstr, sizeof (idstr), "%u", id); 882 883 pairs[i] = fm_nvlist_create(nva); 884 if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, name) != 0 || 885 nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr) != 0) { 886 atomic_add_64( 887 &erpt_kstat_data.fmri_set_failed.value.ui64, 1); 888 } 889 } 890 va_end(ap); 891 892 if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, pairs, npairs) != 0) 893 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 894 895 for (i = 0; i < npairs; i++) 896 fm_nvlist_destroy(pairs[i], FM_NVA_RETAIN); 897 898 if (snvl != NULL) { 899 if (nvlist_add_nvlist(fmri, FM_FMRI_HC_SPECIFIC, snvl) != 0) { 900 atomic_add_64( 901 &erpt_kstat_data.fmri_set_failed.value.ui64, 1); 902 } 903 } 904 } 905 906 /* 907 * Set-up and validate the members of an dev fmri according to: 908 * 909 * Member name Type Value 910 * ==================================================== 911 * version uint8_t 0 912 * auth nvlist_t <auth> 913 * devpath string <devpath> 914 * devid string <devid> 915 * 916 * Note that auth and devid are optional members. 917 */ 918 void 919 fm_fmri_dev_set(nvlist_t *fmri_dev, int version, const nvlist_t *auth, 920 const char *devpath, const char *devid) 921 { 922 if (version != DEV_SCHEME_VERSION0) { 923 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 924 return; 925 } 926 927 if (nvlist_add_uint8(fmri_dev, FM_VERSION, version) != 0) { 928 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 929 return; 930 } 931 932 if (nvlist_add_string(fmri_dev, FM_FMRI_SCHEME, 933 FM_FMRI_SCHEME_DEV) != 0) { 934 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 935 return; 936 } 937 938 if (auth != NULL) { 939 if (nvlist_add_nvlist(fmri_dev, FM_FMRI_AUTHORITY, 940 (nvlist_t *)auth) != 0) { 941 atomic_add_64( 942 &erpt_kstat_data.fmri_set_failed.value.ui64, 1); 943 } 944 } 945 946 if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_PATH, devpath) != 0) { 947 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 948 } 949 950 if (devid != NULL) 951 if (nvlist_add_string(fmri_dev, FM_FMRI_DEV_ID, devid) != 0) 952 atomic_add_64( 953 &erpt_kstat_data.fmri_set_failed.value.ui64, 1); 954 } 955 956 /* 957 * Set-up and validate the members of an cpu fmri according to: 958 * 959 * Member name Type Value 960 * ==================================================== 961 * version uint8_t 0 962 * auth nvlist_t <auth> 963 * cpuid uint32_t <cpu_id> 964 * cpumask uint8_t <cpu_mask> 965 * serial uint64_t <serial_id> 966 * 967 * Note that auth, cpumask, serial are optional members. 968 * 969 */ 970 void 971 fm_fmri_cpu_set(nvlist_t *fmri_cpu, int version, const nvlist_t *auth, 972 uint32_t cpu_id, uint8_t *cpu_maskp, const char *serial_idp) 973 { 974 uint64_t *failedp = &erpt_kstat_data.fmri_set_failed.value.ui64; 975 976 if (version < CPU_SCHEME_VERSION1) { 977 atomic_add_64(failedp, 1); 978 return; 979 } 980 981 if (nvlist_add_uint8(fmri_cpu, FM_VERSION, version) != 0) { 982 atomic_add_64(failedp, 1); 983 return; 984 } 985 986 if (nvlist_add_string(fmri_cpu, FM_FMRI_SCHEME, 987 FM_FMRI_SCHEME_CPU) != 0) { 988 atomic_add_64(failedp, 1); 989 return; 990 } 991 992 if (auth != NULL && nvlist_add_nvlist(fmri_cpu, FM_FMRI_AUTHORITY, 993 (nvlist_t *)auth) != 0) 994 atomic_add_64(failedp, 1); 995 996 if (nvlist_add_uint32(fmri_cpu, FM_FMRI_CPU_ID, cpu_id) != 0) 997 atomic_add_64(failedp, 1); 998 999 if (cpu_maskp != NULL && nvlist_add_uint8(fmri_cpu, FM_FMRI_CPU_MASK, 1000 *cpu_maskp) != 0) 1001 atomic_add_64(failedp, 1); 1002 1003 if (serial_idp == NULL || nvlist_add_string(fmri_cpu, 1004 FM_FMRI_CPU_SERIAL_ID, (char *)serial_idp) != 0) 1005 atomic_add_64(failedp, 1); 1006 } 1007 1008 /* 1009 * Set-up and validate the members of a mem according to: 1010 * 1011 * Member name Type Value 1012 * ==================================================== 1013 * version uint8_t 0 1014 * auth nvlist_t <auth> [optional] 1015 * unum string <unum> 1016 * serial string <serial> [optional*] 1017 * offset uint64_t <offset> [optional] 1018 * 1019 * * serial is required if offset is present 1020 */ 1021 void 1022 fm_fmri_mem_set(nvlist_t *fmri, int version, const nvlist_t *auth, 1023 const char *unum, const char *serial, uint64_t offset) 1024 { 1025 if (version != MEM_SCHEME_VERSION0) { 1026 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1027 return; 1028 } 1029 1030 if (!serial && (offset != (uint64_t)-1)) { 1031 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1032 return; 1033 } 1034 1035 if (nvlist_add_uint8(fmri, FM_VERSION, version) != 0) { 1036 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1037 return; 1038 } 1039 1040 if (nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0) { 1041 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1042 return; 1043 } 1044 1045 if (auth != NULL) { 1046 if (nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, 1047 (nvlist_t *)auth) != 0) { 1048 atomic_add_64( 1049 &erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1050 } 1051 } 1052 1053 if (nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum) != 0) { 1054 atomic_add_64(&erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1055 } 1056 1057 if (serial != NULL) { 1058 if (nvlist_add_string_array(fmri, FM_FMRI_MEM_SERIAL_ID, 1059 (char **)&serial, 1) != 0) { 1060 atomic_add_64( 1061 &erpt_kstat_data.fmri_set_failed.value.ui64, 1); 1062 } 1063 if (offset != (uint64_t)-1) { 1064 if (nvlist_add_uint64(fmri, FM_FMRI_MEM_OFFSET, 1065 offset) != 0) { 1066 atomic_add_64(&erpt_kstat_data. 1067 fmri_set_failed.value.ui64, 1); 1068 } 1069 } 1070 } 1071 } 1072 1073 uint64_t 1074 fm_ena_increment(uint64_t ena) 1075 { 1076 uint64_t new_ena; 1077 1078 switch (ENA_FORMAT(ena)) { 1079 case FM_ENA_FMT1: 1080 new_ena = ena + (1 << ENA_FMT1_GEN_SHFT); 1081 break; 1082 case FM_ENA_FMT2: 1083 new_ena = ena + (1 << ENA_FMT2_GEN_SHFT); 1084 break; 1085 default: 1086 new_ena = 0; 1087 } 1088 1089 return (new_ena); 1090 } 1091 1092 uint64_t 1093 fm_ena_generate_cpu(uint64_t timestamp, processorid_t cpuid, uchar_t format) 1094 { 1095 uint64_t ena = 0; 1096 1097 switch (format) { 1098 case FM_ENA_FMT1: 1099 if (timestamp) { 1100 ena = (uint64_t)((format & ENA_FORMAT_MASK) | 1101 ((cpuid << ENA_FMT1_CPUID_SHFT) & 1102 ENA_FMT1_CPUID_MASK) | 1103 ((timestamp << ENA_FMT1_TIME_SHFT) & 1104 ENA_FMT1_TIME_MASK)); 1105 } else { 1106 ena = (uint64_t)((format & ENA_FORMAT_MASK) | 1107 ((cpuid << ENA_FMT1_CPUID_SHFT) & 1108 ENA_FMT1_CPUID_MASK) | 1109 ((gethrtime_waitfree() << ENA_FMT1_TIME_SHFT) & 1110 ENA_FMT1_TIME_MASK)); 1111 } 1112 break; 1113 case FM_ENA_FMT2: 1114 ena = (uint64_t)((format & ENA_FORMAT_MASK) | 1115 ((timestamp << ENA_FMT2_TIME_SHFT) & ENA_FMT2_TIME_MASK)); 1116 break; 1117 default: 1118 break; 1119 } 1120 1121 return (ena); 1122 } 1123 1124 uint64_t 1125 fm_ena_generate(uint64_t timestamp, uchar_t format) 1126 { 1127 return (fm_ena_generate_cpu(timestamp, CPU->cpu_id, format)); 1128 } 1129 1130 uint64_t 1131 fm_ena_generation_get(uint64_t ena) 1132 { 1133 uint64_t gen; 1134 1135 switch (ENA_FORMAT(ena)) { 1136 case FM_ENA_FMT1: 1137 gen = (ena & ENA_FMT1_GEN_MASK) >> ENA_FMT1_GEN_SHFT; 1138 break; 1139 case FM_ENA_FMT2: 1140 gen = (ena & ENA_FMT2_GEN_MASK) >> ENA_FMT2_GEN_SHFT; 1141 break; 1142 default: 1143 gen = 0; 1144 break; 1145 } 1146 1147 return (gen); 1148 } 1149 1150 uchar_t 1151 fm_ena_format_get(uint64_t ena) 1152 { 1153 1154 return (ENA_FORMAT(ena)); 1155 } 1156 1157 uint64_t 1158 fm_ena_id_get(uint64_t ena) 1159 { 1160 uint64_t id; 1161 1162 switch (ENA_FORMAT(ena)) { 1163 case FM_ENA_FMT1: 1164 id = (ena & ENA_FMT1_ID_MASK) >> ENA_FMT1_ID_SHFT; 1165 break; 1166 case FM_ENA_FMT2: 1167 id = (ena & ENA_FMT2_ID_MASK) >> ENA_FMT2_ID_SHFT; 1168 break; 1169 default: 1170 id = 0; 1171 } 1172 1173 return (id); 1174 } 1175 1176 uint64_t 1177 fm_ena_time_get(uint64_t ena) 1178 { 1179 uint64_t time; 1180 1181 switch (ENA_FORMAT(ena)) { 1182 case FM_ENA_FMT1: 1183 time = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT; 1184 break; 1185 case FM_ENA_FMT2: 1186 time = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT; 1187 break; 1188 default: 1189 time = 0; 1190 } 1191 1192 return (time); 1193 } 1194 1195 /* 1196 * Convert a getpcstack() trace to symbolic name+offset, and add the resulting 1197 * string array to a Fault Management ereport as FM_EREPORT_PAYLOAD_NAME_STACK. 1198 */ 1199 void 1200 fm_payload_stack_add(nvlist_t *payload, const pc_t *stack, int depth) 1201 { 1202 int i; 1203 char *sym; 1204 ulong_t off; 1205 char *stkpp[FM_STK_DEPTH]; 1206 char buf[FM_STK_DEPTH * FM_SYM_SZ]; 1207 char *stkp = buf; 1208 1209 for (i = 0; i < depth && i != FM_STK_DEPTH; i++, stkp += FM_SYM_SZ) { 1210 if ((sym = kobj_getsymname(stack[i], &off)) != NULL) 1211 (void) snprintf(stkp, FM_SYM_SZ, "%s+%lx", sym, off); 1212 else 1213 (void) snprintf(stkp, FM_SYM_SZ, "%lx", (long)stack[i]); 1214 stkpp[i] = stkp; 1215 } 1216 1217 fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_STACK, 1218 DATA_TYPE_STRING_ARRAY, depth, stkpp, NULL); 1219 } 1220