gcpu_mca.c revision 0ad0f0b2adb964c7bd56bbf5a831721e1a67beaf
/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/cpu_module_impl.h>
#include <sys/cpu_module_ms.h>
#include <sys/x86_archext.h>
#include <sys/sysmacros.h>
#include <sys/privregs.h>
#include <sys/sysevent.h>
#include "gcpu.h"
/*
* Clear to log telemetry found at initialization. While processor docs
* say you should process this telemetry on all but Intel family 0x6
* there are way too many exceptions and we want to avoid bogus
* diagnoses.
*/
int gcpu_suppress_log_on_init = 1;
/*
* gcpu_mca_stack_flag is a debug assist option to capture a stack trace at
* error logout time. The stack will be included in the ereport if the
* error type selects stack inclusion, or in all cases if
* gcpu_mca_stack_ereport_include is nonzero.
*/
int gcpu_mca_stack_flag = 0;
int gcpu_mca_stack_ereport_include = 0;
/*
* The number of times to re-read MCA telemetry to try to obtain a
* consistent snapshot if we find it to be changing under our feet.
*/
int gcpu_mca_telemetry_retries = 5;
#ifndef __xpv
int gcpu_mca_cmci_throttling_threshold = 10;
int gcpu_mca_cmci_reenable_threshold = 1000;
#endif
static gcpu_error_disp_t gcpu_errtypes[] = {
/*
* Unclassified
*/
{
NULL,
},
/*
* Microcode ROM Parity Error
*/
{
NULL,
},
/*
* External - BINIT# from another processor during power-on config
*/
{
NULL,
},
/*
*/
{
NULL,
},
/*
* Internal parity error
*/
{
NULL,
},
/*
* Internal timer error
*/
{
NULL,
},
/*
* Internal unclassified
*/
{
NULL,
},
/*
* Compound error codes - generic memory hierarchy
*/
{
NULL,
FM_EREPORT_PAYLOAD_FLAGS_COMMON, /* yes, no compound name */
},
/*
* Compound error codes - TLB errors
*/
{
"%1$s" "TLB" "%2$s" "_ERR",
},
/*
* Compound error codes - memory hierarchy
*/
{
"%1$s" "CACHE" "%2$s" "_" "%3$s" "_ERR",
},
/*
* Compound error codes - bus and interconnect errors
*/
{
"BUS" "%2$s" "_" "%4$s" "_" "%3$s" "_" "%5$s" "_" "%6$s" "_ERR",
},
/*
* Compound error codes - memory controller errors
*/
{
"MC" "_" "%8$s" "_" "%9$s" "_ERR",
},
};
static gcpu_error_disp_t gcpu_unknown = {
"UNKNOWN",
0,
0
};
static errorq_t *gcpu_mca_queue;
static kmutex_t gcpu_mca_queue_lock;
#ifdef __xpv
static int isxpv = 1;
#else
static int isxpv = 0;
#endif
static const gcpu_error_disp_t *
{
int i;
for (i = 0; i < sizeof (gcpu_errtypes) / sizeof (gcpu_error_disp_t);
i++, ged++) {
return (ged);
}
return (NULL);
}
static uint8_t
{
}
#define GCPU_MNEMONIC_UNDEF "undefined"
#define GCPU_MNEMONIC_RESVD "reserved"
/*
* Mappings of TT, LL, RRRR, PP, II and T values to compound error name
* mnemonics and to ereport class name components.
*/
struct gcpu_mnexp {
const char *mne_compound; /* used in expanding compound errname */
const char *mne_ereport; /* used in expanding ereport class */
};
{ GCPU_MNEMONIC_UNDEF, "" }
};
};
};
};
{ GCPU_MNEMONIC_RESVD, "" },
};
};
};
};
enum gcpu_mn_namespace {
};
static const char *
enum gcpu_mn_namespace nspace)
{
return (GCPU_MNEMONIC_UNDEF); /* for all namespaces */
switch (nspace) {
/*NOTREACHED*/
/*NOTREACHED*/
default:
return (GCPU_MNEMONIC_UNDEF);
/*NOTREACHED*/
}
}
/*
* The ereport class leaf component is either a simple string with no
* format specifiers, or a string with one or more embedded %n$s specifiers -
* positional selection for string arguments. The kernel snprintf does
* not support %n$ (and teaching it to do so is too big a headache) so
* we will expand this restricted format string ourselves.
*/
#define GCPU_CLASS_VARCOMPS 9
static void
enum gcpu_mn_namespace nspace)
{
const char *mn[GCPU_CLASS_VARCOMPS];
char *p = buf; /* current position in buf */
char c;
if (c != '%') {
/* not the beginning of a format specifier - copy */
*p++ = c;
continue;
}
error = 0;
which = -1;
expfmtchar = -1;
if ((c = *fmt++) == '\0')
break; /* early termination of fmt specifier */
switch (c) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
error++;
break;
}
which = c - '1';
goto nextfmt;
/*NOTREACHED*/
case '$':
error++;
break;
}
expfmtchar = 's';
goto nextfmt;
/*NOTREACHED*/
case 's':
if (expfmtchar != 's') {
error++;
break;
}
p += strlen(p);
break;
default:
error++;
break;
}
if (error)
break;
}
*p = '\0'; /* NUL termination */
}
static void
{
char *p = buf; /* current position in buf */
p += strlen(p);
if (p >= q)
return;
} else {
}
}
/*
* Create an "hc" scheme FMRI identifying the given cpu with
* motherboard/chip/core/strand instance numbers.
*/
static nvlist_t *
{
return (NULL);
"motherboard", 0,
return (nvl);
}
int gcpu_bleat_count_thresh = 5;
/*
* Called when we are unable to propogate a logout structure onto an
* errorq for subsequent ereport preparation and logging etc. The caller
* should usually only decide to call this for severe errors - those we
* suspect we may need to panic for.
*/
static void
{
static hrtime_t gcpu_last_bleat;
static int bleatcount;
int i;
/*
* Throttle spamming of the console. The first gcpu_bleat_count_thresh
* can come as fast as we like, but once we've spammed that many
* to the console we require a minimum interval to pass before
* any more complaints.
*/
if (++bleatcount > gcpu_bleat_count_thresh) {
return;
else
bleatcount = 0;
}
"Machine-Check Errors unlogged on chip %d core %d strand %d, "
if (!(status & MSR_MC_STATUS_VAL))
continue;
"STAT 0x%016llx ADDR 0x%016llx MISC 0x%016llx",
i, IA32_MSR_MC(i, STATUS),
break;
case MSR_MC_STATUS_ADDRV:
"STAT 0x%016llx ADDR 0x%016llx",
i, IA32_MSR_MC(i, STATUS),
break;
case MSR_MC_STATUS_MISCV:
"STAT 0x%016llx MISC 0x%016llx",
i, IA32_MSR_MC(i, STATUS),
break;
default:
"STAT 0x%016llx",
i, IA32_MSR_MC(i, STATUS),
break;
}
}
}
static void
{
/*
* Include the compound error name if requested and if this
* is a compound error type.
*/
char buf[FM_MAX_CLASS];
}
/*
* Include disposition information for this error
*/
if (members & FM_EREPORT_PAYLOAD_FLAG_DISP &&
int i, empty = 1;
char buf[128];
static struct _gcpu_disp_name {
const char *dn;
} disp_names[] = {
"processor_context_corrupt" },
"return_ip_invalid" },
"unconstrained" },
"forcefatal" },
"ignored" },
"corrupt_context_cleared" },
"uncorrected_data_cleared" },
"poisoned" },
"telemetry_unstable" },
};
for (i = 0; i < sizeof (disp_names) /
sizeof (struct _gcpu_disp_name); i++) {
continue;
p += strlen(p);
empty = 0;
}
if (p != buf)
}
/*
* If MCG_STATUS is included add that and an indication of whether
* this ereport was the result of a machine check or poll.
*/
}
/*
* If an instruction pointer is to be included add one provided
* MCG_STATUS indicated it is valid; meaningless for polled events.
*/
mcg & MCG_STATUS_EIPV) {
}
/*
* Add an indication of whether the trap occured during privileged code.
*/
}
/*
* If requested, add the index of the MCA bank. This indicates the
* n'th bank of 4 MCA registers, and does not necessarily correspond
* to MCi_* - use the bank offset to correlate
*/
if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_NUM) {
/* Bank number */
/* Offset of MCi_CTL */
NULL);
}
/*
* Add MCi_STATUS if requested, and decode it.
*/
if (members & FM_EREPORT_PAYLOAD_FLAG_MC_STATUS) {
const char *tbes[] = {
"No tracking", /* 00 */
"Green - below threshold", /* 01 */
"Yellow - above threshold", /* 10 */
"Reserved" /* 11 */
};
/* Bank MCi_STATUS */
/* Overflow? */
/* Uncorrected? */
/* Enabled? */
/* Processor context corrupt? */
/* Error code */
/* Model-specific error code */
NULL);
/*
* If MCG_CAP.TES_P indicates that that thresholding info
* is present in the architural component of the bank status
* then include threshold information for this bank.
*/
NULL);
}
}
/*
* MCi_ADDR info if requested and valid.
*/
if (members & FM_EREPORT_PAYLOAD_FLAG_MC_ADDR &&
bstat & MSR_MC_STATUS_ADDRV) {
}
/*
* MCi_MISC if requested and MCi_STATUS.MISCV).
*/
if (members & FM_EREPORT_PAYLOAD_FLAG_MC_MISC &&
bstat & MSR_MC_STATUS_MISCV) {
}
}
/*
* Construct and post an ereport based on the logout information from a
* single MCA bank. We are not necessarily running on the cpu that
* detected the error.
*/
static void
{
char buf[FM_MAX_CLASS];
const char *classfmt;
if (panicstr) {
return;
/*
* Allocate another element for scratch space, but fallback
* to the one we have if that fails. We'd like to use the
* additional scratch space for nvlist construction.
*/
else
} else {
}
return;
/*
* Common payload data required by the protocol:
* - ereport class
* - detector
* - ENA
*/
/*
* Ereport class - call into model-specific support to allow it to
* provide a cpu class or leaf class, otherwise calculate our own.
*/
/*
* The detector FMRI.
*/
/*
* It will be better when virtualized.
*/
if (panicstr) {
} else {
}
/*
* Add the architectural ereport class-specific payload data.
*/
/*
* Allow model-specific code to add ereport members.
*/
/*
* Include stack if options is turned on and either selected in
* the payload member bitmask or inclusion is forced.
*/
if (gcpu_mca_stack_flag &&
}
/*
* If injection has taken place anytime in the past then note this
* on the ereport.
*/
if (cmi_inj_tainted() == B_TRUE) {
}
/*
* Post ereport.
*/
if (panicstr) {
if (scr_eqep)
} else {
}
}
/*ARGSUSED*/
void
{
const gcpu_bank_logout_t *gbl;
int i;
const gcpu_error_disp_t *gened;
/*
* Perform a match based on IA32 MCA architectural
* components alone.
*/
/*
* Now see if an model-specific match can be made.
*/
gcl->gcl_ms_logout);
/*
* Prepare and dispatch an ereport for logging and
* diagnosis.
*/
gbl->gbl_status);
/*
* Telemetry kept changing as we tried to read
* it. Force an unknown ereport leafclass but
* keep the telemetry unchanged for logging.
*/
gbl->gbl_status);
}
}
}
static size_t gcpu_mca_queue_datasz = 0;
/*
* The following code is ready to make a weak attempt at growing the
* errorq structure size. Since it is not foolproof (we don't know
* who may already be producing to the outgoing errorq) our caller
* instead assures that we'll always be called with no greater data
* size than on our first call.
*/
static void
{
int slots;
if (gcpu_mca_queue_datasz >= datasz) {
return;
}
if (gcpu_mca_queue) {
}
if (gcpu_mca_queue != NULL)
}
/*
* Perform MCA initialization as described in section 14.6 of Intel 64
* and IA-32 Architectures Software Developer's Manual Volume 3A.
*/
static uint_t global_nbanks;
void
{
int mcg_ctl_present;
uint32_t ctl_skip_mask = 0;
uint32_t status_skip_mask = 0;
int i;
#ifndef __xpv
int mcg_ctl2_present;
uint32_t cmci_capable = 0;
#endif
return;
/*
*/
return;
/*
* CPU startup code only calls cmi_mca_init if x86_feature indicates
* both MCA and MCE support (i.e., X86_MCA). P5, K6, and earlier
* processors, which have their own * more primitive way of doing
* machine checks, will not have cmi_mca_init called since their
* CPUID information will not indicate both MCA and MCE features.
*/
/*
* Determine whether the IA32_MCG_CTL register is present. If it
* is we will enable all features by writing -1 to it towards
* the end of this initialization; if it is absent then volume 3A
* says we must nonetheless continue to initialize the individual
* banks.
*/
#ifndef __xpv
#endif
/*
* We squirell values away for inspection/debugging.
*/
if (mcg_ctl_present)
/*
* Determine the number of error-reporting banks implemented.
*/
if (nbanks != 0 && global_nbanks == 0)
/*
* If someone is hiding the number of banks (perhaps we are fully
* virtualized?) or if this processor has more banks than the
* first to set global_nbanks then bail. The latter requirement
* is because we need to size our errorq data structure and we
* don't want to have to grow the errorq (destroy and recreate)
* which may just lose some telemetry.
*/
return;
sizeof (struct gcpu_bios_bankcfg), KM_SLEEP);
/*
* Calculate the size we need to allocate for a gcpu_logout_t
* with a gcl_data array big enough for all banks of this cpu.
* Add any space requested by the model-specific logout support.
*/
for (i = 0; i < GCPU_MCA_LOGOUT_NUM; i++) {
sizeof (gcpu_bank_logout_t);
}
#ifdef __xpv
#endif
#ifndef __xpv
KM_SLEEP);
#endif
/*
* Create our errorq to transport the logout structures. This
* can fail so users of gcpu_mca_queue must be prepared for NULL.
*/
/*
* Not knowing which, if any, banks are shared between cores we
* assure serialization of MCA bank initialization by each cpu
* on the chip. On chip architectures in which some banks are
* shared this will mean the shared resource is initialized more
* than once - we're simply aiming to avoid simultaneous MSR writes
* to the shared resource.
*
* Even with these precautions, some platforms may yield a GP fault
* if a core other than a designated master tries to write anything
* but all 0's to MCi_{STATUS,ADDR,CTL}. So we will perform
* those writes under on_trap protection.
*/
/*
* Initialize poller data, but don't start polling yet.
*/
/*
* Work out which MCA banks we will initialize. In MCA logout
* code we will only read those banks which we initialize here.
*/
for (i = 0; i < nbanks; i++) {
if (!cms_present(hdl)) {
/*
* Model-specific support is not present, try to use
* sane defaults.
*
* On AMD family 6 processors, reports about spurious
* machine checks indicate that bank 0 should be
* skipped.
*
* On Intel family 6 processors, the documentation tells
* us not to write to MC0_CTL.
*
*/
if (i == 0 && family == 6) {
switch (vendor) {
case X86_VENDOR_AMD:
skipstatus = B_TRUE;
/*FALLTHRU*/
case X86_VENDOR_Intel:
break;
}
}
}
ctl_skip_mask |= skipctl << i;
status_skip_mask |= skipstatus << i;
if (skipctl && skipstatus)
continue;
/*
* Record which MCA banks were enabled, from the point of view
* of the whole chip (if some cores share a bank we must be
* sure either can logout from it).
*/
#ifndef __xpv
/*
* check CMCI capability
*/
if (mcg_ctl2_present) {
if (ctl2 & MSR_MC_CTL2_EN)
continue;
ctl2 |= MSR_MC_CTL2_EN;
if (cap)
cmci_capable ++;
/*
* Set threshold to 1 while unset the en field, to avoid
* CMCI trigged before APIC LVT entry init.
*/
/*
* init cmci related count
*/
}
#endif
}
#ifndef __xpv
if (cmci_capable)
cmi_enable_cmci = 1;
#endif
#ifndef __xpv
/*
* Log any valid telemetry lurking in the MCA banks, but do not
* clear the status registers. Ignore the disposition returned -
* we have already paniced or reset for any nasty errors found here.
*
* Intel vol 3A says that we should not do this on family 0x6,
* and that for any extended family the BIOS clears things
* on power-on reset so you'll only potentially find valid telemetry
* on warm reset (we do it for both - on power-on reset we should
* just see zeroes).
*
* AMD docs since K7 say we should process anything we find here.
*/
if (!gcpu_suppress_log_on_init &&
vendor == X86_VENDOR_AMD))
/*
* Initialize all MCi_CTL and clear all MCi_STATUS, allowing the
* model-specific module the power of veto.
*/
for (i = 0; i < nbanks; i++) {
struct gcpu_bios_bankcfg *bcfgp =
/*
* Stash inherited bank MCA state, even for banks we will
* not initialize ourselves. Do not read the MISC register
* unconditionally - on some processors that will #GP on
* banks that do not implement the MISC register (would be
* caught by on_trap, anyway).
*/
&bcfgp->bios_bank_ctl);
&bcfgp->bios_bank_addr);
/*
* In some old BIOS the status value after boot can indicate
* MISCV when there is actually no MISC register for
* that bank. The following read could therefore
* aggravate a general protection fault. This should be
* caught by on_trap, but the #GP fault handler is busted
* and can suffer a double fault even before we get to
* trap() to check for on_trap protection. Until that
* issue is fixed we remove the one access that we know
* can cause a #GP.
*
* if (bcfgp->bios_bank_status & MSR_MC_STATUS_MISCV)
* (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, MISC),
* &bcfgp->bios_bank_misc);
*/
bcfgp->bios_bank_misc = 0;
if (!(ctl_skip_mask & (1 << i))) {
}
if (!(status_skip_mask & (1 << i))) {
}
}
#endif
/*
* Now let the model-specific support perform further initialization
* of non-architectural features.
*/
#ifndef __xpv
/* enable all machine-check features */
if (mcg_ctl_present)
#endif
#ifndef __xpv
/* enable machine-check exception in CR4 */
#endif
}
static uint64_t
{
int i;
/*
* If this a machine check then if the return instruction pointer
* is not valid the current context is lost.
*/
int poisoned;
if (!(mcistatus & MSR_MC_STATUS_VAL))
continue;
continue;
pcc = 0;
mcesp->mce_npcc_ok++;
}
uc = 0;
mcesp->mce_nuc_ok++;
}
if (uc) {
if (poisoned) {
}
}
if ((ms_scope & CMS_ERRSCOPE_IGNORE_ERR) == 0) {
/*
* We're not being instructed to ignore the error,
* so apply our standard disposition logic to it.
*/
}
curctxbad++;
}
/*
* Even if the above may not indicate that the error
* is terminal, model-specific support may insist
* that we treat it as such. Such errors wil be
* fatal even if discovered via poll.
*/
if (ms_scope & CMS_ERRSCOPE_FORCE_FATAL) {
forcefatal++;
mcesp->mce_forcefatal++;
}
} else {
mcesp->mce_ignored++;
}
}
if (unconstrained > 0)
if (curctxbad > 0)
if (forcefatal > 0)
if (gcpu_mca_queue != NULL) {
int how;
if (ismc) {
ERRORQ_ASYNC : /* no panic, so arrange drain */
ERRORQ_SYNC; /* panic flow will drain */
} else {
cmi_panic_on_ue()) ?
ERRORQ_SYNC : /* poller will panic */
ERRORQ_ASYNC; /* no panic */
}
} else if (disp != 0) {
}
return (disp);
}
/*
* Gather error telemetry from our source, and then submit it for
* processing.
*/
static uint32_t gcpu_deferrred_polled_clears;
#ifndef __xpv
static void
{
!(status & MSR_MC_STATUS_CEC_MASK)))) {
if (!(bank_cmci_p->cmci_enabled)) {
/*
* when cmci is disabled, and the bank has no error or
* no corrected error for
* gcpu_mca_cmci_reenable_threshold consecutive polls,
* turn on this bank's cmci.
*/
bank_cmci_p->drtcmci ++;
if (bank_cmci_p->drtcmci >=
/* turn on cmci */
(void) cmi_hdl_rdmsr(hdl,
ctl2 |= MSR_MC_CTL2_EN;
(void) cmi_hdl_wrmsr(hdl,
/* reset counter and set flag */
bank_cmci_p->drtcmci = 0;
}
} else {
/*
* when cmci is enabled,if is in cyclic poll and the
* bank has no error or no corrected error, reset ncmci
* counter
*/
bank_cmci_p->ncmci = 0;
}
}
}
static void
int what)
{
/*
* if cmci of this bank occurred beyond
* gcpu_mca_cmci_throttling_threshold between 2 polls,
* turn off this bank's CMCI;
*/
/* if it is cmci trap, increase the count */
bank_cmci_p->ncmci++;
/* turn off cmci */
&ctl2);
ctl2 &= ~MSR_MC_CTL2_EN;
ctl2);
/* clear the flag and count */
bank_cmci_p->cmci_enabled = 0;
bank_cmci_p->ncmci = 0;
}
}
}
#endif
static void
{
int i;
if (status == 0)
continue;
goto serialize;
/*
* For i86xpv we always clear status in order to invalidate
* the interposed telemetry.
*
* For native machine checks we always clear status here. For
* native polls we must be a little more cautious since there
* is an outside chance that we may clear telemetry from a
* shared MCA bank on which a sibling core is machine checking.
*
* For polled observations of errors that look like they may
* do not guarantee a machine check on error occurence)
* we will not clear the status at this wakeup unless
* we saw the same status at the previous poll. We will
* always process and log the current observations - it
* is only the clearing of MCi_STATUS which may be
* deferred until the next wakeup.
*/
goto serialize;
}
/*
* We have a polled observation of a machine check
* candidate. If we saw essentially the same status at the
* last poll then clear the status now since this appears
* not to be a #MC candidate after all. If we see quite
* different status now then do not clear, but reconsider at
* the next poll. In no actual machine check clears
* the status in the interim then the status should not
* keep changing forever (meaning we'd never clear it)
* since before long we'll simply have latched the highest-
* priority error and set the OVerflow bit. Nonetheless
* we count how many times we defer clearing and after
* a while insist on clearing the status.
*/
if (pgbl->gbl_clrdefcnt != 0) {
/* We deferred clear on this bank at last wakeup */
/*
* Status is unchanged so clear it now and,
* since we have already logged this info,
* avoid logging it again.
*/
gbl->gbl_status = 0;
(void) cmi_hdl_wrmsr(hdl,
} else {
/* Record deferral for next wakeup */
}
} else {
/* Record initial deferral for next wakeup */
}
{
#ifdef __xpv
;
#else
/*
* Intel Vol 3A says to execute a serializing
* instruction here, ie CPUID. Well WRMSR is also
* defined to be serializing, so the status clear above
* should suffice. To be a good citizen, and since
* some clears are deferred, we'll execute a CPUID
* instruction here.
*/
struct cpuid_regs tmp;
(void) __cpuid_insn(&tmp);
#endif
}
}
}
/*ARGSUSED5*/
void
{
int i, nerr = 0;
int first = 0;
int last = 0;
int willpanic = 0;
CMI_SUCCESS) {
return;
}
if (ismc) {
} else {
}
if (cap & MCG_CAP_TES_P)
gbl->gbl_status = 0;
gbl->gbl_clrdefcnt = 0;
/*
* Only logout from MCA banks we have initialized from at
* least one core. If a core shares an MCA bank with another
* but perhaps lost the race to initialize it, then it must
* still be allowed to logout from the shared bank.
*/
continue;
/*
* On a poll look only at the banks we've been asked to check.
*/
continue;
continue;
#ifndef __xpv
#endif
if (!(status & MSR_MC_STATUS_VAL))
continue;
if (first == 0)
first = i;
last = i;
addr = -1;
misc = 0;
if (status & MSR_MC_STATUS_ADDRV)
if (status & MSR_MC_STATUS_MISCV)
#ifndef __xpv
#endif
/*
* Allow the model-specific code to extract bank telemetry.
*/
/*
* data will not change during the above sequence of MSR reads,
* or that it can only change by the addition of the OVerflow
* bit to the status register. If the status has changed
* other than in the overflow bit then we attempt to reread
* for a consistent snapshot, but eventually give up and
* go with what we've got. We only perform this check
* for a poll - a further #MC during a #MC will reset, and
* polled errors should not overwrite higher-priority
* trapping errors (but could set the overflow bit).
*/
&status2)) == CMI_SUCCESS) {
if (retries-- > 0) {
goto retry;
} else {
}
}
}
nerr++;
}
if (gcpu_mca_stack_flag)
else
gcl->gcl_stackdepth = 0;
/*
* Decide our disposition for this error or errors, and submit for
* logging and subsequent diagnosis.
*/
if (nerr != 0) {
if (!willpanic)
} else {
disp = 0;
if (mcesp) {
}
}
/*
* Clear MCG_STATUS if MCIP is set (machine check in progress).
* If a second #MC had occured before now the system would have
* reset. We can only do thise once gcpu_mca_process has copied
* the logout structure.
*/
/*
* At this point we have read and logged all telemetry that is visible
* under the MCA. On architectures for which the NorthBridge is
* on-chip this may include NB-observed errors, but where the NB
* is off chip it may have been the source of the #MC request and
* so we must call into the memory-controller driver to give it
* a chance to log errors.
*/
if (ismc) {
}
}
#ifndef __xpv
int gcpu_mca_trap_vomit_summary = 0;
/*
* On a native machine check exception we come here from mcetrap via
* cmi_mca_trap. A machine check on one cpu of a chip does not trap others
* cpus of the chip, so it is possible that another cpu on this chip could
* initiate a poll while we're in the #mc handler; it is also possible that
* this trap has occured during a poll on this cpu. So we must acquire
* the chip-wide poll lock, but be careful to avoid deadlock.
*
* The 'data' pointer cannot be NULL due to init order.
*/
{
int tooklock = 0;
return (0);
/*
* Synchronize with any poller from another core that may happen
* to share access to one or more of the MCA banks.
*/
/*
* The lock is not owned by the thread we have
* interrupted. Spin for this adaptive lock.
*/
while (!mutex_tryenter(poll_lock)) {
;
}
tooklock = 1;
}
if (tooklock)
/*
* gcpu_mca_trap_vomit_summary may be set for debug assistance.
*/
"%u PCC (%u ok), "
"%u UC (%d ok, %u poisoned), "
"%u forcefatal, %u ignored",
}
}
#endif
/*ARGSUSED*/
void
{
/* Nothing to do here */
}
/*ARGSUSED*/
void
{
}
/*
* Write the requested values to the indicated MSRs. Having no knowledge
* of the model-specific requirements for writing to these model-specific
* registers, we will only blindly write to those MSRs if the 'force'
* argument is nonzero. That option should only be used in prototyping
* and debugging.
*/
/*ARGSUSED*/
int force)
{
int i, errs = 0;
for (i = 0; i < nregs; i++) {
if (cms_present(hdl)) {
errs++;
} else if (force) {
} else {
errs++;
}
}
}