/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Fault Management Architecture (FMA) Resource and Protocol Support
*
* The routines contained herein provide services to support kernel subsystems
* in publishing fault management telemetry (see PSARC 2002/412 and 2003/089).
*
* Name-Value Pair Lists
*
* The embodiment of an FMA protocol element (event, fmri or authority) is a
* name-value pair list (nvlist_t). FMA-specific nvlist construtor and
* destructor functions, fm_nvlist_create() and fm_nvlist_destroy(), are used
* to create an nvpair list using custom allocators. Callers may choose to
* allocate either from the kernel memory allocator, or from a preallocated
* buffer, useful in constrained contexts like high-level interrupt routines.
*
* Protocol Event and FMRI Construction
*
* Convenience routines are provided to construct nvlist events according to
* the FMA Event Protocol and Naming Schema specification for ereports and
* FMRIs for the dev, cpu, hc, mem, legacy hc and de schemes.
*
* ENA Manipulation
*
* Routines to generate ENA formats 0, 1 and 2 are available as well as
* routines to increment formats 1 and 2. Individual fields within the
* ENA are extractable via fm_ena_time_get(), fm_ena_id_get(),
* fm_ena_format_get() and fm_ena_gen_get().
*/
#include <sys/sysevent.h>
#include <sys/sysevent_impl.h>
#include <sys/sysmacros.h>
#include <sys/ddifm_impl.h>
#include <sys/compress.h>
#include <sys/systeminfo.h>
/*
* URL and SUNW-MSG-ID value to display for fm_panic(), defined below. These
*/
void *ereport_dumpbuf;
extern void fastreboot_disable_highpil(void);
/*
* Common fault management kstats to record ereport generation
* failures
*/
struct erpt_kstat {
};
{ "erpt-dropped", KSTAT_DATA_UINT64 },
{ "erpt-set-failed", KSTAT_DATA_UINT64 },
{ "fmri-set-failed", KSTAT_DATA_UINT64 },
{ "payload-set-failed", KSTAT_DATA_UINT64 }
};
/*ARGSUSED*/
static void
{
if (!panicstr)
else
}
void
fm_init(void)
{
(void) sysevent_evc_bind(FM_ERROR_CHAN,
(void) sysevent_evc_control(ereport_chan,
if (ereport_qlen == 0)
if (ereport_size == 0)
if (ereport_errorq == NULL)
panic("failed to create required ereport error queue");
/* Initialize ereport allocation and generation kstats */
sizeof (struct erpt_kstat) / sizeof (kstat_named_t),
} else {
}
}
/*
* Formatting utility function for fm_nvprintr. We attempt to wrap chunks of
* output so they aren't split across console lines, and return the end column.
*/
/*PRINTFLIKE4*/
static int
{
int width;
char c1;
console_printf("\n\r");
c = 0;
console_printf(" ");
c++;
}
}
}
/*
* Recursively print a nvlist in the specified column width and return the
* column we end up in. This function is called recursively by fm_nvprint(),
* below. We generically format the entire nvpair using hexadecimal
* integers and strings, and elide any integer arrays. Arrays are basically
* used for cache dumps right now, so we suppress them so as not to overwhelm
* the amount of console output we produce at panic time. This can be further
* enhanced as FMA technology grows based upon the needs of consumers. All
* FMA telemetry is logged using the dump device transport, so the console
* output serves only as a fallback in case this procedure is unsuccessful.
*/
static int
{
boolean_t b;
char *str;
continue; /* already printed by caller */
switch (type) {
case DATA_TYPE_BOOLEAN:
break;
case DATA_TYPE_BOOLEAN_VALUE:
(void) nvpair_value_boolean_value(nvp, &b);
break;
case DATA_TYPE_BYTE:
break;
case DATA_TYPE_INT8:
break;
case DATA_TYPE_UINT8:
break;
case DATA_TYPE_INT16:
break;
case DATA_TYPE_UINT16:
break;
case DATA_TYPE_INT32:
break;
case DATA_TYPE_UINT32:
break;
case DATA_TYPE_INT64:
(u_longlong_t)i64);
break;
case DATA_TYPE_UINT64:
(u_longlong_t)i64);
break;
case DATA_TYPE_HRTIME:
(u_longlong_t)i64);
break;
case DATA_TYPE_STRING:
break;
case DATA_TYPE_NVLIST:
break;
case DATA_TYPE_NVLIST_ARRAY: {
for (i = 0; i < nelem; i++) {
}
}
break;
case DATA_TYPE_BOOLEAN_ARRAY:
case DATA_TYPE_BYTE_ARRAY:
case DATA_TYPE_INT8_ARRAY:
case DATA_TYPE_UINT8_ARRAY:
case DATA_TYPE_INT16_ARRAY:
case DATA_TYPE_UINT16_ARRAY:
case DATA_TYPE_INT32_ARRAY:
case DATA_TYPE_UINT32_ARRAY:
case DATA_TYPE_INT64_ARRAY:
case DATA_TYPE_UINT64_ARRAY:
case DATA_TYPE_STRING_ARRAY:
break;
case DATA_TYPE_UNKNOWN:
break;
}
}
return (c);
}
void
{
char *class;
int c = 0;
console_printf("\r");
console_printf("\n");
console_printf("\n");
}
/*
* Wrapper for panic() that first produces an FMA-style message for admins.
* Normally such messages are generated by fmd(1M)'s syslog-msgs agent: this
* is the one exception to that rule and the only error that gets messaged.
* This function is intended for use by subsystems that have detected a fatal
* error and enqueued appropriate ereports and wish to then force a panic.
*/
/*PRINTFLIKE1*/
void
{
#endif /* __i386 || __amd64 */
}
/*
* Simply tell the caller if fm_panicstr is set, ie. an fma event has
* caused the panic. If so, something other than the default panic
* diagnosis method will diagnose the cause of the panic.
*/
int
{
if (fm_panicstr)
return (1);
else
return (0);
}
/*
* Print any appropriate FMA banner message before the panic message. This
* function is called by panicsys() and prints the message for fm_panic().
* We print the message here so that it comes after the system is quiesced.
* A one-line summary is recorded in the log only (cmn_err(9F) with "!" prefix).
* The rest of the message is for the console only and not needed in the log,
* so it is printed using console_printf(). We break it up into multiple
* chunks so as to avoid overflowing any small legacy prom_printf() buffers.
*/
void
fm_banner(void)
{
if (!fm_panicstr)
return; /* panic was not initiated by fm_panic(); do nothing */
if (panicstr) {
now = panic_hrtime;
} else {
gethrestime(&tod);
now = gethrtime_waitfree();
}
"TYPE: Error, VER: 1, SEVERITY: Major\n", fm_msgid);
"\n\rSUNW-MSG-ID: %s, TYPE: Error, VER: 1, SEVERITY: Major\n"
"EVENT-TIME: 0x%lx.0x%lx (0x%llx)\n",
"PLATFORM: %s, CSN: -, HOSTNAME: %s\n"
"SOURCE: %s, REV: %s %s\n",
"DESC: Errors have been detected that require a reboot to ensure system\n"
"integrity. See %s/%s for more information.\n",
"AUTO-RESPONSE: Solaris will attempt to save and diagnose the error telemetry\n"
"IMPACT: The system will sync files, save a crash dump if needed, and reboot\n"
"REC-ACTION: Save the error summary below in case telemetry cannot be saved\n");
console_printf("\n");
}
/*
* Utility function to write all of the pending ereports to the dump device.
* This function is called at either normal reboot or panic time, and simply
* iterates over the in-transit messages in the ereport sysevent channel.
*/
void
fm_ereport_dump(void)
{
char *buf;
if (panicstr) {
now = panic_hrtime;
} else {
if (ereport_errorq != NULL)
gethrestime(&tod);
now = gethrtime_waitfree();
}
/*
* In the panic case, sysevent_evc_walk_init() will return NULL.
*/
!panicstr)
return; /* event channel isn't initialized yet */
break;
}
}
/*
* Post an error report (ereport) to the sysevent error channel. The error
* channel must be established with a prior call to sysevent_evc_create()
* before publication may occur.
*/
void
{
return;
}
EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
return;
}
(void) sysevent_evc_unbind(error_chan);
return;
}
(void) sysevent_evc_unbind(error_chan);
}
/*
* Wrapppers for FM nvlist allocators
*/
/* ARGSUSED */
static void *
{
}
/* ARGSUSED */
static void
{
}
NULL,
NULL,
};
/*
* Create and initialize a new nv_alloc_t for a fixed buffer, buf. A pointer
* to the newly allocated nv_alloc_t structure is returned upon success or NULL
* is returned to indicate that the nv_alloc structure could not be created.
*/
{
return (NULL);
}
return (nvhdl);
}
/*
* Destroy a previously allocated nv_alloc structure. The fixed buffer
* associated with nva must be freed by the caller.
*/
void
{
}
/*
* Create a new nv list. A pointer to a new nv list structure is returned
* upon success or NULL is returned to indicate that the structure could
* not be created. The newly created nv list is created and managed by the
* operations installed in nva. If nva is NULL, the default FMA nva
* operations are installed and used.
*
* When called from the kernel and nva == NULL, this function must be called
* from passive kernel context with no locks held that can prevent a
* sleeping memory allocation from occurring. Otherwise, this function may
* be called from other kernel contexts as long a valid nva created via
* fm_nva_create() is supplied.
*/
nvlist_t *
{
int hdl_alloced = 0;
return (NULL);
}
hdl_alloced = 1;
} else {
}
if (hdl_alloced) {
}
return (NULL);
}
return (nvl);
}
/*
* Destroy a previously allocated nvlist structure. flag indicates whether
* or not the associated nva structure should be freed (FM_NVA_FREE) or
* retained (FM_NVA_RETAIN). Retaining the nv alloc structure allows
* it to be re-used for future nvlist creation operations.
*/
void
{
if (flag == FM_NVA_FREE)
}
}
int
{
switch (type) {
case DATA_TYPE_BYTE:
break;
case DATA_TYPE_BYTE_ARRAY:
break;
case DATA_TYPE_BOOLEAN_VALUE:
break;
case DATA_TYPE_BOOLEAN_ARRAY:
break;
case DATA_TYPE_INT8:
break;
case DATA_TYPE_INT8_ARRAY:
break;
case DATA_TYPE_UINT8:
break;
case DATA_TYPE_UINT8_ARRAY:
break;
case DATA_TYPE_INT16:
break;
case DATA_TYPE_INT16_ARRAY:
break;
case DATA_TYPE_UINT16:
break;
case DATA_TYPE_UINT16_ARRAY:
break;
case DATA_TYPE_INT32:
break;
case DATA_TYPE_INT32_ARRAY:
break;
case DATA_TYPE_UINT32:
break;
case DATA_TYPE_UINT32_ARRAY:
break;
case DATA_TYPE_INT64:
break;
case DATA_TYPE_INT64_ARRAY:
break;
case DATA_TYPE_UINT64:
break;
case DATA_TYPE_UINT64_ARRAY:
break;
case DATA_TYPE_STRING:
break;
case DATA_TYPE_STRING_ARRAY:
break;
case DATA_TYPE_NVLIST:
break;
case DATA_TYPE_NVLIST_ARRAY:
break;
default:
}
}
return (ret);
}
void
{
int ret;
const char *name;
if (ret)
}
/*
* Set-up and validate the members of an ereport event according to:
*
* Member name Type Value
* ====================================================
* class string ereport
* version uint8_t 0
* ena uint64_t <ena>
* detector nvlist_t <detector>
* ereport-payload nvlist_t <var args>
*
* We don't actually add a 'version' member to the payload. Really,
* the version quoted to us by our caller is that of the category 1
* "ereport" event class (and we require FM_EREPORT_VERS0) but
* the payload version of the actual leaf class event under construction
* may be something else. Callers should supply a version in the varargs,
* or (better) we could take two version arguments - one for the
* ereport category 1 classification (expect FM_EREPORT_VERS0) and one
* for the leaf class.
*/
void
{
const char *name;
int ret;
if (version != FM_EREPORT_VERS0) {
return;
}
return;
}
}
}
if (ret)
}
/*
* Set-up and validate the members of an hc fmri according to;
*
* Member name Type Value
* ===================================================
* version uint8_t 0
* auth nvlist_t <auth>
* hc-name string <name>
* hc-id string <id>
*
* Note that auth and hc-id are optional members.
*/
static int
{
if (version != FM_HC_SCHEME_VERSION) {
return (0);
}
return (0);
}
return (0);
}
return (1);
}
void
{
int i;
return;
for (i = 0; i < npairs; i++) {
}
}
for (i = 0; i < npairs; i++)
}
}
}
/*
* Set-up and validate the members of an dev fmri according to:
*
* Member name Type Value
* ====================================================
* version uint8_t 0
* auth nvlist_t <auth>
* devpath string <devpath>
* [devid] string <devid>
* [target-port-l0id] string <target-port-lun0-id>
*
* Note that auth and devid are optional members.
*/
void
{
int err = 0;
if (version != DEV_SCHEME_VERSION0) {
return;
}
}
if (err)
}
/*
* Set-up and validate the members of an cpu fmri according to:
*
* Member name Type Value
* ====================================================
* version uint8_t 0
* auth nvlist_t <auth>
* cpuid uint32_t <cpu_id>
* cpumask uint8_t <cpu_mask>
* serial uint64_t <serial_id>
*
* Note that auth, cpumask, serial are optional members.
*
*/
void
{
if (version < CPU_SCHEME_VERSION1) {
return;
}
return;
}
FM_FMRI_SCHEME_CPU) != 0) {
return;
}
*cpu_maskp) != 0)
FM_FMRI_CPU_SERIAL_ID, (char *)serial_idp) != 0)
}
/*
* Set-up and validate the members of a mem according to:
*
* Member name Type Value
* ====================================================
* version uint8_t 0
* auth nvlist_t <auth> [optional]
* unum string <unum>
* serial string <serial> [optional*]
* offset uint64_t <offset> [optional]
*
* * serial is required if offset is present
*/
void
{
if (version != MEM_SCHEME_VERSION0) {
return;
}
return;
}
return;
}
return;
}
}
}
}
(char **)&serial, 1) != 0) {
}
FM_FMRI_MEM_OFFSET, offset) != 0) {
}
}
}
void
{
if (version != ZFS_SCHEME_VERSION0) {
return;
}
return;
}
return;
}
}
if (vdev_guid != 0) {
}
}
}
{
switch (ENA_FORMAT(ena)) {
case FM_ENA_FMT1:
break;
case FM_ENA_FMT2:
break;
default:
new_ena = 0;
}
return (new_ena);
}
{
switch (format) {
case FM_ENA_FMT1:
if (timestamp) {
((cpuid << ENA_FMT1_CPUID_SHFT) &
((timestamp << ENA_FMT1_TIME_SHFT) &
} else {
((cpuid << ENA_FMT1_CPUID_SHFT) &
((gethrtime_waitfree() << ENA_FMT1_TIME_SHFT) &
}
break;
case FM_ENA_FMT2:
break;
default:
break;
}
return (ena);
}
{
}
{
switch (ENA_FORMAT(ena)) {
case FM_ENA_FMT1:
break;
case FM_ENA_FMT2:
break;
default:
gen = 0;
break;
}
return (gen);
}
{
return (ENA_FORMAT(ena));
}
{
switch (ENA_FORMAT(ena)) {
case FM_ENA_FMT1:
break;
case FM_ENA_FMT2:
break;
default:
id = 0;
}
return (id);
}
{
switch (ENA_FORMAT(ena)) {
case FM_ENA_FMT1:
break;
case FM_ENA_FMT2:
break;
default:
time = 0;
}
return (time);
}
/*
* Convert a getpcstack() trace to symbolic name+offset, and add the resulting
* string array to a Fault Management ereport as FM_EREPORT_PAYLOAD_NAME_STACK.
*/
void
{
int i;
char *sym;
else
}
}
void
{
uprintf("Killed process %d (%s) in contract id %d "
}
void
{
uint_t n;
int i, j;
return;
/*
* copy the bboard nvpairs to the pairs array
*/
!= 0) {
return;
}
for (i = 0; i < n; i++) {
&hcname) != 0) {
return;
}
return;
}
for (j = 0; j <= i; j++) {
}
return;
}
}
/*
* create the pairs from passed in pairs
*/
for (i = n; i < npairs + n; i++) {
for (j = 0; j <= i; j++) {
}
return;
}
}
/*
* Create the fmri hc list
*/
npairs + n) != 0) {
return;
}
for (i = 0; i < npairs + n; i++) {
}
return;
}
}
}