authamd_main.c revision a4e4e13f4001644f2f960e3be0056c22b3a40fd1
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* "Generic AMD" model-specific support. If no more-specific support can
* be found, or such modules declines to initialize, then for AuthenticAMD
* cpus this module can have a crack at providing some AMD model-specific
* support that at least goes beyond common MCA architectural features
* if not down to the nitty-gritty level for a particular model. We
* are layered on top of a cpu module, likely cpu.generic, so there is no
* need for us to perform common architecturally-accessible functions.
*/
#include <sys/cpu_module.h>
#include <sys/pci_cfgspace.h>
#include <sys/x86_archext.h>
#include <sys/controlregs.h>
#include <sys/sysmacros.h>
#include <sys/cpu_module_ms_impl.h>
#include "authamd.h"
int authamd_ms_support_disable = 0;
#define AUTHAMD_F_REVS_BCDE \
#define AUTHAMD_F_REVS_FG \
#define AUTHAMD_10_REVS_AB \
/*
* Bitmasks of support for various features. Try to enable features
* via inclusion in one of these bitmasks and check that at the
* feature imlementation - that way new family support may often simply
* simply need to update these bitmasks.
*/
/*
* Families that this module will provide some model-specific
* support for (if no more-specific module claims it first).
* We try to support whole families rather than differentiate down
* to revision.
*/
#define AUTHAMD_SUPPORTED(fam) \
(fam) == AUTHAMD_FAMILY_10)
/*
* Models that include an on-chip NorthBridge.
*/
#define AUTHAMD_NBONCHIP(rev) \
/*
*/
#define AUTHAMD_MEMECC_RECOGNISED(rev) \
/*
*/
#define AUTHAMD_HAS_ONLINESPARECTL(rev) \
/*
*/
#define AUTHAMD_DO_NBMCACFG(rev) \
/*
*/
#define AUTHAMD_HAS_CHIPSCRUB(rev) \
/*
* evaluates to 0 if no support, otherwise the number of MC4_MISCj.
*/
#define AUTHAMD_NBMISC_NUM(rev) \
/*
* table walk errors - bit 10 of NB CTL.
*/
#define AUTHAMD_NOGARTTBLWLK_MC(rev) \
/*
*/
#define AUTHAMD_L3CAPABLE(rev) \
/*
* We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED
* revisions as:
*
* - being reported by the NB
* - being a compound bus/interconnect error (external to chip)
* - having LL of LG
* - having CECC or UECC set
*
* We do not check the extended error code (first nibble of the
* model-specific error code on AMD) since this has changed from
* family 0xf to family 0x10 (ext code 0 now reserved on family 0x10).
* abort cases.
*
* We insist that the detector be the NorthBridge bank; although
* an address at sufficient resolution to be useful and the NB will
* report most errors.
*/
((bank) == AMD_MCA_BANK_NB && \
static authamd_error_disp_t authamd_memce_disp = {
};
static authamd_error_disp_t authamd_memue_disp = {
};
static authamd_error_disp_t authamd_ckmemce_disp = {
};
static authamd_error_disp_t authamd_ckmemue_disp = {
};
/*
* We recognise GART walk errors as:
*
* - being reported by the NB
* - being a compound TLB error
* - having LL of LG and TT of GEN
* - having UC set
* - possibly having PCC set (if source CPU)
*/
((bank) == AMD_MCA_BANK_NB && \
(status) & MSR_MC_STATUS_UC)
static authamd_error_disp_t authamd_gart_disp = {
FM_EREPORT_CPU_GENAMD, /* use generic subclass */
FM_EREPORT_CPU_GENADM_GARTTBLWLK, /* use generic leafclass */
0 /* no additional payload */
};
static int
{
}
static void
{
}
static uint32_t
{
}
void
{
return;
if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
}
}
void
{
if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
}
}
/*
* Read EccCnt repeatedly for all possible channel/chip-select combos:
*
* - read sparectl register
* - if EccErrCntWrEn is set, clear that bit in the just-read value
* and write it back to sparectl; this *may* clobber the EccCnt
* for the channel/chip-select combination currently selected, so
* we leave this bit clear if we had to clear it
* - cycle through all channel/chip-select combinations writing each
* combination to sparectl before reading the register back for
* EccCnt for that combination; since EccErrCntWrEn is clear
* the writes to select what count to read will not themselves
* zero any counts
*/
static int
{
union mcreg_sparectl sparectl;
/*
* Check for feature support; this macro will test down to the
* family revision number, whereafter we'll switch on family
* assuming that future revisions will use the same register
* format.
*/
if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) {
return (0);
}
MCREG_VAL32(&sparectl) =
switch (family) {
case AUTHAMD_FAMILY_F:
break;
case AUTHAMD_FAMILY_10:
break;
}
switch (family) {
case AUTHAMD_FAMILY_F:
chan;
break;
case AUTHAMD_FAMILY_10:
chan;
break;
}
switch (family) {
case AUTHAMD_FAMILY_F:
EccErrCntDramCs) = cs;
break;
case AUTHAMD_FAMILY_10:
EccErrCntDramCs) = cs;
break;
}
switch (family) {
case AUTHAMD_FAMILY_F:
break;
case AUTHAMD_FAMILY_10:
break;
}
}
}
return (1);
}
/*
* Clear EccCnt for all possible channel/chip-select combos:
*
* - set EccErrCntWrEn in sparectl, if necessary
* - write 0 to EccCnt for all channel/chip-select combinations
* - clear EccErrCntWrEn
*
* If requested also disable the interrupts taken on counter overflow
* and on swap done.
*/
static void
{
union mcreg_sparectl sparectl;
if (!AUTHAMD_HAS_ONLINESPARECTL(rev))
return;
MCREG_VAL32(&sparectl) =
switch (family) {
case AUTHAMD_FAMILY_F:
if (clrint) {
}
break;
case AUTHAMD_FAMILY_10:
if (clrint) {
}
break;
}
switch (family) {
case AUTHAMD_FAMILY_F:
chan;
break;
case AUTHAMD_FAMILY_10:
chan;
break;
}
switch (family) {
case AUTHAMD_FAMILY_F:
EccErrCntDramCs) = cs;
EccErrCnt) = 0;
break;
case AUTHAMD_FAMILY_10:
EccErrCntDramCs) = cs;
EccErrCnt) = 0;
break;
}
}
}
}
/*
* cms_init entry point.
*
* This module provides broad model-specific support for AMD families
* 0x6, 0xf and 0x10. Future families will have to be evaluated once their
* documentation is available.
*/
int
{
return (ENOTSUP);
if (!(x86_feature & X86_MCA))
return (ENOTSUP);
return (ENOTSUP);
if (!(cap & MCG_CAP_CTL_P))
return (ENOTSUP);
} else {
}
}
return (0);
}
/*
* cms_logout_size entry point.
*/
/*ARGSUSED*/
{
return (sizeof (struct authamd_logout));
}
/*
* cms_mcgctl_val entry point
*
* Instead of setting all bits to 1 we can set just those for the
* error detector banks known to exist.
*/
/*ARGSUSED*/
{
}
/*
* cms_bankctl_skipinit entry point
*
* On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC)
* may produce spurious machine checks.
*
* Only allow a single core to setup the NorthBridge MCi_CTL register.
*/
/*ARGSUSED*/
{
}
return (B_FALSE);
}
/*
* cms_bankctl_val entry point
*/
{
/*
* The Intel MCA says we can write all 1's to enable #MC for
* all errors, and AMD docs say much the same. But, depending
* perhaps on other config registers, taking machine checks
* aborts may be bad - they set UC and sometime also PCC, but
* we should not always panic for these error types.
*
* Our cms_error_action entry point can suppress such panics,
* however we can also use the cms_bankctl_val entry point to
* veto enabling of some of the known villains in the first place.
*/
val &= ~AMD_NB_EN_GARTTBLWK;
return (val);
}
/*
* Bits to add to NB MCA config (after watchdog config).
*/
/*
* Bits to remove from NB MCA config (after watchdog config)
*/
/*
* NB Watchdog policy, and rate we use if enabling.
*/
enum {
/*
* Per-core cache scrubbing policy and rates.
*/
enum {
AUTHAMD_SCRUB_BIOSDEFAULT, /* leave as BIOS configured */
AUTHAMD_SCRUB_FIXED, /* assign our chosen rate */
AUTHAMD_SCRUB_MAX /* use higher of ours and BIOS rate */
static uint32_t
{
if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) {
}
switch (authamd_scrub_policy) {
case AUTHAMD_SCRUB_FIXED:
break;
default:
"using default policy of AUTHAMD_SCRUB_MAX",
/*FALLTHRU*/
case AUTHAMD_SCRUB_MAX:
else
}
return (rate);
}
/*
* cms_mca_init entry point.
*/
/*ARGSUSED*/
void
{
/*
* On chips with a NB online spare control register take control
* and clear ECC counts.
*/
if (AUTHAMD_HAS_ONLINESPARECTL(rev) &&
}
/*
* And since we are claiming the telemetry stop the BIOS receiving
* an SMI on NB threshold overflow.
*/
if (AUTHAMD_NBMISC_NUM(rev) &&
union mcmsr_nbmisc nbm;
int i;
for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) {
continue;
} else if (X86_CHIPREV_ATLEAST(rev,
}
}
}
/*
* NB MCA Configuration Register.
*/
if (AUTHAMD_DO_NBMCACFG(rev) &&
switch (authamd_nb_watchdog_policy) {
break;
case AUTHAMD_NB_WDOG_DISABLE:
break;
default:
"unrecognised, using default policy",
/*FALLTHRU*/
if (!(val & AMD_NB_CFG_WDOGTMRDIS))
break; /* if enabled leave rate intact */
/*FALLTHRU*/
break;
}
/*
* Bit 0 of the NB MCA Config register is reserved on family
* 0x10.
*/
val);
}
/*
* Cache scrubbing. We can't enable DRAM scrubbing since
* we don't know the DRAM base for this node.
*/
if (AUTHAMD_HAS_CHIPSCRUB(rev) &&
int l3cap = 0;
if (AUTHAMD_L3CAPABLE(rev)) {
MC_CTL_REG_NBCAP) & MC_NBCAP_L3CAPABLE) != 0;
}
"authamd_scrub_rate_dcache");
"authamd_scrub_rate_l2cache");
"authamd_scrub_rate_l3cache") : 0;
}
}
/*
* cms_poll_ownermask entry point.
*/
{
int dopoll = 0;
dopoll = 1;
dopoll = 1;
}
if (dopoll)
}
/*
* cms_bank_logout entry point.
*/
/*ARGSUSED*/
void
{
return;
/*
* For main memory ECC errors on revisions with an Online Spare
* Control Register grab the ECC counts by channel and chip-select
* and reset them to 0.
*/
if (AUTHAMD_MEMECC_RECOGNISED(rev) &&
}
}
/*
* cms_error_action entry point
*/
int authamd_forgive_uc = 0; /* For test/debug only */
int authamd_forgive_pcc = 0; /* For test/debug only */
int authamd_fake_poison = 0; /* For test/debug only */
/*ARGSUSED*/
{
if (authamd_forgive_uc)
if (authamd_forgive_pcc)
if (rv)
return (rv);
if (disp == &authamd_gart_disp) {
/*
* GART walk errors set UC and possibly PCC (if source CPU)
* but should not be regarded as terminal.
*/
return (CMS_ERRSCOPE_IGNORE_ERR);
}
/*
* May also want to consider master abort and target abort. These
* also set UC and PCC (if src CPU) but the requester gets -1
* and I believe the IO stuff in Solaris will handle that.
*/
return (rv);
}
/*
* cms_disp_match entry point
*/
/*ARGSUSED*/
{
/* uint16_t errcode = MCAX86_ERRCODE(status); */
/*
* Recognise main memory ECC errors
*/
if (AUTHAMD_MEMECC_RECOGNISED(rev) &&
if (status & AMD_BANK_STAT_CECC) {
return (exterrcode == 0 ? &authamd_memce_disp :
} else if (status & AMD_BANK_STAT_UECC) {
return (exterrcode == 0 ? &authamd_memue_disp :
}
}
/*
* Recognise GART walk errors
*/
return (&authamd_gart_disp);
return (NULL);
}
/*
* cms_ereport_class entry point
*/
/*ARGSUSED*/
void
{
return;
}
/*ARGSUSED*/
static void
{
int nelems = 0;
return;
continue;
continue;
"motherboard", 0,
"memory-controller", 0,
"dram-channel", chan,
"chip-select", cs);
}
}
if (nelems == 0)
return;
NULL);
NULL);
for (i = 0; i < nelems; i++)
}
/*
* cms_ereport_add_logout entry point
*/
/*ARGSUSED*/
void
{
return;
NULL);
DATA_TYPE_STRING, "E",
NULL);
}
}
NULL);
DATA_TYPE_STRING, "C",
NULL);
}
}
mslogout);
}
}
/*
* cms_msrinject entry point
*/
{
rv = CMS_SUCCESS;
return (rv);
}
authamd_init, /* cms_init */
NULL, /* cms_post_startup */
NULL, /* cms_post_mpstartup */
authamd_logout_size, /* cms_logout_size */
authamd_mcgctl_val, /* cms_mcgctl_val */
authamd_bankctl_skipinit, /* cms_bankctl_skipinit */
authamd_bankctl_val, /* cms_bankctl_val */
NULL, /* cms_bankstatus_skipinit */
NULL, /* cms_bankstatus_val */
authamd_mca_init, /* cms_mca_init */
authamd_poll_ownermask, /* cms_poll_ownermask */
authamd_bank_logout, /* cms_bank_logout */
authamd_error_action, /* cms_error_action */
authamd_disp_match, /* cms_disp_match */
authamd_ereport_class, /* cms_ereport_class */
NULL, /* cms_ereport_detector */
NULL, /* cms_ereport_includestack */
authamd_ereport_add_logout, /* cms_ereport_add_logout */
authamd_msrinject, /* cms_msrinject */
NULL, /* cms_fini */
};
"Generic AMD model-specific MCA"
};
static struct modlinkage modlinkage = {
(void *)&modlcpu,
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
{
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}