xref: /onnv-gate/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c (revision 12437:76e2c024699d)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
51538Sgavinm  * Common Development and Distribution License (the "License").
61538Sgavinm  * You may not use this file except in compliance with the License.
71414Scindi  *
81414Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi  * or http://www.opensolaris.org/os/licensing.
101414Scindi  * See the License for the specific language governing permissions
111414Scindi  * and limitations under the License.
121414Scindi  *
131414Scindi  * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi  * If applicable, add the following below this CDDL HEADER, with the
161414Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi  *
191414Scindi  * CDDL HEADER END
201414Scindi  */
211414Scindi 
221414Scindi /*
23*12437SAdrian.Frost@Sun.COM  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241414Scindi  */
251414Scindi 
261414Scindi #include <sys/types.h>
271414Scindi #include <sys/regset.h>
281414Scindi #include <sys/privregs.h>
291414Scindi #include <sys/pci_impl.h>
301414Scindi #include <sys/cpuvar.h>
311414Scindi #include <sys/x86_archext.h>
321414Scindi #include <sys/cmn_err.h>
331414Scindi #include <sys/systm.h>
341414Scindi #include <sys/sysmacros.h>
353434Sesaxe #include <sys/pghw.h>
361414Scindi #include <sys/cyclic.h>
371414Scindi #include <sys/sysevent.h>
381414Scindi #include <sys/smbios.h>
391414Scindi #include <sys/mca_x86.h>
401414Scindi #include <sys/mca_amd.h>
411414Scindi #include <sys/mc.h>
422869Sgavinm #include <sys/mc_amd.h>
431414Scindi #include <sys/psw.h>
441414Scindi #include <sys/ddi.h>
451414Scindi #include <sys/sunddi.h>
461414Scindi #include <sys/sdt.h>
471414Scindi #include <sys/fm/util.h>
481414Scindi #include <sys/fm/protocol.h>
491414Scindi #include <sys/fm/cpu/AMD.h>
5010942STom.Pothier@Sun.COM #include <sys/fm/smb/fmsmb.h>
511959Ssethg #include <sys/acpi/acpi.h>
521959Ssethg #include <sys/acpi/acpi_pci.h>
531959Ssethg #include <sys/acpica.h>
545254Sgavinm #include <sys/cpu_module.h>
551414Scindi 
561414Scindi #include "ao.h"
571414Scindi #include "ao_mca_disp.h"
581414Scindi 
595254Sgavinm #define	AO_F_REVS_FG (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G)
601414Scindi 
615254Sgavinm int ao_mca_smi_disable = 1;		/* attempt to disable SMI polling */
622869Sgavinm 
6310942STom.Pothier@Sun.COM extern int x86gentopo_legacy;	/* x86 generic topology support */
6410942STom.Pothier@Sun.COM 
652869Sgavinm struct ao_ctl_init {
662869Sgavinm 	uint32_t ctl_revmask;	/* rev(s) to which this applies */
672869Sgavinm 	uint64_t ctl_bits;	/* mca ctl reg bitmask to set */
682869Sgavinm };
692869Sgavinm 
702869Sgavinm /*
712869Sgavinm  * Additional NB MCA ctl initialization for revs F and G
722869Sgavinm  */
732869Sgavinm static const struct ao_ctl_init ao_nb_ctl_init[] = {
745254Sgavinm 	{ AO_F_REVS_FG, AMD_NB_CTL_INIT_REV_FG },
752869Sgavinm 	{ X86_CHIPREV_UNKNOWN, 0 }
761414Scindi };
771414Scindi 
781414Scindi typedef struct ao_bank_cfg {
792869Sgavinm 	uint64_t bank_ctl_init_cmn;			/* Common init value */
802869Sgavinm 	const struct ao_ctl_init *bank_ctl_init_extra;	/* Extra for each rev */
815254Sgavinm 	void (*bank_misc_initfunc)(cmi_hdl_t, ao_ms_data_t *, uint32_t);
825254Sgavinm 	uint_t bank_ctl_mask;
831414Scindi } ao_bank_cfg_t;
841414Scindi 
855254Sgavinm static void nb_mcamisc_init(cmi_hdl_t, ao_ms_data_t *, uint32_t);
862869Sgavinm 
871414Scindi static const ao_bank_cfg_t ao_bank_cfgs[] = {
885254Sgavinm 	{ AMD_DC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_DC_MASK },
895254Sgavinm 	{ AMD_IC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_IC_MASK },
905254Sgavinm 	{ AMD_BU_CTL_INIT_CMN, NULL, NULL, AMD_MSR_BU_MASK },
915254Sgavinm 	{ AMD_LS_CTL_INIT_CMN, NULL, NULL, AMD_MSR_LS_MASK },
925254Sgavinm 	{ AMD_NB_CTL_INIT_CMN, &ao_nb_ctl_init[0], nb_mcamisc_init,
935254Sgavinm 		AMD_MSR_NB_MASK },
941414Scindi };
951414Scindi 
965254Sgavinm static int ao_nbanks = sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]);
971414Scindi 
981414Scindi /*
991414Scindi  * This is quite awful but necessary to work around x86 system vendor's view of
1001414Scindi  * the world.  Other operating systems (you know who you are) don't understand
1011414Scindi  * Opteron-specific error handling, so BIOS and system vendors often hide these
1021414Scindi  * conditions from them by using SMI polling to copy out any errors from the
1031414Scindi  * machine-check registers.  When Solaris runs on a system with this feature,
1041414Scindi  * we want to disable the SMI polling so we can use FMA instead.  Sadly, there
1051414Scindi  * isn't even a standard self-describing way to express the whole situation,
1061414Scindi  * so we have to resort to hard-coded values.  This should all be changed to
1071414Scindi  * be a self-describing vendor-specific SMBIOS structure in the future.
1081414Scindi  */
1091414Scindi static const struct ao_smi_disable {
1101414Scindi 	const char *asd_sys_vendor;	/* SMB_TYPE_SYSTEM vendor prefix */
1113164Sgavinm 	const char *asd_sys_product;	/* SMB_TYPE_SYSTEM product prefix */
1121414Scindi 	const char *asd_bios_vendor;	/* SMB_TYPE_BIOS vendor prefix */
1131964Ssethg 	uint8_t asd_code;		/* output code for SMI disable */
1141414Scindi } ao_smi_disable[] = {
1153164Sgavinm 	{ "Sun Microsystems", "Galaxy12",
1163164Sgavinm 	    "American Megatrends", 0x59 },
1173164Sgavinm 	{ "Sun Microsystems", "Sun Fire X4100 Server",
1183164Sgavinm 	    "American Megatrends", 0x59 },
1193164Sgavinm 	{ "Sun Microsystems", "Sun Fire X4200 Server",
1203164Sgavinm 	    "American Megatrends", 0x59 },
1213164Sgavinm 	{ NULL, NULL, NULL, 0 }
1221414Scindi };
1231414Scindi 
1241414Scindi static int
ao_disp_match_r4(uint16_t ref,uint8_t r4)1251414Scindi ao_disp_match_r4(uint16_t ref, uint8_t r4)
1261414Scindi {
1271414Scindi 	static const uint16_t ao_r4_map[] = {
1285254Sgavinm 		AO_MCA_R4_BIT_ERR,	/* MCAX86_ERRCODE_RRRR_ERR */
1295254Sgavinm 		AO_MCA_R4_BIT_RD,	/* MCAX86_ERRCODE_RRRR_RD */
1305254Sgavinm 		AO_MCA_R4_BIT_WR,	/* MCAX86_ERRCODE_RRRR_WR */
1315254Sgavinm 		AO_MCA_R4_BIT_DRD,	/* MCAX86_ERRCODE_RRRR_DRD */
1325254Sgavinm 		AO_MCA_R4_BIT_DWR,	/* MCAX86_ERRCODE_RRRR_DWR */
1335254Sgavinm 		AO_MCA_R4_BIT_IRD,	/* MCAX86_ERRCODE_RRRR_IRD */
1345254Sgavinm 		AO_MCA_R4_BIT_PREFETCH,	/* MCAX86_ERRCODE_RRRR_PREFETCH */
1355254Sgavinm 		AO_MCA_R4_BIT_EVICT,	/* MCAX86_ERRCODE_RRRR_EVICT */
1365254Sgavinm 		AO_MCA_R4_BIT_SNOOP	/* MCAX86_ERRCODE_RRRR_SNOOP */
1371414Scindi 	};
1381414Scindi 
1391414Scindi 	ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t));
1401414Scindi 
1411414Scindi 	return ((ref & ao_r4_map[r4]) != 0);
1421414Scindi }
1431414Scindi 
1441414Scindi static int
ao_disp_match_pp(uint8_t ref,uint8_t pp)1451414Scindi ao_disp_match_pp(uint8_t ref, uint8_t pp)
1461414Scindi {
1471414Scindi 	static const uint8_t ao_pp_map[] = {
1485254Sgavinm 		AO_MCA_PP_BIT_SRC,	/* MCAX86_ERRCODE_PP_SRC */
1495254Sgavinm 		AO_MCA_PP_BIT_RES,	/* MCAX86_ERRCODE_PP_RES */
1505254Sgavinm 		AO_MCA_PP_BIT_OBS,	/* MCAX86_ERRCODE_PP_OBS */
1515254Sgavinm 		AO_MCA_PP_BIT_GEN	/* MCAX86_ERRCODE_PP_GEN */
1521414Scindi 	};
1531414Scindi 
1541414Scindi 	ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t));
1551414Scindi 
1561414Scindi 	return ((ref & ao_pp_map[pp]) != 0);
1571414Scindi }
1581414Scindi 
1591414Scindi static int
ao_disp_match_ii(uint8_t ref,uint8_t ii)1601414Scindi ao_disp_match_ii(uint8_t ref, uint8_t ii)
1611414Scindi {
1621414Scindi 	static const uint8_t ao_ii_map[] = {
1635254Sgavinm 		AO_MCA_II_BIT_MEM,	/* MCAX86_ERRCODE_II_MEM */
1641414Scindi 		0,
1655254Sgavinm 		AO_MCA_II_BIT_IO,	/* MCAX86_ERRCODE_II_IO */
1665254Sgavinm 		AO_MCA_II_BIT_GEN	/* MCAX86_ERRCODE_II_GEN */
1671414Scindi 	};
1681414Scindi 
1691414Scindi 	ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t));
1701414Scindi 
1711414Scindi 	return ((ref & ao_ii_map[ii]) != 0);
1721414Scindi }
1731414Scindi 
1741414Scindi static uint8_t
bit_strip(uint16_t * codep,uint16_t mask,uint16_t shift)1751414Scindi bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
1761414Scindi {
1771414Scindi 	uint8_t val = (*codep & mask) >> shift;
1781414Scindi 	*codep &= ~mask;
1791414Scindi 	return (val);
1801414Scindi }
1811414Scindi 
1821414Scindi #define	BIT_STRIP(codep, name) \
1835254Sgavinm 	bit_strip(codep, MCAX86_ERRCODE_##name##_MASK, \
1845254Sgavinm 	MCAX86_ERRCODE_##name##_SHIFT)
1851414Scindi 
1865254Sgavinm /*ARGSUSED*/
1871414Scindi static int
ao_disp_match_one(const ao_error_disp_t * aed,uint64_t status,uint32_t rev,int bankno)1882869Sgavinm ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev,
1892869Sgavinm     int bankno)
1901414Scindi {
1915254Sgavinm 	uint16_t code = MCAX86_ERRCODE(status);
1925254Sgavinm 	uint8_t extcode = AMD_EXT_ERRCODE(status);
1931414Scindi 	uint64_t stat_mask = aed->aed_stat_mask;
1941414Scindi 	uint64_t stat_mask_res = aed->aed_stat_mask_res;
1951414Scindi 
1961414Scindi 	/*
1971414Scindi 	 * If the bank's status register indicates overflow, then we can no
1981414Scindi 	 * longer rely on the value of CECC: our experience with actual fault
1991414Scindi 	 * injection has shown that multiple CE's overwriting each other shows
2001414Scindi 	 * AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero.  This
2011414Scindi 	 * should be clarified in a future BKDG or by the Revision Guide.
2022869Sgavinm 	 * This behaviour is fixed in revision F.
2031414Scindi 	 */
2042869Sgavinm 	if (bankno == AMD_MCA_BANK_NB &&
2052869Sgavinm 	    !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) &&
2065254Sgavinm 	    status & MSR_MC_STATUS_OVER) {
2071414Scindi 		stat_mask &= ~AMD_BANK_STAT_CECC;
2081414Scindi 		stat_mask_res &= ~AMD_BANK_STAT_CECC;
2091414Scindi 	}
2101414Scindi 
2111414Scindi 	if ((status & stat_mask) != stat_mask_res)
2121414Scindi 		return (0);
2131414Scindi 
2141414Scindi 	/*
2151414Scindi 	 * r4 and pp bits are stored separately, so we mask off and compare them
2161414Scindi 	 * for the code types that use them.  Once we've taken the r4 and pp
2171414Scindi 	 * bits out of the equation, we can directly compare the resulting code
2181414Scindi 	 * with the one stored in the ao_error_disp_t.
2191414Scindi 	 */
2201414Scindi 	if (AMD_ERRCODE_ISMEM(code)) {
2215254Sgavinm 		uint8_t r4 = BIT_STRIP(&code, RRRR);
2221414Scindi 
2231414Scindi 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4))
2241414Scindi 			return (0);
2251414Scindi 
2261414Scindi 	} else if (AMD_ERRCODE_ISBUS(code)) {
2275254Sgavinm 		uint8_t r4 = BIT_STRIP(&code, RRRR);
2281414Scindi 		uint8_t pp = BIT_STRIP(&code, PP);
2291414Scindi 		uint8_t ii = BIT_STRIP(&code, II);
2301414Scindi 
2311414Scindi 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) ||
2321414Scindi 		    !ao_disp_match_pp(aed->aed_stat_pp_bits, pp) ||
2331414Scindi 		    !ao_disp_match_ii(aed->aed_stat_ii_bits, ii))
2341414Scindi 			return (0);
2351414Scindi 	}
2361414Scindi 
2371414Scindi 	return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode);
2381414Scindi }
2391414Scindi 
2405254Sgavinm /*ARGSUSED*/
2415254Sgavinm 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)242*12437SAdrian.Frost@Sun.COM ao_ms_disp_match(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
2435254Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
2441414Scindi {
2455254Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
2465254Sgavinm 	uint32_t rev = ao->ao_ms_shared->aos_chiprev;
2471414Scindi 	const ao_error_disp_t *aed;
2481414Scindi 
2495254Sgavinm 	for (aed = ao_error_disp[banknum]; aed->aed_stat_mask != 0; aed++) {
2505254Sgavinm 		if (ao_disp_match_one(aed, status, rev, banknum))
2515254Sgavinm 			return ((cms_cookie_t)aed);
2521414Scindi 	}
2531414Scindi 
2545254Sgavinm 	return (NULL);
2552869Sgavinm }
2562869Sgavinm 
2575254Sgavinm /*ARGSUSED*/
2585254Sgavinm void
ao_ms_ereport_class(cmi_hdl_t hdl,cms_cookie_t mscookie,const char ** cpuclsp,const char ** leafclsp)2595254Sgavinm ao_ms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
2605254Sgavinm     const char **cpuclsp, const char **leafclsp)
2611414Scindi {
2625254Sgavinm 	const ao_error_disp_t *aed = mscookie;
2631414Scindi 
2645254Sgavinm 	if (aed != NULL) {
2655254Sgavinm 		*cpuclsp = FM_EREPORT_CPU_AMD;
2665254Sgavinm 		*leafclsp = aed->aed_class;
2675254Sgavinm 	}
2685254Sgavinm }
2692869Sgavinm 
2705254Sgavinm static int
ao_chip_once(ao_ms_data_t * ao,enum ao_cfgonce_bitnum what)2715254Sgavinm ao_chip_once(ao_ms_data_t *ao, enum ao_cfgonce_bitnum what)
2725254Sgavinm {
2735254Sgavinm 	return (atomic_set_long_excl(&ao->ao_ms_shared->aos_cfgonce,
2745254Sgavinm 	    what) == 0 ?  B_TRUE : B_FALSE);
2751414Scindi }
2761414Scindi 
2771414Scindi /*
2782869Sgavinm  * This knob exists in case any platform has a problem with our default
2792869Sgavinm  * policy of disabling any interrupt registered in the NB MC4_MISC
2802869Sgavinm  * register.  Setting this may cause Solaris and external entities
2812869Sgavinm  * who also have an interest in this register to argue over available
2822869Sgavinm  * telemetry (so setting it is generally not recommended).
2831414Scindi  */
2842869Sgavinm int ao_nb_cfg_mc4misc_noseize = 0;
2851414Scindi 
2861414Scindi /*
2872869Sgavinm  * The BIOS may have setup to receive SMI on counter overflow.  It may also
2882869Sgavinm  * have locked various fields or made them read-only.  We will clear any
2892869Sgavinm  * SMI request and leave the register locked.  We will also clear the
2902869Sgavinm  * counter and enable counting - while we don't use the counter it is nice
2912869Sgavinm  * to have it enabled for verification and debug work.
2921414Scindi  */
2932869Sgavinm static void
nb_mcamisc_init(cmi_hdl_t hdl,ao_ms_data_t * ao,uint32_t rev)2945254Sgavinm nb_mcamisc_init(cmi_hdl_t hdl, ao_ms_data_t *ao, uint32_t rev)
2952869Sgavinm {
2965254Sgavinm 	uint64_t val, nval;
2972869Sgavinm 
2985254Sgavinm 	if (!X86_CHIPREV_MATCH(rev, AO_F_REVS_FG))
2992869Sgavinm 		return;
3002869Sgavinm 
3015254Sgavinm 	if (cmi_hdl_rdmsr(hdl, AMD_MSR_NB_MISC, &val) != CMI_SUCCESS)
3025254Sgavinm 		return;
3035254Sgavinm 
3045254Sgavinm 	ao->ao_ms_shared->aos_bcfg_nb_misc = val;
3052869Sgavinm 
3062869Sgavinm 	if (ao_nb_cfg_mc4misc_noseize)
3072869Sgavinm 		return;		/* stash BIOS value, but no changes */
3082869Sgavinm 
3092869Sgavinm 
3102869Sgavinm 	/*
3112869Sgavinm 	 * The Valid bit tells us whether the CtrP bit is defined; if it
3122869Sgavinm 	 * is the CtrP bit tells us whether an ErrCount field is present.
3132869Sgavinm 	 * If not then there is nothing for us to do.
3142869Sgavinm 	 */
3152869Sgavinm 	if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP))
3162869Sgavinm 		return;
3172869Sgavinm 
3185254Sgavinm 
3195254Sgavinm 	nval = val;
3205254Sgavinm 	nval |= AMD_NB_MISC_CNTEN;		/* enable ECC error counting */
3215254Sgavinm 	nval &= ~AMD_NB_MISC_ERRCOUNT_MASK;	/* clear ErrCount */
3225254Sgavinm 	nval &= ~AMD_NB_MISC_OVRFLW;		/* clear Ovrflw */
3235254Sgavinm 	nval &= ~AMD_NB_MISC_INTTYPE_MASK;	/* no interrupt on overflow */
3245254Sgavinm 	nval |= AMD_NB_MISC_LOCKED;
3252869Sgavinm 
3265254Sgavinm 	if (nval != val) {
3275254Sgavinm 		uint64_t locked = val & AMD_NB_MISC_LOCKED;
3285254Sgavinm 
3295254Sgavinm 		if (locked)
3305254Sgavinm 			ao_bankstatus_prewrite(hdl, ao);
3312869Sgavinm 
3325254Sgavinm 		(void) cmi_hdl_wrmsr(hdl, AMD_MSR_NB_MISC, nval);
3332869Sgavinm 
3345254Sgavinm 		if (locked)
3355254Sgavinm 			ao_bankstatus_postwrite(hdl, ao);
3365254Sgavinm 	}
3372869Sgavinm }
3382869Sgavinm 
3392869Sgavinm /*
3403766Sgavinm  * NorthBridge (NB) MCA Configuration.
3412869Sgavinm  *
3422869Sgavinm  * We add and remove bits from the BIOS-configured value, rather than
3432869Sgavinm  * writing an absolute value.  The variables ao_nb_cfg_{add,remove}_cmn and
3442869Sgavinm  * ap_nb_cfg_{add,remove}_revFG are available for modification via kmdb
3452869Sgavinm  * and /etc/system.  The revision-specific adds and removes are applied
3462869Sgavinm  * after the common changes, and one write is made to the config register.
3472869Sgavinm  * These are not intended for watchdog configuration via these variables -
3482869Sgavinm  * use the watchdog policy below.
3492869Sgavinm  */
3502869Sgavinm 
3512869Sgavinm /*
3522869Sgavinm  * Bits to be added to the NB configuration register - all revs.
3532869Sgavinm  */
3542869Sgavinm uint32_t ao_nb_cfg_add_cmn = AMD_NB_CFG_ADD_CMN;
3552869Sgavinm 
3562869Sgavinm /*
3572869Sgavinm  * Bits to be cleared from the NB configuration register - all revs.
3582869Sgavinm  */
3592869Sgavinm uint32_t ao_nb_cfg_remove_cmn = AMD_NB_CFG_REMOVE_CMN;
3602869Sgavinm 
3612869Sgavinm /*
3622869Sgavinm  * Bits to be added to the NB configuration register - revs F and G.
3632869Sgavinm  */
3642869Sgavinm uint32_t ao_nb_cfg_add_revFG = AMD_NB_CFG_ADD_REV_FG;
3652869Sgavinm 
3662869Sgavinm /*
3672869Sgavinm  * Bits to be cleared from the NB configuration register - revs F and G.
3682869Sgavinm  */
3692869Sgavinm uint32_t ao_nb_cfg_remove_revFG = AMD_NB_CFG_REMOVE_REV_FG;
3702869Sgavinm 
3712869Sgavinm struct ao_nb_cfg {
3722869Sgavinm 	uint32_t cfg_revmask;
3732869Sgavinm 	uint32_t *cfg_add_p;
3742869Sgavinm 	uint32_t *cfg_remove_p;
3752869Sgavinm };
3762869Sgavinm 
3772869Sgavinm static const struct ao_nb_cfg ao_cfg_extra[] = {
3785254Sgavinm 	{ AO_F_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG },
3792869Sgavinm 	{ X86_CHIPREV_UNKNOWN, NULL, NULL }
3802869Sgavinm };
3811414Scindi 
3821414Scindi /*
3831414Scindi  * Bits to be used if we configure the NorthBridge (NB) Watchdog.  The watchdog
3841414Scindi  * triggers a machine check exception when no response to an NB system access
3852869Sgavinm  * occurs within a specified time interval.
3861414Scindi  */
3871414Scindi uint32_t ao_nb_cfg_wdog =
3881414Scindi     AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
3891414Scindi     AMD_NB_CFG_WDOGTMRBASESEL_1MS;
3901414Scindi 
3912869Sgavinm /*
3922869Sgavinm  * The default watchdog policy is to enable it (at the above rate) if it
3932869Sgavinm  * is disabled;  if it is enabled then we leave it enabled at the rate
3942869Sgavinm  * chosen by the BIOS.
3952869Sgavinm  */
3962869Sgavinm enum {
3972869Sgavinm 	AO_NB_WDOG_LEAVEALONE,		/* Don't touch watchdog config */
3982869Sgavinm 	AO_NB_WDOG_DISABLE,		/* Always disable watchdog */
3992869Sgavinm 	AO_NB_WDOG_ENABLE_IF_DISABLED,	/* If disabled, enable at our rate */
4002869Sgavinm 	AO_NB_WDOG_ENABLE_FORCE_RATE	/* Enable and set our rate */
4012869Sgavinm } ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED;
4022869Sgavinm 
4031414Scindi static void
ao_nb_cfg(ao_ms_data_t * ao,uint32_t rev)4045254Sgavinm ao_nb_cfg(ao_ms_data_t *ao, uint32_t rev)
4051414Scindi {
4062869Sgavinm 	const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0];
40710947SSrihari.Venkatesan@Sun.COM 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
4081414Scindi 	uint32_t val;
4091414Scindi 
4101414Scindi 	/*
4111414Scindi 	 * Read the NorthBridge (NB) configuration register in PCI space,
4121414Scindi 	 * modify the settings accordingly, and store the new value back.
4135254Sgavinm 	 * Note that the stashed BIOS config value aos_bcfg_nb_cfg is used
4145254Sgavinm 	 * in ereport payload population to determine ECC syndrome type for
4155254Sgavinm 	 * memory errors.
4161414Scindi 	 */
4175254Sgavinm 	ao->ao_ms_shared->aos_bcfg_nb_cfg = val =
41810947SSrihari.Venkatesan@Sun.COM 	    ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
4191414Scindi 
4202869Sgavinm 	switch (ao_nb_watchdog_policy) {
4212869Sgavinm 	case AO_NB_WDOG_LEAVEALONE:
4222869Sgavinm 		break;
4232869Sgavinm 
4242869Sgavinm 	case AO_NB_WDOG_DISABLE:
4252869Sgavinm 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
4262869Sgavinm 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
4272869Sgavinm 		val |= AMD_NB_CFG_WDOGTMRDIS;
4282869Sgavinm 		break;
4292869Sgavinm 
4302869Sgavinm 	default:
4312869Sgavinm 		cmn_err(CE_NOTE, "ao_nb_watchdog_policy=%d unrecognised, "
4322869Sgavinm 		    "using default policy", ao_nb_watchdog_policy);
4332869Sgavinm 		/*FALLTHRU*/
4342869Sgavinm 
4352869Sgavinm 	case AO_NB_WDOG_ENABLE_IF_DISABLED:
4365327Sgavinm 		if (!(val & AMD_NB_CFG_WDOGTMRDIS))
4372869Sgavinm 			break;	/* if enabled leave rate intact */
4382869Sgavinm 		/*FALLTHRU*/
4392869Sgavinm 
4402869Sgavinm 	case AO_NB_WDOG_ENABLE_FORCE_RATE:
4411414Scindi 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
4421414Scindi 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
4431414Scindi 		val &= ~AMD_NB_CFG_WDOGTMRDIS;
4441414Scindi 		val |= ao_nb_cfg_wdog;
4452869Sgavinm 		break;
4462869Sgavinm 	}
4472869Sgavinm 
4482869Sgavinm 	/*
4492869Sgavinm 	 * Now apply bit adds and removes, first those common to all revs
4502869Sgavinm 	 * and then the revision-specific ones.
4512869Sgavinm 	 */
4522869Sgavinm 	val &= ~ao_nb_cfg_remove_cmn;
4532869Sgavinm 	val |= ao_nb_cfg_add_cmn;
4542869Sgavinm 
4552869Sgavinm 	while (nbcp->cfg_revmask != X86_CHIPREV_UNKNOWN) {
4562869Sgavinm 		if (X86_CHIPREV_MATCH(rev, nbcp->cfg_revmask)) {
4572869Sgavinm 			val &= ~(*nbcp->cfg_remove_p);
4582869Sgavinm 			val |= *nbcp->cfg_add_p;
4592869Sgavinm 		}
4602869Sgavinm 		nbcp++;
4611414Scindi 	}
4621414Scindi 
46310947SSrihari.Venkatesan@Sun.COM 	ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG, val);
4643766Sgavinm }
4653766Sgavinm 
4663766Sgavinm static void
ao_dram_cfg(ao_ms_data_t * ao,uint32_t rev)4675254Sgavinm ao_dram_cfg(ao_ms_data_t *ao, uint32_t rev)
4683766Sgavinm {
46910947SSrihari.Venkatesan@Sun.COM 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
4703766Sgavinm 	union mcreg_dramcfg_lo dcfglo;
4713766Sgavinm 
4725254Sgavinm 	ao->ao_ms_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) =
47310947SSrihari.Venkatesan@Sun.COM 	    ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO);
4745254Sgavinm 	ao->ao_ms_shared->aos_bcfg_dcfg_hi =
47510947SSrihari.Venkatesan@Sun.COM 	    ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGHI);
4763766Sgavinm #ifdef OPTERON_ERRATUM_172
4775254Sgavinm 	if (X86_CHIPREV_MATCH(rev, AO_F_REVS_FG) &&
4785254Sgavinm 	    MCREG_FIELD_F_revFG(&dcfglo, ParEn)) {
4795254Sgavinm 		MCREG_FIELD_F_revFG(&dcfglo, ParEn) = 0;
48010947SSrihari.Venkatesan@Sun.COM 		ao_pcicfg_write(procnodeid, MC_FUNC_DRAMCTL,
48110947SSrihari.Venkatesan@Sun.COM 		    MC_DC_REG_DRAMCFGLO, MCREG_VAL32(&dcfglo));
4823766Sgavinm 	}
4833766Sgavinm #endif
4842869Sgavinm }
4852869Sgavinm 
4862869Sgavinm /*
4872869Sgavinm  * This knob exists in case any platform has a problem with our default
4882869Sgavinm  * policy of disabling any interrupt registered in the online spare
4892869Sgavinm  * control register.  Setting this may cause Solaris and external entities
4902869Sgavinm  * who also have an interest in this register to argue over available
4912869Sgavinm  * telemetry (so setting it is generally not recommended).
4922869Sgavinm  */
4932869Sgavinm int ao_nb_cfg_sparectl_noseize = 0;
4942869Sgavinm 
4952869Sgavinm /*
4962869Sgavinm  * Setup the online spare control register (revs F and G).  We disable
4972869Sgavinm  * any interrupt registered by the BIOS and zero all error counts.
4982869Sgavinm  */
4992869Sgavinm static void
ao_sparectl_cfg(ao_ms_data_t * ao)5005254Sgavinm ao_sparectl_cfg(ao_ms_data_t *ao)
5012869Sgavinm {
50210947SSrihari.Venkatesan@Sun.COM 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
5032869Sgavinm 	union mcreg_sparectl sparectl;
5042869Sgavinm 	int chan, cs;
5052869Sgavinm 
5065254Sgavinm 	ao->ao_ms_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) =
50710947SSrihari.Venkatesan@Sun.COM 	    ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
5081414Scindi 
5092869Sgavinm 	if (ao_nb_cfg_sparectl_noseize)
5102869Sgavinm 		return;	/* stash BIOS value, but no changes */
5112869Sgavinm 
5122869Sgavinm 	/*
5132869Sgavinm 	 * If the BIOS has requested SMI interrupt type for ECC count
5142869Sgavinm 	 * overflow for a chip-select or channel force those off.
5152869Sgavinm 	 */
5165254Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0;
5175254Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0;
5182869Sgavinm 
5192869Sgavinm 	/*
5202869Sgavinm 	 * Zero EccErrCnt and write this back to all chan/cs combinations.
5212869Sgavinm 	 */
5225254Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1;
5235254Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, EccErrCnt) = 0;
5242869Sgavinm 	for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) {
5255254Sgavinm 		MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = chan;
5262869Sgavinm 
5272869Sgavinm 		for (cs = 0; cs < MC_CHIP_NCS; cs++) {
5285254Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramCs) = cs;
52910947SSrihari.Venkatesan@Sun.COM 			ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
5303766Sgavinm 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
5312869Sgavinm 		}
5322869Sgavinm 	}
5331414Scindi }
5341414Scindi 
5355254Sgavinm int ao_forgive_uc = 0;		/* For test/debug only */
5365254Sgavinm int ao_forgive_pcc = 0;		/* For test/debug only */
5375254Sgavinm int ao_fake_poison = 0;		/* For test/debug only */
5381414Scindi 
5395254Sgavinm 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)5405254Sgavinm ao_ms_error_action(cmi_hdl_t hdl, int ismc, int banknum,
5415254Sgavinm     uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
5425254Sgavinm {
5435254Sgavinm 	const ao_error_disp_t *aed;
5445254Sgavinm 	uint32_t retval = 0;
5455254Sgavinm 	uint8_t when;
5465254Sgavinm 	int en;
5472869Sgavinm 
5485254Sgavinm 	if (ao_forgive_uc)
5495254Sgavinm 		retval |= CMS_ERRSCOPE_CLEARED_UC;
5501414Scindi 
5515254Sgavinm 	if (ao_forgive_pcc)
5525254Sgavinm 		retval |= CMS_ERRSCOPE_CURCONTEXT_OK;
5531414Scindi 
5545254Sgavinm 	if (ao_fake_poison && status & MSR_MC_STATUS_UC)
5555254Sgavinm 		retval |= CMS_ERRSCOPE_POISONED;
5561414Scindi 
5575254Sgavinm 	if (retval)
5585254Sgavinm 		return (retval);
5591414Scindi 
560*12437SAdrian.Frost@Sun.COM 	aed = ao_ms_disp_match(hdl, ismc, banknum, status, addr, misc,
561*12437SAdrian.Frost@Sun.COM 	    mslogout);
5621414Scindi 
5631414Scindi 	/*
5645254Sgavinm 	 * If we do not recognise the error let the cpu module apply
5655254Sgavinm 	 * the generic criteria to decide how to react.
5661414Scindi 	 */
5675254Sgavinm 	if (aed == NULL)
5685254Sgavinm 		return (0);
5691414Scindi 
5705254Sgavinm 	en = (status & MSR_MC_STATUS_EN) != 0;
5711414Scindi 
5725254Sgavinm 	if ((when = aed->aed_panic_when) == AO_AED_PANIC_NEVER)
5735254Sgavinm 		retval |= CMS_ERRSCOPE_IGNORE_ERR;
5745254Sgavinm 
5755254Sgavinm 	if ((when & AO_AED_PANIC_ALWAYS) ||
5765254Sgavinm 	    ((when & AO_AED_PANIC_IFMCE) && (en || ismc)))
5775254Sgavinm 		retval |= CMS_ERRSCOPE_FORCE_FATAL;
5782869Sgavinm 
5795254Sgavinm 	/*
5805254Sgavinm 	 * The original AMD implementation would panic on a machine check
5815254Sgavinm 	 * (not a poll) if the status overflow bit was set, with an
5825254Sgavinm 	 * exception for the case of rev F or later with an NB error
5835254Sgavinm 	 * indicating CECC.  This came from the perception that the
5845254Sgavinm 	 * overflow bit was not correctly managed on rev E and earlier, for
5855254Sgavinm 	 * example that repeated correctable memeory errors did not set
5865254Sgavinm 	 * OVER but somehow clear CECC.
5875254Sgavinm 	 *
5885254Sgavinm 	 * We will leave the generic support to evaluate overflow errors
5895254Sgavinm 	 * and decide to panic on their individual merits, e.g., if PCC
5905254Sgavinm 	 * is set and so on.  The AMD docs do say (as Intel does) that
5915254Sgavinm 	 * the status information is *all* from the higher-priority
5925254Sgavinm 	 * error in the case of an overflow, so it is at least as serious
5935254Sgavinm 	 * as the original and we can decide panic etc based on it.
5945254Sgavinm 	 */
5951414Scindi 
5965254Sgavinm 	return (retval);
5971414Scindi }
5981414Scindi 
5995254Sgavinm /*
6005254Sgavinm  * Will need to change for family 0x10
6015254Sgavinm  */
6021414Scindi static uint_t
ao_ereport_synd(ao_ms_data_t * ao,uint64_t status,uint_t * typep,int is_nb)6035254Sgavinm ao_ereport_synd(ao_ms_data_t *ao, uint64_t status, uint_t *typep,
6042869Sgavinm     int is_nb)
6051414Scindi {
6061414Scindi 	if (is_nb) {
6075254Sgavinm 		if (ao->ao_ms_shared->aos_bcfg_nb_cfg &
6085254Sgavinm 		    AMD_NB_CFG_CHIPKILLECCEN) {
6091414Scindi 			*typep = AMD_SYNDTYPE_CHIPKILL;
6105254Sgavinm 			return (AMD_NB_STAT_CKSYND(status));
6111414Scindi 		} else {
6121414Scindi 			*typep = AMD_SYNDTYPE_ECC;
6135254Sgavinm 			return (AMD_BANK_SYND(status));
6141414Scindi 		}
6151414Scindi 	} else {
6161414Scindi 		*typep = AMD_SYNDTYPE_ECC;
6175254Sgavinm 		return (AMD_BANK_SYND(status));
6181414Scindi 	}
6191414Scindi }
6201414Scindi 
6215254Sgavinm static nvlist_t *
ao_ereport_create_resource_elem(cmi_hdl_t hdl,nv_alloc_t * nva,mc_unum_t * unump,int dimmnum)62210942STom.Pothier@Sun.COM ao_ereport_create_resource_elem(cmi_hdl_t hdl, nv_alloc_t *nva,
62310942STom.Pothier@Sun.COM     mc_unum_t *unump, int dimmnum)
6241414Scindi {
6255254Sgavinm 	nvlist_t *nvl, *snvl;
62610942STom.Pothier@Sun.COM 	nvlist_t *board_list = NULL;
6275254Sgavinm 
6285254Sgavinm 	if ((nvl = fm_nvlist_create(nva)) == NULL)	/* freed by caller */
6295254Sgavinm 		return (NULL);
6301414Scindi 
6315254Sgavinm 	if ((snvl = fm_nvlist_create(nva)) == NULL) {
6325254Sgavinm 		fm_nvlist_destroy(nvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
6335254Sgavinm 		return (NULL);
6345254Sgavinm 	}
6351414Scindi 
6361414Scindi 	(void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
6371414Scindi 	    unump->unum_offset);
6381414Scindi 
63910942STom.Pothier@Sun.COM 	if (!x86gentopo_legacy) {
64010942STom.Pothier@Sun.COM 		board_list = cmi_hdl_smb_bboard(hdl);
64110942STom.Pothier@Sun.COM 
64210942STom.Pothier@Sun.COM 		if (board_list == NULL) {
64310942STom.Pothier@Sun.COM 			fm_nvlist_destroy(nvl,
64410942STom.Pothier@Sun.COM 			    nva ? FM_NVA_RETAIN : FM_NVA_FREE);
64510942STom.Pothier@Sun.COM 			fm_nvlist_destroy(snvl,
64610942STom.Pothier@Sun.COM 			    nva ? FM_NVA_RETAIN : FM_NVA_FREE);
64710942STom.Pothier@Sun.COM 			return (NULL);
64810942STom.Pothier@Sun.COM 		}
64910942STom.Pothier@Sun.COM 
65010942STom.Pothier@Sun.COM 		fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, snvl,
65110942STom.Pothier@Sun.COM 		    board_list, 4,
65210942STom.Pothier@Sun.COM 		    "chip", cmi_hdl_smb_chipid(hdl),
65310942STom.Pothier@Sun.COM 		    "memory-controller", unump->unum_mc,
65410942STom.Pothier@Sun.COM 		    "dimm", unump->unum_dimms[dimmnum],
65510942STom.Pothier@Sun.COM 		    "rank", unump->unum_rank);
65610942STom.Pothier@Sun.COM 	} else {
65710942STom.Pothier@Sun.COM 		fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, snvl, 5,
65810942STom.Pothier@Sun.COM 		    "motherboard", unump->unum_board,
65910942STom.Pothier@Sun.COM 		    "chip", unump->unum_chip,
66010942STom.Pothier@Sun.COM 		    "memory-controller", unump->unum_mc,
66110942STom.Pothier@Sun.COM 		    "dimm", unump->unum_dimms[dimmnum],
66210942STom.Pothier@Sun.COM 		    "rank", unump->unum_rank);
66310942STom.Pothier@Sun.COM 	}
6641414Scindi 
6651784Sgavinm 	fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
6665254Sgavinm 
6675254Sgavinm 	return (nvl);
6681414Scindi }
6691414Scindi 
6701414Scindi static void
ao_ereport_add_resource(cmi_hdl_t hdl,nvlist_t * payload,nv_alloc_t * nva,mc_unum_t * unump)67110942STom.Pothier@Sun.COM ao_ereport_add_resource(cmi_hdl_t hdl, nvlist_t *payload, nv_alloc_t *nva,
67210942STom.Pothier@Sun.COM     mc_unum_t *unump)
6731414Scindi {
6741414Scindi 
6751414Scindi 	nvlist_t *elems[MC_UNUM_NDIMM];
6761414Scindi 	int nelems = 0;
6771414Scindi 	int i;
6781414Scindi 
6791414Scindi 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
6802869Sgavinm 		if (unump->unum_dimms[i] == MC_INVALNUM)
6811414Scindi 			break;
6825254Sgavinm 
68310942STom.Pothier@Sun.COM 		if ((elems[nelems] = ao_ereport_create_resource_elem(hdl, nva,
6845254Sgavinm 		    unump, i)) == NULL)
6855254Sgavinm 			break;
6865254Sgavinm 
6875254Sgavinm 		nelems++;
6881414Scindi 	}
6891414Scindi 
6905254Sgavinm 	if (nelems == 0)
6915254Sgavinm 		return;
6925254Sgavinm 
6931414Scindi 	fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
6941625Sgavinm 	    DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
6951414Scindi 
6961414Scindi 	for (i = 0; i < nelems; i++)
6971784Sgavinm 		fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
6981414Scindi }
6991414Scindi 
7005254Sgavinm /*ARGSUSED*/
7015254Sgavinm 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)7025254Sgavinm ao_ms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport,
7035254Sgavinm     nv_alloc_t *nva, int banknum, uint64_t status, uint64_t addr,
7045254Sgavinm     uint64_t misc, void *mslogout, cms_cookie_t mscookie)
7051414Scindi {
7065254Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
7075254Sgavinm 	const ao_error_disp_t *aed = mscookie;
7081414Scindi 	uint_t synd, syndtype;
7095254Sgavinm 	uint64_t members;
7101414Scindi 
7115254Sgavinm 	if (aed == NULL)
7125254Sgavinm 		return;
7131414Scindi 
7145254Sgavinm 	members = aed->aed_ereport_members;
7151414Scindi 
7165254Sgavinm 	synd = ao_ereport_synd(ao, status, &syndtype,
7175254Sgavinm 	    banknum == AMD_MCA_BANK_NB);
7182869Sgavinm 
7191414Scindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
7205254Sgavinm 		fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND,
7211414Scindi 		    DATA_TYPE_UINT16, synd, NULL);
7221414Scindi 	}
7231414Scindi 
7241414Scindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) {
7255254Sgavinm 		fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE,
7261414Scindi 		    DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ?
72711947Ssrihari.venkatesan@oracle.com 		    "C4" : "E"), NULL);
7281414Scindi 	}
7291414Scindi 
7301414Scindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) {
7311414Scindi 		mc_unum_t unum;
7321414Scindi 
7335254Sgavinm 		if (((aed->aed_flags & AO_AED_FLAGS_ADDRTYPE) ==
7345254Sgavinm 		    AO_AED_F_PHYSICAL) && (status & MSR_MC_STATUS_ADDRV) &&
7355254Sgavinm 		    cmi_mc_patounum(addr, aed->aed_addrvalid_hi,
7365254Sgavinm 		    aed->aed_addrvalid_lo, synd, syndtype, &unum) ==
7375254Sgavinm 		    CMI_SUCCESS)
73810942STom.Pothier@Sun.COM 			ao_ereport_add_resource(hdl, ereport, nva, &unum);
7391414Scindi 	}
7401414Scindi }
7411414Scindi 
7421414Scindi /*ARGSUSED*/
7435254Sgavinm boolean_t
ao_ms_ereport_includestack(cmi_hdl_t hdl,cms_cookie_t mscookie)7445254Sgavinm ao_ms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
7451414Scindi {
7465254Sgavinm 	const ao_error_disp_t *aed = mscookie;
7471414Scindi 
7485254Sgavinm 	if (aed == NULL)
7495254Sgavinm 		return (0);
7501414Scindi 
7515254Sgavinm 	return ((aed->aed_ereport_members &
7525254Sgavinm 	    FM_EREPORT_PAYLOAD_FLAG_STACK) != 0);
7531414Scindi }
7541414Scindi 
7555254Sgavinm cms_errno_t
ao_ms_msrinject(cmi_hdl_t hdl,uint_t msr,uint64_t val)7565254Sgavinm ao_ms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
7571414Scindi {
7585254Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
7595254Sgavinm 	cms_errno_t rv = CMSERR_BADMSRWRITE;
7602869Sgavinm 
7615254Sgavinm 	ao_bankstatus_prewrite(hdl, ao);
7625254Sgavinm 	if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
7635254Sgavinm 		rv = CMS_SUCCESS;
7645254Sgavinm 	ao_bankstatus_postwrite(hdl, ao);
7652869Sgavinm 
7662869Sgavinm 	return (rv);
7671414Scindi }
7681414Scindi 
7691414Scindi /*ARGSUSED*/
7705254Sgavinm uint64_t
ao_ms_mcgctl_val(cmi_hdl_t hdl,int nbanks,uint64_t def)7715254Sgavinm ao_ms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
7721414Scindi {
7735254Sgavinm 	return ((1ULL << nbanks) - 1);
7745254Sgavinm }
7755254Sgavinm 
7765254Sgavinm boolean_t
ao_ms_bankctl_skipinit(cmi_hdl_t hdl,int banknum)7775254Sgavinm ao_ms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
7785254Sgavinm {
7795254Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
7805254Sgavinm 
7815254Sgavinm 	if (banknum != AMD_MCA_BANK_NB)
7825254Sgavinm 		return (B_FALSE);
7835254Sgavinm 
7845639Sgavinm 	/*
7855639Sgavinm 	 * If we are the first to atomically set the "I'll do it" bit
7865639Sgavinm 	 * then return B_FALSE (do not skip), otherwise skip with B_TRUE.
7875639Sgavinm 	 */
7885639Sgavinm 	return (ao_chip_once(ao, AO_CFGONCE_NBMCA) == B_TRUE ?
7895639Sgavinm 	    B_FALSE : B_TRUE);
7905254Sgavinm }
7911414Scindi 
7925254Sgavinm uint64_t
ao_ms_bankctl_val(cmi_hdl_t hdl,int banknum,uint64_t def)7935254Sgavinm ao_ms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
7945254Sgavinm {
7955254Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
7965254Sgavinm 	const struct ao_ctl_init *extrap;
7975254Sgavinm 	const ao_bank_cfg_t *bankcfg;
7985254Sgavinm 	uint64_t mcictl;
7995254Sgavinm 	uint32_t rev = ao->ao_ms_shared->aos_chiprev;
8005254Sgavinm 
8015254Sgavinm 	if (banknum >= sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]))
8025254Sgavinm 		return (def);
8031414Scindi 
8045254Sgavinm 	bankcfg = &ao_bank_cfgs[banknum];
8055254Sgavinm 	extrap = bankcfg->bank_ctl_init_extra;
8065254Sgavinm 
8075254Sgavinm 	mcictl = bankcfg->bank_ctl_init_cmn;
8081414Scindi 
8095254Sgavinm 	while (extrap != NULL && extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) {
8105254Sgavinm 		if (X86_CHIPREV_MATCH(rev, extrap->ctl_revmask))
8115254Sgavinm 			mcictl |= extrap->ctl_bits;
8125254Sgavinm 		extrap++;
8135254Sgavinm 	}
8145254Sgavinm 
8155254Sgavinm 	return (mcictl);
8161414Scindi }
8171414Scindi 
8187532SSean.Ye@Sun.COM /*ARGSUSED*/
8191414Scindi void
ao_bankstatus_prewrite(cmi_hdl_t hdl,ao_ms_data_t * ao)8205254Sgavinm ao_bankstatus_prewrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
8211414Scindi {
8227532SSean.Ye@Sun.COM #ifndef __xpv
8235254Sgavinm 	uint64_t hwcr;
8245254Sgavinm 
8255254Sgavinm 	if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
8265254Sgavinm 		return;
8275254Sgavinm 
8285254Sgavinm 	ao->ao_ms_hwcr_val = hwcr;
8295254Sgavinm 
8305254Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
8315254Sgavinm 		hwcr |= AMD_HWCR_MCI_STATUS_WREN;
8325254Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
8335254Sgavinm 	}
8347532SSean.Ye@Sun.COM #endif
8355254Sgavinm }
8365254Sgavinm 
8377532SSean.Ye@Sun.COM /*ARGSUSED*/
8385254Sgavinm void
ao_bankstatus_postwrite(cmi_hdl_t hdl,ao_ms_data_t * ao)8395254Sgavinm ao_bankstatus_postwrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
8405254Sgavinm {
8417532SSean.Ye@Sun.COM #ifndef __xpv
8425254Sgavinm 	uint64_t hwcr = ao->ao_ms_hwcr_val;
8435254Sgavinm 
8445254Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
8455254Sgavinm 		hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
8465254Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
8475254Sgavinm 	}
8487532SSean.Ye@Sun.COM #endif
8495254Sgavinm }
8505254Sgavinm 
8515254Sgavinm void
ao_ms_mca_init(cmi_hdl_t hdl,int nbanks)8525254Sgavinm ao_ms_mca_init(cmi_hdl_t hdl, int nbanks)
8535254Sgavinm {
8545254Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
8555254Sgavinm 	uint32_t rev = ao->ao_ms_shared->aos_chiprev;
8565254Sgavinm 	ao_ms_mca_t *mca = &ao->ao_ms_mca;
8575254Sgavinm 	uint64_t *maskp;
8581414Scindi 	int i;
8591414Scindi 
8605254Sgavinm 	maskp = mca->ao_mca_bios_cfg.bcfg_bank_mask = kmem_zalloc(nbanks *
8615254Sgavinm 	    sizeof (uint64_t), KM_SLEEP);
8621414Scindi 
8632869Sgavinm 	/*
8645254Sgavinm 	 * Read the bank ctl mask MSRs, but only as many as we know
8655254Sgavinm 	 * certainly exist - don't calculate the register address.
8665254Sgavinm 	 * Also initialize the MCi_MISC register where required.
8672869Sgavinm 	 */
8685254Sgavinm 	for (i = 0; i < MIN(nbanks, ao_nbanks); i++) {
8695254Sgavinm 		(void) cmi_hdl_rdmsr(hdl, ao_bank_cfgs[i].bank_ctl_mask,
8705254Sgavinm 		    maskp++);
8715254Sgavinm 		if (ao_bank_cfgs[i].bank_misc_initfunc != NULL)
8725254Sgavinm 			ao_bank_cfgs[i].bank_misc_initfunc(hdl, ao, rev);
8732869Sgavinm 
8742869Sgavinm 	}
8752869Sgavinm 
8765254Sgavinm 	if (ao_chip_once(ao, AO_CFGONCE_NBCFG) == B_TRUE) {
8775254Sgavinm 		ao_nb_cfg(ao, rev);
8781414Scindi 
8795254Sgavinm 		if (X86_CHIPREV_MATCH(rev, AO_F_REVS_FG))
8805254Sgavinm 			ao_sparectl_cfg(ao);
8812869Sgavinm 	}
8821414Scindi 
8835254Sgavinm 	if (ao_chip_once(ao, AO_CFGONCE_DRAMCFG) == B_TRUE)
8845254Sgavinm 		ao_dram_cfg(ao, rev);
8851414Scindi 
88610947SSrihari.Venkatesan@Sun.COM 	ao_procnode_scrubber_enable(hdl, ao);
8871414Scindi }
8881414Scindi 
8891959Ssethg /*
8901959Ssethg  * Note that although this cpu module is loaded before the PSMs are
8911959Ssethg  * loaded (and hence before acpica is loaded), this function is
8921959Ssethg  * called from post_startup(), after PSMs are initialized and acpica
8931959Ssethg  * is loaded.
8941959Ssethg  */
8951959Ssethg static int
ao_acpi_find_smicmd(int * asd_port)8961959Ssethg ao_acpi_find_smicmd(int *asd_port)
8971959Ssethg {
8987851SDana.Myers@Sun.COM 	ACPI_TABLE_FADT *fadt = NULL;
8991959Ssethg 
9001959Ssethg 	/*
9017851SDana.Myers@Sun.COM 	 * AcpiGetTable works even if ACPI is disabled, so a failure
9021959Ssethg 	 * here means we weren't able to retreive a pointer to the FADT.
9031959Ssethg 	 */
9047851SDana.Myers@Sun.COM 	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
9057851SDana.Myers@Sun.COM 	    AE_OK)
9061959Ssethg 		return (-1);
9071959Ssethg 
9081959Ssethg 	ASSERT(fadt != NULL);
9091959Ssethg 
9107851SDana.Myers@Sun.COM 	*asd_port = fadt->SmiCommand;
9111959Ssethg 	return (0);
9121959Ssethg }
9131959Ssethg 
9141414Scindi /*ARGSUSED*/
9151414Scindi void
ao_ms_post_startup(cmi_hdl_t hdl)9165254Sgavinm ao_ms_post_startup(cmi_hdl_t hdl)
9171414Scindi {
9181414Scindi 	const struct ao_smi_disable *asd;
9191414Scindi 	id_t id;
9201959Ssethg 	int rv = -1, asd_port;
9211414Scindi 
9221414Scindi 	smbios_system_t sy;
9231414Scindi 	smbios_bios_t sb;
9241414Scindi 	smbios_info_t si;
9251414Scindi 
9261414Scindi 	/*
9271414Scindi 	 * Fetch the System and BIOS vendor strings from SMBIOS and see if they
9281414Scindi 	 * match a value in our table.  If so, disable SMI error polling.  This
9291414Scindi 	 * is grotesque and should be replaced by self-describing vendor-
9301414Scindi 	 * specific SMBIOS data or a specification enhancement instead.
9311414Scindi 	 */
9321414Scindi 	if (ao_mca_smi_disable && ksmbios != NULL &&
9331414Scindi 	    smbios_info_bios(ksmbios, &sb) != SMB_ERR &&
9341414Scindi 	    (id = smbios_info_system(ksmbios, &sy)) != SMB_ERR &&
9351414Scindi 	    smbios_info_common(ksmbios, id, &si) != SMB_ERR) {
9361414Scindi 
9371414Scindi 		for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) {
9381414Scindi 			if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer,
9391414Scindi 			    strlen(asd->asd_sys_vendor)) != 0 ||
9403164Sgavinm 			    strncmp(asd->asd_sys_product, si.smbi_product,
9413164Sgavinm 			    strlen(asd->asd_sys_product)) != 0 ||
9421414Scindi 			    strncmp(asd->asd_bios_vendor, sb.smbb_vendor,
9431414Scindi 			    strlen(asd->asd_bios_vendor)) != 0)
9441414Scindi 				continue;
9451414Scindi 
9461959Ssethg 			/*
9471959Ssethg 			 * Look for the SMI_CMD port in the ACPI FADT,
9481959Ssethg 			 * if the port is 0, this platform doesn't support
9491959Ssethg 			 * SMM, so there is no SMI error polling to disable.
9501959Ssethg 			 */
9511959Ssethg 			if ((rv = ao_acpi_find_smicmd(&asd_port)) == 0 &&
9521959Ssethg 			    asd_port != 0) {
9531959Ssethg 				cmn_err(CE_CONT, "?SMI polling disabled in "
9541959Ssethg 				    "favor of Solaris Fault Management for "
9551959Ssethg 				    "AMD Processors\n");
9561414Scindi 
9571964Ssethg 				outb(asd_port, asd->asd_code);
9581959Ssethg 
9591959Ssethg 			} else if (rv < 0) {
9601959Ssethg 				cmn_err(CE_CONT, "?Solaris Fault Management "
9611959Ssethg 				    "for AMD Processors could not disable SMI "
9621959Ssethg 				    "polling because an error occurred while "
9631959Ssethg 				    "trying to determine the SMI command port "
9641959Ssethg 				    "from the ACPI FADT table\n");
9651959Ssethg 			}
9661414Scindi 			break;
9671414Scindi 		}
9681414Scindi 	}
9691414Scindi }
970