#include <sys/types.h>
#include <sys/regset.h>
#include <sys/privregs.h>
#include <sys/pci_impl.h>
#include <sys/cpuvar.h>
#include <sys/x86_archext.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/pghw.h>
#include <sys/cyclic.h>
#include <sys/sysevent.h>
#include <sys/smbios.h>
#include <sys/mca_x86.h>
#include <sys/mca_amd.h>
#include <sys/mc.h>
#include <sys/mc_amd.h>
#include <sys/psw.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sdt.h>
#include <sys/fm/util.h>
#include <sys/fm/protocol.h>
#include <sys/fm/cpu/AMD.h>
#include <sys/fm/smb/fmsmb.h>
#include <sys/acpi/acpi.h>
#include <sys/acpi/acpi_pci.h>
#include <sys/acpica.h>
#include <sys/cpu_module.h>
#include "ao.h"
#include "ao_mca_disp.h"
#define AO_F_REVS_FG (X86_CHIPREV_AMD_LEGACY_F_REV_F | \
X86_CHIPREV_AMD_LEGACY_F_REV_G)
int ao_mca_smi_disable = 1;
extern int x86gentopo_legacy;
struct ao_ctl_init {
uint32_t ctl_revmask;
uint64_t ctl_bits;
};
static const struct ao_ctl_init ao_nb_ctl_init[] = {
{ AO_F_REVS_FG, AMD_NB_CTL_INIT_REV_FG },
{ X86_CHIPREV_UNKNOWN, 0 }
};
typedef struct ao_bank_cfg {
uint64_t bank_ctl_init_cmn;
const struct ao_ctl_init *bank_ctl_init_extra;
void (*bank_misc_initfunc)(cmi_hdl_t, ao_ms_data_t *, uint32_t);
uint_t bank_ctl_mask;
} ao_bank_cfg_t;
static void nb_mcamisc_init(cmi_hdl_t, ao_ms_data_t *, uint32_t);
static const ao_bank_cfg_t ao_bank_cfgs[] = {
{ AMD_DC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_DC_MASK },
{ AMD_IC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_IC_MASK },
{ AMD_BU_CTL_INIT_CMN, NULL, NULL, AMD_MSR_BU_MASK },
{ AMD_LS_CTL_INIT_CMN, NULL, NULL, AMD_MSR_LS_MASK },
{ AMD_NB_CTL_INIT_CMN, &ao_nb_ctl_init[0], nb_mcamisc_init,
AMD_MSR_NB_MASK },
};
static int ao_nbanks = sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]);
static const struct ao_smi_disable {
const char *asd_sys_vendor;
const char *asd_sys_product;
const char *asd_bios_vendor;
uint8_t asd_code;
} ao_smi_disable[] = {
{ "Sun Microsystems", "Galaxy12",
"American Megatrends", 0x59 },
{ "Sun Microsystems", "Sun Fire X4100 Server",
"American Megatrends", 0x59 },
{ "Sun Microsystems", "Sun Fire X4200 Server",
"American Megatrends", 0x59 },
{ NULL, NULL, NULL, 0 }
};
static int
ao_disp_match_r4(uint16_t ref, uint8_t r4)
{
static const uint16_t ao_r4_map[] = {
AO_MCA_R4_BIT_ERR,
AO_MCA_R4_BIT_RD,
AO_MCA_R4_BIT_WR,
AO_MCA_R4_BIT_DRD,
AO_MCA_R4_BIT_DWR,
AO_MCA_R4_BIT_IRD,
AO_MCA_R4_BIT_PREFETCH,
AO_MCA_R4_BIT_EVICT,
AO_MCA_R4_BIT_SNOOP
};
ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t));
return ((ref & ao_r4_map[r4]) != 0);
}
static int
ao_disp_match_pp(uint8_t ref, uint8_t pp)
{
static const uint8_t ao_pp_map[] = {
AO_MCA_PP_BIT_SRC,
AO_MCA_PP_BIT_RES,
AO_MCA_PP_BIT_OBS,
AO_MCA_PP_BIT_GEN
};
ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t));
return ((ref & ao_pp_map[pp]) != 0);
}
static int
ao_disp_match_ii(uint8_t ref, uint8_t ii)
{
static const uint8_t ao_ii_map[] = {
AO_MCA_II_BIT_MEM,
0,
AO_MCA_II_BIT_IO,
AO_MCA_II_BIT_GEN
};
ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t));
return ((ref & ao_ii_map[ii]) != 0);
}
static uint8_t
bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
{
uint8_t val = (*codep & mask) >> shift;
*codep &= ~mask;
return (val);
}
#define BIT_STRIP(codep, name) \
bit_strip(codep, MCAX86_ERRCODE_##name##_MASK, \
MCAX86_ERRCODE_##name##_SHIFT)
static int
ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev,
int bankno)
{
uint16_t code = MCAX86_ERRCODE(status);
uint8_t extcode = AMD_EXT_ERRCODE(status);
uint64_t stat_mask = aed->aed_stat_mask;
uint64_t stat_mask_res = aed->aed_stat_mask_res;
if (bankno == AMD_MCA_BANK_NB &&
!chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_F) &&
status & MSR_MC_STATUS_OVER) {
stat_mask &= ~AMD_BANK_STAT_CECC;
stat_mask_res &= ~AMD_BANK_STAT_CECC;
}
if ((status & stat_mask) != stat_mask_res)
return (0);
if (AMD_ERRCODE_ISMEM(code)) {
uint8_t r4 = BIT_STRIP(&code, RRRR);
if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4))
return (0);
} else if (AMD_ERRCODE_ISBUS(code)) {
uint8_t r4 = BIT_STRIP(&code, RRRR);
uint8_t pp = BIT_STRIP(&code, PP);
uint8_t ii = BIT_STRIP(&code, II);
if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) ||
!ao_disp_match_pp(aed->aed_stat_pp_bits, pp) ||
!ao_disp_match_ii(aed->aed_stat_ii_bits, ii))
return (0);
}
return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode);
}
cms_cookie_t
ao_ms_disp_match(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
uint64_t addr, uint64_t misc, void *mslogout)
{
ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
const ao_error_disp_t *aed;
for (aed = ao_error_disp[banknum]; aed->aed_stat_mask != 0; aed++) {
if (ao_disp_match_one(aed, status, rev, banknum))
return ((cms_cookie_t)aed);
}
return (NULL);
}
void
ao_ms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
const char **cpuclsp, const char **leafclsp)
{
const ao_error_disp_t *aed = mscookie;
if (aed != NULL) {
*cpuclsp = FM_EREPORT_CPU_AMD;
*leafclsp = aed->aed_class;
}
}
static int
ao_chip_once(ao_ms_data_t *ao, enum ao_cfgonce_bitnum what)
{
return (atomic_set_long_excl(&ao->ao_ms_shared->aos_cfgonce,
what) == 0 ? B_TRUE : B_FALSE);
}
int ao_nb_cfg_mc4misc_noseize = 0;
static void
nb_mcamisc_init(cmi_hdl_t hdl, ao_ms_data_t *ao, uint32_t rev)
{
uint64_t val, nval;
if (!chiprev_matches(rev, AO_F_REVS_FG))
return;
if (cmi_hdl_rdmsr(hdl, AMD_MSR_NB_MISC, &val) != CMI_SUCCESS)
return;
ao->ao_ms_shared->aos_bcfg_nb_misc = val;
if (ao_nb_cfg_mc4misc_noseize)
return;
if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP))
return;
nval = val;
nval |= AMD_NB_MISC_CNTEN;
nval &= ~AMD_NB_MISC_ERRCOUNT_MASK;
nval &= ~AMD_NB_MISC_OVRFLW;
nval &= ~AMD_NB_MISC_INTTYPE_MASK;
nval |= AMD_NB_MISC_LOCKED;
if (nval != val) {
uint64_t locked = val & AMD_NB_MISC_LOCKED;
if (locked)
ao_bankstatus_prewrite(hdl, ao);
(void) cmi_hdl_wrmsr(hdl, AMD_MSR_NB_MISC, nval);
if (locked)
ao_bankstatus_postwrite(hdl, ao);
}
}
uint32_t ao_nb_cfg_add_cmn = AMD_NB_CFG_ADD_CMN;
uint32_t ao_nb_cfg_remove_cmn = AMD_NB_CFG_REMOVE_CMN;
uint32_t ao_nb_cfg_add_revFG = AMD_NB_CFG_ADD_REV_FG;
uint32_t ao_nb_cfg_remove_revFG = AMD_NB_CFG_REMOVE_REV_FG;
struct ao_nb_cfg {
uint32_t cfg_revmask;
uint32_t *cfg_add_p;
uint32_t *cfg_remove_p;
};
static const struct ao_nb_cfg ao_cfg_extra[] = {
{ AO_F_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG },
{ X86_CHIPREV_UNKNOWN, NULL, NULL }
};
uint32_t ao_nb_cfg_wdog =
AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
AMD_NB_CFG_WDOGTMRBASESEL_1MS;
enum {
AO_NB_WDOG_LEAVEALONE,
AO_NB_WDOG_DISABLE,
AO_NB_WDOG_ENABLE_IF_DISABLED,
AO_NB_WDOG_ENABLE_FORCE_RATE
} ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED;
static void
ao_nb_cfg(ao_ms_data_t *ao, uint32_t rev)
{
const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0];
uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
uint32_t val;
ao->ao_ms_shared->aos_bcfg_nb_cfg = val =
ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
switch (ao_nb_watchdog_policy) {
case AO_NB_WDOG_LEAVEALONE:
break;
case AO_NB_WDOG_DISABLE:
val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
val |= AMD_NB_CFG_WDOGTMRDIS;
break;
default:
cmn_err(CE_NOTE, "ao_nb_watchdog_policy=%d unrecognised, "
"using default policy", ao_nb_watchdog_policy);
case AO_NB_WDOG_ENABLE_IF_DISABLED:
if (!(val & AMD_NB_CFG_WDOGTMRDIS))
break;
case AO_NB_WDOG_ENABLE_FORCE_RATE:
val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
val &= ~AMD_NB_CFG_WDOGTMRDIS;
val |= ao_nb_cfg_wdog;
break;
}
val &= ~ao_nb_cfg_remove_cmn;
val |= ao_nb_cfg_add_cmn;
while (nbcp->cfg_revmask != X86_CHIPREV_UNKNOWN) {
if (chiprev_matches(rev, nbcp->cfg_revmask)) {
val &= ~(*nbcp->cfg_remove_p);
val |= *nbcp->cfg_add_p;
}
nbcp++;
}
ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG, val);
}
static void
ao_dram_cfg(ao_ms_data_t *ao, uint32_t rev)
{
uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
union mcreg_dramcfg_lo dcfglo;
ao->ao_ms_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) =
ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO);
ao->ao_ms_shared->aos_bcfg_dcfg_hi =
ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGHI);
#ifdef OPTERON_ERRATUM_172
if (chiprev_matches(rev, AO_F_REVS_FG) &&
MCREG_FIELD_F_revFG(&dcfglo, ParEn)) {
MCREG_FIELD_F_revFG(&dcfglo, ParEn) = 0;
ao_pcicfg_write(procnodeid, MC_FUNC_DRAMCTL,
MC_DC_REG_DRAMCFGLO, MCREG_VAL32(&dcfglo));
}
#endif
}
int ao_nb_cfg_sparectl_noseize = 0;
static void
ao_sparectl_cfg(ao_ms_data_t *ao)
{
uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
union mcreg_sparectl sparectl;
int chan, cs;
ao->ao_ms_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) =
ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
if (ao_nb_cfg_sparectl_noseize)
return;
MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0;
MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0;
MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1;
MCREG_FIELD_F_revFG(&sparectl, EccErrCnt) = 0;
for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) {
MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = chan;
for (cs = 0; cs < MC_CHIP_NCS; cs++) {
MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramCs) = cs;
ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
}
}
}
int ao_forgive_uc = 0;
int ao_forgive_pcc = 0;
int ao_fake_poison = 0;
uint32_t
ao_ms_error_action(cmi_hdl_t hdl, int ismc, int banknum,
uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
{
const ao_error_disp_t *aed;
uint32_t retval = 0;
uint8_t when;
int en;
if (ao_forgive_uc)
retval |= CMS_ERRSCOPE_CLEARED_UC;
if (ao_forgive_pcc)
retval |= CMS_ERRSCOPE_CURCONTEXT_OK;
if (ao_fake_poison && status & MSR_MC_STATUS_UC)
retval |= CMS_ERRSCOPE_POISONED;
if (retval)
return (retval);
aed = ao_ms_disp_match(hdl, ismc, banknum, status, addr, misc,
mslogout);
if (aed == NULL)
return (0);
en = (status & MSR_MC_STATUS_EN) != 0;
if ((when = aed->aed_panic_when) == AO_AED_PANIC_NEVER)
retval |= CMS_ERRSCOPE_IGNORE_ERR;
if ((when & AO_AED_PANIC_ALWAYS) ||
((when & AO_AED_PANIC_IFMCE) && (en || ismc)))
retval |= CMS_ERRSCOPE_FORCE_FATAL;
return (retval);
}
static uint_t
ao_ereport_synd(ao_ms_data_t *ao, uint64_t status, uint_t *typep,
int is_nb)
{
if (is_nb) {
if (ao->ao_ms_shared->aos_bcfg_nb_cfg &
AMD_NB_CFG_CHIPKILLECCEN) {
*typep = AMD_SYNDTYPE_CHIPKILL;
return (AMD_NB_STAT_CKSYND(status));
} else {
*typep = AMD_SYNDTYPE_ECC;
return (AMD_BANK_SYND(status));
}
} else {
*typep = AMD_SYNDTYPE_ECC;
return (AMD_BANK_SYND(status));
}
}
static nvlist_t *
ao_ereport_create_resource_elem(cmi_hdl_t hdl, nv_alloc_t *nva,
mc_unum_t *unump, int dimmnum)
{
nvlist_t *nvl, *snvl;
nvlist_t *board_list = NULL;
if ((nvl = fm_nvlist_create(nva)) == NULL)
return (NULL);
if ((snvl = fm_nvlist_create(nva)) == NULL) {
fm_nvlist_destroy(nvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
return (NULL);
}
(void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
unump->unum_offset);
if (!x86gentopo_legacy) {
board_list = cmi_hdl_smb_bboard(hdl);
if (board_list == NULL) {
fm_nvlist_destroy(nvl,
nva ? FM_NVA_RETAIN : FM_NVA_FREE);
fm_nvlist_destroy(snvl,
nva ? FM_NVA_RETAIN : FM_NVA_FREE);
return (NULL);
}
fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, snvl,
board_list, 4,
"chip", cmi_hdl_smb_chipid(hdl),
"memory-controller", unump->unum_mc,
"dimm", unump->unum_dimms[dimmnum],
"rank", unump->unum_rank);
} else {
fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, snvl, 5,
"motherboard", unump->unum_board,
"chip", unump->unum_chip,
"memory-controller", unump->unum_mc,
"dimm", unump->unum_dimms[dimmnum],
"rank", unump->unum_rank);
}
fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
return (nvl);
}
static void
ao_ereport_add_resource(cmi_hdl_t hdl, nvlist_t *payload, nv_alloc_t *nva,
mc_unum_t *unump)
{
nvlist_t *elems[MC_UNUM_NDIMM];
int nelems = 0;
int i;
for (i = 0; i < MC_UNUM_NDIMM; i++) {
if (unump->unum_dimms[i] == MC_INVALNUM)
break;
if ((elems[nelems] = ao_ereport_create_resource_elem(hdl, nva,
unump, i)) == NULL)
break;
nelems++;
}
if (nelems == 0)
return;
fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
for (i = 0; i < nelems; i++)
fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
}
void
ao_ms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport,
nv_alloc_t *nva, int banknum, uint64_t status, uint64_t addr,
uint64_t misc, void *mslogout, cms_cookie_t mscookie)
{
ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
const ao_error_disp_t *aed = mscookie;
uint_t synd, syndtype;
uint64_t members;
if (aed == NULL)
return;
members = aed->aed_ereport_members;
synd = ao_ereport_synd(ao, status, &syndtype,
banknum == AMD_MCA_BANK_NB);
if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND,
DATA_TYPE_UINT16, synd, NULL);
}
if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) {
fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE,
DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ?
"C4" : "E"), NULL);
}
if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) {
mc_unum_t unum;
if (((aed->aed_flags & AO_AED_FLAGS_ADDRTYPE) ==
AO_AED_F_PHYSICAL) && (status & MSR_MC_STATUS_ADDRV) &&
cmi_mc_patounum(addr, aed->aed_addrvalid_hi,
aed->aed_addrvalid_lo, synd, syndtype, &unum) ==
CMI_SUCCESS)
ao_ereport_add_resource(hdl, ereport, nva, &unum);
}
}
boolean_t
ao_ms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
{
const ao_error_disp_t *aed = mscookie;
if (aed == NULL)
return (0);
return ((aed->aed_ereport_members &
FM_EREPORT_PAYLOAD_FLAG_STACK) != 0);
}
cms_errno_t
ao_ms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
{
ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
cms_errno_t rv = CMSERR_BADMSRWRITE;
ao_bankstatus_prewrite(hdl, ao);
if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
rv = CMS_SUCCESS;
ao_bankstatus_postwrite(hdl, ao);
return (rv);
}
uint64_t
ao_ms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
{
return ((1ULL << nbanks) - 1);
}
boolean_t
ao_ms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
{
ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
if (banknum != AMD_MCA_BANK_NB)
return (B_FALSE);
return (ao_chip_once(ao, AO_CFGONCE_NBMCA) == B_TRUE ?
B_FALSE : B_TRUE);
}
uint64_t
ao_ms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
{
ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
const struct ao_ctl_init *extrap;
const ao_bank_cfg_t *bankcfg;
uint64_t mcictl;
x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
if (banknum >= sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]))
return (def);
bankcfg = &ao_bank_cfgs[banknum];
extrap = bankcfg->bank_ctl_init_extra;
mcictl = bankcfg->bank_ctl_init_cmn;
while (extrap != NULL && extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) {
if (chiprev_matches(rev, extrap->ctl_revmask))
mcictl |= extrap->ctl_bits;
extrap++;
}
return (mcictl);
}
void
ao_bankstatus_prewrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
{
#ifndef __xpv
uint64_t hwcr;
if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
return;
ao->ao_ms_hwcr_val = hwcr;
if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
hwcr |= AMD_HWCR_MCI_STATUS_WREN;
(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
}
#endif
}
void
ao_bankstatus_postwrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
{
#ifndef __xpv
uint64_t hwcr = ao->ao_ms_hwcr_val;
if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
}
#endif
}
void
ao_ms_mca_init(cmi_hdl_t hdl, int nbanks)
{
ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
ao_ms_mca_t *mca = &ao->ao_ms_mca;
uint64_t *maskp;
int i;
maskp = mca->ao_mca_bios_cfg.bcfg_bank_mask = kmem_zalloc(nbanks *
sizeof (uint64_t), KM_SLEEP);
for (i = 0; i < MIN(nbanks, ao_nbanks); i++) {
(void) cmi_hdl_rdmsr(hdl, ao_bank_cfgs[i].bank_ctl_mask,
maskp++);
if (ao_bank_cfgs[i].bank_misc_initfunc != NULL)
ao_bank_cfgs[i].bank_misc_initfunc(hdl, ao, rev);
}
if (ao_chip_once(ao, AO_CFGONCE_NBCFG) == B_TRUE) {
ao_nb_cfg(ao, rev);
if (chiprev_matches(rev, AO_F_REVS_FG))
ao_sparectl_cfg(ao);
}
if (ao_chip_once(ao, AO_CFGONCE_DRAMCFG) == B_TRUE)
ao_dram_cfg(ao, rev);
ao_procnode_scrubber_enable(hdl, ao);
}
static int
ao_acpi_find_smicmd(int *asd_port)
{
ACPI_TABLE_FADT *fadt = NULL;
if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
AE_OK)
return (-1);
ASSERT(fadt != NULL);
*asd_port = fadt->SmiCommand;
return (0);
}
void
ao_ms_post_startup(cmi_hdl_t hdl)
{
const struct ao_smi_disable *asd;
id_t id;
int rv = -1, asd_port;
smbios_system_t sy;
smbios_bios_t sb;
smbios_info_t si;
if (ao_mca_smi_disable && ksmbios != NULL &&
smbios_info_bios(ksmbios, &sb) != SMB_ERR &&
(id = smbios_info_system(ksmbios, &sy)) != SMB_ERR &&
smbios_info_common(ksmbios, id, &si) != SMB_ERR) {
for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) {
if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer,
strlen(asd->asd_sys_vendor)) != 0 ||
strncmp(asd->asd_sys_product, si.smbi_product,
strlen(asd->asd_sys_product)) != 0 ||
strncmp(asd->asd_bios_vendor, sb.smbb_vendor,
strlen(asd->asd_bios_vendor)) != 0)
continue;
if ((rv = ao_acpi_find_smicmd(&asd_port)) == 0 &&
asd_port != 0) {
cmn_err(CE_CONT, "?SMI polling disabled in "
"favor of Solaris Fault Management for "
"AMD Processors\n");
outb(asd_port, asd->asd_code);
} else if (rv < 0) {
cmn_err(CE_CONT, "?Solaris Fault Management "
"for AMD Processors could not disable SMI "
"polling because an error occurred while "
"trying to determine the SMI command port "
"from the ACPI FADT table\n");
}
break;
}
}
}