17aec1d6eScindi /*
27aec1d6eScindi  * CDDL HEADER START
37aec1d6eScindi  *
47aec1d6eScindi  * The contents of this file are subject to the terms of the
5a307a255Sgavinm  * Common Development and Distribution License (the "License").
6a307a255Sgavinm  * You may not use this file except in compliance with the License.
77aec1d6eScindi  *
87aec1d6eScindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97aec1d6eScindi  * or http://www.opensolaris.org/os/licensing.
107aec1d6eScindi  * See the License for the specific language governing permissions
117aec1d6eScindi  * and limitations under the License.
127aec1d6eScindi  *
137aec1d6eScindi  * When distributing Covered Code, include this CDDL HEADER in each
147aec1d6eScindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157aec1d6eScindi  * If applicable, add the following below this CDDL HEADER, with the
167aec1d6eScindi  * fields enclosed by brackets "[]" replaced with your own identifying
177aec1d6eScindi  * information: Portions Copyright [yyyy] [name of copyright owner]
187aec1d6eScindi  *
197aec1d6eScindi  * CDDL HEADER END
207aec1d6eScindi  */
217aec1d6eScindi 
227aec1d6eScindi /*
23c84b7bbeSAdrian Frost  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24*22e4c3acSKeith M Wesolowski  * Copyright 2022 Oxide Computer Co.
257aec1d6eScindi  */
267aec1d6eScindi 
277aec1d6eScindi #include <sys/types.h>
287aec1d6eScindi #include <sys/regset.h>
297aec1d6eScindi #include <sys/privregs.h>
307aec1d6eScindi #include <sys/pci_impl.h>
317aec1d6eScindi #include <sys/cpuvar.h>
327aec1d6eScindi #include <sys/x86_archext.h>
337aec1d6eScindi #include <sys/cmn_err.h>
347aec1d6eScindi #include <sys/systm.h>
357aec1d6eScindi #include <sys/sysmacros.h>
36fb2f18f8Sesaxe #include <sys/pghw.h>
377aec1d6eScindi #include <sys/cyclic.h>
387aec1d6eScindi #include <sys/sysevent.h>
397aec1d6eScindi #include <sys/smbios.h>
407aec1d6eScindi #include <sys/mca_x86.h>
417aec1d6eScindi #include <sys/mca_amd.h>
427aec1d6eScindi #include <sys/mc.h>
438a40a695Sgavinm #include <sys/mc_amd.h>
447aec1d6eScindi #include <sys/psw.h>
457aec1d6eScindi #include <sys/ddi.h>
467aec1d6eScindi #include <sys/sunddi.h>
477aec1d6eScindi #include <sys/sdt.h>
487aec1d6eScindi #include <sys/fm/util.h>
497aec1d6eScindi #include <sys/fm/protocol.h>
507aec1d6eScindi #include <sys/fm/cpu/AMD.h>
51074bb90dSTom Pothier #include <sys/fm/smb/fmsmb.h>
52bc946ef3Ssethg #include <sys/acpi/acpi.h>
53bc946ef3Ssethg #include <sys/acpi/acpi_pci.h>
54bc946ef3Ssethg #include <sys/acpica.h>
5520c794b3Sgavinm #include <sys/cpu_module.h>
567aec1d6eScindi 
577aec1d6eScindi #include "ao.h"
587aec1d6eScindi #include "ao_mca_disp.h"
597aec1d6eScindi 
60*22e4c3acSKeith M Wesolowski #define	AO_F_REVS_FG (X86_CHIPREV_AMD_LEGACY_F_REV_F | \
61*22e4c3acSKeith M Wesolowski 	X86_CHIPREV_AMD_LEGACY_F_REV_G)
628a40a695Sgavinm 
637aec1d6eScindi int ao_mca_smi_disable = 1;		/* attempt to disable SMI polling */
647aec1d6eScindi 
65074bb90dSTom Pothier extern int x86gentopo_legacy;	/* x86 generic topology support */
66074bb90dSTom Pothier 
678a40a695Sgavinm struct ao_ctl_init {
688a40a695Sgavinm 	uint32_t ctl_revmask;	/* rev(s) to which this applies */
698a40a695Sgavinm 	uint64_t ctl_bits;	/* mca ctl reg bitmask to set */
708a40a695Sgavinm };
718a40a695Sgavinm 
728a40a695Sgavinm /*
738a40a695Sgavinm  * Additional NB MCA ctl initialization for revs F and G
748a40a695Sgavinm  */
758a40a695Sgavinm static const struct ao_ctl_init ao_nb_ctl_init[] = {
7620c794b3Sgavinm 	{ AO_F_REVS_FG, AMD_NB_CTL_INIT_REV_FG },
778a40a695Sgavinm 	{ X86_CHIPREV_UNKNOWN, 0 }
787aec1d6eScindi };
797aec1d6eScindi 
807aec1d6eScindi typedef struct ao_bank_cfg {
818a40a695Sgavinm 	uint64_t bank_ctl_init_cmn;			/* Common init value */
828a40a695Sgavinm 	const struct ao_ctl_init *bank_ctl_init_extra;	/* Extra for each rev */
8320c794b3Sgavinm 	void (*bank_misc_initfunc)(cmi_hdl_t, ao_ms_data_t *, uint32_t);
8420c794b3Sgavinm 	uint_t bank_ctl_mask;
857aec1d6eScindi } ao_bank_cfg_t;
867aec1d6eScindi 
8720c794b3Sgavinm static void nb_mcamisc_init(cmi_hdl_t, ao_ms_data_t *, uint32_t);
888a40a695Sgavinm 
897aec1d6eScindi static const ao_bank_cfg_t ao_bank_cfgs[] = {
9020c794b3Sgavinm 	{ AMD_DC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_DC_MASK },
9120c794b3Sgavinm 	{ AMD_IC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_IC_MASK },
9220c794b3Sgavinm 	{ AMD_BU_CTL_INIT_CMN, NULL, NULL, AMD_MSR_BU_MASK },
9320c794b3Sgavinm 	{ AMD_LS_CTL_INIT_CMN, NULL, NULL, AMD_MSR_LS_MASK },
9420c794b3Sgavinm 	{ AMD_NB_CTL_INIT_CMN, &ao_nb_ctl_init[0], nb_mcamisc_init,
9520c794b3Sgavinm 		AMD_MSR_NB_MASK },
967aec1d6eScindi };
977aec1d6eScindi 
9820c794b3Sgavinm static int ao_nbanks = sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]);
997aec1d6eScindi 
1007aec1d6eScindi /*
1017aec1d6eScindi  * This is quite awful but necessary to work around x86 system vendor's view of
1027aec1d6eScindi  * the world.  Other operating systems (you know who you are) don't understand
1037aec1d6eScindi  * Opteron-specific error handling, so BIOS and system vendors often hide these
1047aec1d6eScindi  * conditions from them by using SMI polling to copy out any errors from the
1057aec1d6eScindi  * machine-check registers.  When Solaris runs on a system with this feature,
1067aec1d6eScindi  * we want to disable the SMI polling so we can use FMA instead.  Sadly, there
1077aec1d6eScindi  * isn't even a standard self-describing way to express the whole situation,
1087aec1d6eScindi  * so we have to resort to hard-coded values.  This should all be changed to
1097aec1d6eScindi  * be a self-describing vendor-specific SMBIOS structure in the future.
1107aec1d6eScindi  */
1117aec1d6eScindi static const struct ao_smi_disable {
1127aec1d6eScindi 	const char *asd_sys_vendor;	/* SMB_TYPE_SYSTEM vendor prefix */
1134156fc34Sgavinm 	const char *asd_sys_product;	/* SMB_TYPE_SYSTEM product prefix */
1147aec1d6eScindi 	const char *asd_bios_vendor;	/* SMB_TYPE_BIOS vendor prefix */
115a6d65bf4Ssethg 	uint8_t asd_code;		/* output code for SMI disable */
1167aec1d6eScindi } ao_smi_disable[] = {
1174156fc34Sgavinm 	{ "Sun Microsystems", "Galaxy12",
1184156fc34Sgavinm 	    "American Megatrends", 0x59 },
1194156fc34Sgavinm 	{ "Sun Microsystems", "Sun Fire X4100 Server",
1204156fc34Sgavinm 	    "American Megatrends", 0x59 },
1214156fc34Sgavinm 	{ "Sun Microsystems", "Sun Fire X4200 Server",
1224156fc34Sgavinm 	    "American Megatrends", 0x59 },
1234156fc34Sgavinm 	{ NULL, NULL, NULL, 0 }
1247aec1d6eScindi };
1257aec1d6eScindi 
1267aec1d6eScindi static int
ao_disp_match_r4(uint16_t ref,uint8_t r4)1277aec1d6eScindi ao_disp_match_r4(uint16_t ref, uint8_t r4)
1287aec1d6eScindi {
1297aec1d6eScindi 	static const uint16_t ao_r4_map[] = {
13020c794b3Sgavinm 		AO_MCA_R4_BIT_ERR,	/* MCAX86_ERRCODE_RRRR_ERR */
13120c794b3Sgavinm 		AO_MCA_R4_BIT_RD,	/* MCAX86_ERRCODE_RRRR_RD */
13220c794b3Sgavinm 		AO_MCA_R4_BIT_WR,	/* MCAX86_ERRCODE_RRRR_WR */
13320c794b3Sgavinm 		AO_MCA_R4_BIT_DRD,	/* MCAX86_ERRCODE_RRRR_DRD */
13420c794b3Sgavinm 		AO_MCA_R4_BIT_DWR,	/* MCAX86_ERRCODE_RRRR_DWR */
13520c794b3Sgavinm 		AO_MCA_R4_BIT_IRD,	/* MCAX86_ERRCODE_RRRR_IRD */
13620c794b3Sgavinm 		AO_MCA_R4_BIT_PREFETCH,	/* MCAX86_ERRCODE_RRRR_PREFETCH */
13720c794b3Sgavinm 		AO_MCA_R4_BIT_EVICT,	/* MCAX86_ERRCODE_RRRR_EVICT */
13820c794b3Sgavinm 		AO_MCA_R4_BIT_SNOOP	/* MCAX86_ERRCODE_RRRR_SNOOP */
1397aec1d6eScindi 	};
1407aec1d6eScindi 
1417aec1d6eScindi 	ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t));
1427aec1d6eScindi 
1437aec1d6eScindi 	return ((ref & ao_r4_map[r4]) != 0);
1447aec1d6eScindi }
1457aec1d6eScindi 
1467aec1d6eScindi static int
ao_disp_match_pp(uint8_t ref,uint8_t pp)1477aec1d6eScindi ao_disp_match_pp(uint8_t ref, uint8_t pp)
1487aec1d6eScindi {
1497aec1d6eScindi 	static const uint8_t ao_pp_map[] = {
15020c794b3Sgavinm 		AO_MCA_PP_BIT_SRC,	/* MCAX86_ERRCODE_PP_SRC */
15120c794b3Sgavinm 		AO_MCA_PP_BIT_RES,	/* MCAX86_ERRCODE_PP_RES */
15220c794b3Sgavinm 		AO_MCA_PP_BIT_OBS,	/* MCAX86_ERRCODE_PP_OBS */
15320c794b3Sgavinm 		AO_MCA_PP_BIT_GEN	/* MCAX86_ERRCODE_PP_GEN */
1547aec1d6eScindi 	};
1557aec1d6eScindi 
1567aec1d6eScindi 	ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t));
1577aec1d6eScindi 
1587aec1d6eScindi 	return ((ref & ao_pp_map[pp]) != 0);
1597aec1d6eScindi }
1607aec1d6eScindi 
1617aec1d6eScindi static int
ao_disp_match_ii(uint8_t ref,uint8_t ii)1627aec1d6eScindi ao_disp_match_ii(uint8_t ref, uint8_t ii)
1637aec1d6eScindi {
1647aec1d6eScindi 	static const uint8_t ao_ii_map[] = {
16520c794b3Sgavinm 		AO_MCA_II_BIT_MEM,	/* MCAX86_ERRCODE_II_MEM */
1667aec1d6eScindi 		0,
16720c794b3Sgavinm 		AO_MCA_II_BIT_IO,	/* MCAX86_ERRCODE_II_IO */
16820c794b3Sgavinm 		AO_MCA_II_BIT_GEN	/* MCAX86_ERRCODE_II_GEN */
1697aec1d6eScindi 	};
1707aec1d6eScindi 
1717aec1d6eScindi 	ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t));
1727aec1d6eScindi 
1737aec1d6eScindi 	return ((ref & ao_ii_map[ii]) != 0);
1747aec1d6eScindi }
1757aec1d6eScindi 
1767aec1d6eScindi static uint8_t
bit_strip(uint16_t * codep,uint16_t mask,uint16_t shift)1777aec1d6eScindi bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
1787aec1d6eScindi {
1797aec1d6eScindi 	uint8_t val = (*codep & mask) >> shift;
1807aec1d6eScindi 	*codep &= ~mask;
1817aec1d6eScindi 	return (val);
1827aec1d6eScindi }
1837aec1d6eScindi 
1847aec1d6eScindi #define	BIT_STRIP(codep, name) \
18520c794b3Sgavinm 	bit_strip(codep, MCAX86_ERRCODE_##name##_MASK, \
18620c794b3Sgavinm 	MCAX86_ERRCODE_##name##_SHIFT)
1877aec1d6eScindi 
18820c794b3Sgavinm /*ARGSUSED*/
1897aec1d6eScindi static int
ao_disp_match_one(const ao_error_disp_t * aed,uint64_t status,uint32_t rev,int bankno)1908a40a695Sgavinm ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev,
1918a40a695Sgavinm     int bankno)
1927aec1d6eScindi {
19320c794b3Sgavinm 	uint16_t code = MCAX86_ERRCODE(status);
19420c794b3Sgavinm 	uint8_t extcode = AMD_EXT_ERRCODE(status);
1957aec1d6eScindi 	uint64_t stat_mask = aed->aed_stat_mask;
1967aec1d6eScindi 	uint64_t stat_mask_res = aed->aed_stat_mask_res;
1977aec1d6eScindi 
1987aec1d6eScindi 	/*
1997aec1d6eScindi 	 * If the bank's status register indicates overflow, then we can no
2007aec1d6eScindi 	 * longer rely on the value of CECC: our experience with actual fault
2017aec1d6eScindi 	 * injection has shown that multiple CE's overwriting each other shows
2027aec1d6eScindi 	 * AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero.  This
2037aec1d6eScindi 	 * should be clarified in a future BKDG or by the Revision Guide.
2048a40a695Sgavinm 	 * This behaviour is fixed in revision F.
2057aec1d6eScindi 	 */
2068a40a695Sgavinm 	if (bankno == AMD_MCA_BANK_NB &&
207*22e4c3acSKeith M Wesolowski 	    !chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_F) &&
20820c794b3Sgavinm 	    status & MSR_MC_STATUS_OVER) {
2097aec1d6eScindi 		stat_mask &= ~AMD_BANK_STAT_CECC;
2107aec1d6eScindi 		stat_mask_res &= ~AMD_BANK_STAT_CECC;
2117aec1d6eScindi 	}
2127aec1d6eScindi 
2137aec1d6eScindi 	if ((status & stat_mask) != stat_mask_res)
2147aec1d6eScindi 		return (0);
2157aec1d6eScindi 
2167aec1d6eScindi 	/*
2177aec1d6eScindi 	 * r4 and pp bits are stored separately, so we mask off and compare them
2187aec1d6eScindi 	 * for the code types that use them.  Once we've taken the r4 and pp
2197aec1d6eScindi 	 * bits out of the equation, we can directly compare the resulting code
2207aec1d6eScindi 	 * with the one stored in the ao_error_disp_t.
2217aec1d6eScindi 	 */
2227aec1d6eScindi 	if (AMD_ERRCODE_ISMEM(code)) {
22320c794b3Sgavinm 		uint8_t r4 = BIT_STRIP(&code, RRRR);
2247aec1d6eScindi 
2257aec1d6eScindi 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4))
2267aec1d6eScindi 			return (0);
2277aec1d6eScindi 
2287aec1d6eScindi 	} else if (AMD_ERRCODE_ISBUS(code)) {
22920c794b3Sgavinm 		uint8_t r4 = BIT_STRIP(&code, RRRR);
2307aec1d6eScindi 		uint8_t pp = BIT_STRIP(&code, PP);
2317aec1d6eScindi 		uint8_t ii = BIT_STRIP(&code, II);
2327aec1d6eScindi 
2337aec1d6eScindi 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) ||
2347aec1d6eScindi 		    !ao_disp_match_pp(aed->aed_stat_pp_bits, pp) ||
2357aec1d6eScindi 		    !ao_disp_match_ii(aed->aed_stat_ii_bits, ii))
2367aec1d6eScindi 			return (0);
2377aec1d6eScindi 	}
2387aec1d6eScindi 
2397aec1d6eScindi 	return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode);
2407aec1d6eScindi }
2417aec1d6eScindi 
24220c794b3Sgavinm /*ARGSUSED*/
24320c794b3Sgavinm 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)244c84b7bbeSAdrian Frost ao_ms_disp_match(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
24520c794b3Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
2467aec1d6eScindi {
24720c794b3Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
248*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
2497aec1d6eScindi 	const ao_error_disp_t *aed;
2507aec1d6eScindi 
25120c794b3Sgavinm 	for (aed = ao_error_disp[banknum]; aed->aed_stat_mask != 0; aed++) {
25220c794b3Sgavinm 		if (ao_disp_match_one(aed, status, rev, banknum))
25320c794b3Sgavinm 			return ((cms_cookie_t)aed);
2547aec1d6eScindi 	}
2557aec1d6eScindi 
25620c794b3Sgavinm 	return (NULL);
2577aec1d6eScindi }
2587aec1d6eScindi 
25920c794b3Sgavinm /*ARGSUSED*/
2607aec1d6eScindi void
ao_ms_ereport_class(cmi_hdl_t hdl,cms_cookie_t mscookie,const char ** cpuclsp,const char ** leafclsp)26120c794b3Sgavinm ao_ms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
26220c794b3Sgavinm     const char **cpuclsp, const char **leafclsp)
2637aec1d6eScindi {
26420c794b3Sgavinm 	const ao_error_disp_t *aed = mscookie;
2657aec1d6eScindi 
26620c794b3Sgavinm 	if (aed != NULL) {
26720c794b3Sgavinm 		*cpuclsp = FM_EREPORT_CPU_AMD;
26820c794b3Sgavinm 		*leafclsp = aed->aed_class;
26920c794b3Sgavinm 	}
2707aec1d6eScindi }
2717aec1d6eScindi 
27220c794b3Sgavinm static int
ao_chip_once(ao_ms_data_t * ao,enum ao_cfgonce_bitnum what)27320c794b3Sgavinm ao_chip_once(ao_ms_data_t *ao, enum ao_cfgonce_bitnum what)
2748a40a695Sgavinm {
27520c794b3Sgavinm 	return (atomic_set_long_excl(&ao->ao_ms_shared->aos_cfgonce,
27620c794b3Sgavinm 	    what) == 0 ?  B_TRUE : B_FALSE);
2778a40a695Sgavinm }
2788a40a695Sgavinm 
2798a40a695Sgavinm /*
2808a40a695Sgavinm  * This knob exists in case any platform has a problem with our default
2818a40a695Sgavinm  * policy of disabling any interrupt registered in the NB MC4_MISC
2828a40a695Sgavinm  * register.  Setting this may cause Solaris and external entities
2838a40a695Sgavinm  * who also have an interest in this register to argue over available
2848a40a695Sgavinm  * telemetry (so setting it is generally not recommended).
2858a40a695Sgavinm  */
2868a40a695Sgavinm int ao_nb_cfg_mc4misc_noseize = 0;
2878a40a695Sgavinm 
2888a40a695Sgavinm /*
2898a40a695Sgavinm  * The BIOS may have setup to receive SMI on counter overflow.  It may also
2908a40a695Sgavinm  * have locked various fields or made them read-only.  We will clear any
2918a40a695Sgavinm  * SMI request and leave the register locked.  We will also clear the
2928a40a695Sgavinm  * counter and enable counting - while we don't use the counter it is nice
2938a40a695Sgavinm  * to have it enabled for verification and debug work.
2948a40a695Sgavinm  */
2958a40a695Sgavinm static void
nb_mcamisc_init(cmi_hdl_t hdl,ao_ms_data_t * ao,uint32_t rev)29620c794b3Sgavinm nb_mcamisc_init(cmi_hdl_t hdl, ao_ms_data_t *ao, uint32_t rev)
2978a40a695Sgavinm {
29820c794b3Sgavinm 	uint64_t val, nval;
29920c794b3Sgavinm 
300*22e4c3acSKeith M Wesolowski 	if (!chiprev_matches(rev, AO_F_REVS_FG))
30120c794b3Sgavinm 		return;
3028a40a695Sgavinm 
30320c794b3Sgavinm 	if (cmi_hdl_rdmsr(hdl, AMD_MSR_NB_MISC, &val) != CMI_SUCCESS)
3048a40a695Sgavinm 		return;
3058a40a695Sgavinm 
30620c794b3Sgavinm 	ao->ao_ms_shared->aos_bcfg_nb_misc = val;
3078a40a695Sgavinm 
3088a40a695Sgavinm 	if (ao_nb_cfg_mc4misc_noseize)
3098a40a695Sgavinm 		return;		/* stash BIOS value, but no changes */
3108a40a695Sgavinm 
3118a40a695Sgavinm 
3128a40a695Sgavinm 	/*
3138a40a695Sgavinm 	 * The Valid bit tells us whether the CtrP bit is defined; if it
3148a40a695Sgavinm 	 * is the CtrP bit tells us whether an ErrCount field is present.
3158a40a695Sgavinm 	 * If not then there is nothing for us to do.
3168a40a695Sgavinm 	 */
3178a40a695Sgavinm 	if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP))
3188a40a695Sgavinm 		return;
3198a40a695Sgavinm 
3208a40a695Sgavinm 
32120c794b3Sgavinm 	nval = val;
32220c794b3Sgavinm 	nval |= AMD_NB_MISC_CNTEN;		/* enable ECC error counting */
32320c794b3Sgavinm 	nval &= ~AMD_NB_MISC_ERRCOUNT_MASK;	/* clear ErrCount */
32420c794b3Sgavinm 	nval &= ~AMD_NB_MISC_OVRFLW;		/* clear Ovrflw */
32520c794b3Sgavinm 	nval &= ~AMD_NB_MISC_INTTYPE_MASK;	/* no interrupt on overflow */
32620c794b3Sgavinm 	nval |= AMD_NB_MISC_LOCKED;
32720c794b3Sgavinm 
32820c794b3Sgavinm 	if (nval != val) {
32920c794b3Sgavinm 		uint64_t locked = val & AMD_NB_MISC_LOCKED;
33020c794b3Sgavinm 
33120c794b3Sgavinm 		if (locked)
33220c794b3Sgavinm 			ao_bankstatus_prewrite(hdl, ao);
3338a40a695Sgavinm 
33420c794b3Sgavinm 		(void) cmi_hdl_wrmsr(hdl, AMD_MSR_NB_MISC, nval);
3358a40a695Sgavinm 
33620c794b3Sgavinm 		if (locked)
33720c794b3Sgavinm 			ao_bankstatus_postwrite(hdl, ao);
33820c794b3Sgavinm 	}
3397aec1d6eScindi }
3407aec1d6eScindi 
3417aec1d6eScindi /*
342bb86c342Sgavinm  * NorthBridge (NB) MCA Configuration.
3438a40a695Sgavinm  *
3448a40a695Sgavinm  * We add and remove bits from the BIOS-configured value, rather than
3458a40a695Sgavinm  * writing an absolute value.  The variables ao_nb_cfg_{add,remove}_cmn and
3468a40a695Sgavinm  * ap_nb_cfg_{add,remove}_revFG are available for modification via kmdb
3478a40a695Sgavinm  * and /etc/system.  The revision-specific adds and removes are applied
3488a40a695Sgavinm  * after the common changes, and one write is made to the config register.
3498a40a695Sgavinm  * These are not intended for watchdog configuration via these variables -
3508a40a695Sgavinm  * use the watchdog policy below.
3518a40a695Sgavinm  */
3528a40a695Sgavinm 
3538a40a695Sgavinm /*
3548a40a695Sgavinm  * Bits to be added to the NB configuration register - all revs.
3558a40a695Sgavinm  */
3568a40a695Sgavinm uint32_t ao_nb_cfg_add_cmn = AMD_NB_CFG_ADD_CMN;
3578a40a695Sgavinm 
3588a40a695Sgavinm /*
3598a40a695Sgavinm  * Bits to be cleared from the NB configuration register - all revs.
3608a40a695Sgavinm  */
3618a40a695Sgavinm uint32_t ao_nb_cfg_remove_cmn = AMD_NB_CFG_REMOVE_CMN;
3628a40a695Sgavinm 
3638a40a695Sgavinm /*
3648a40a695Sgavinm  * Bits to be added to the NB configuration register - revs F and G.
3657aec1d6eScindi  */
3668a40a695Sgavinm uint32_t ao_nb_cfg_add_revFG = AMD_NB_CFG_ADD_REV_FG;
3677aec1d6eScindi 
3687aec1d6eScindi /*
3698a40a695Sgavinm  * Bits to be cleared from the NB configuration register - revs F and G.
3707aec1d6eScindi  */
3718a40a695Sgavinm uint32_t ao_nb_cfg_remove_revFG = AMD_NB_CFG_REMOVE_REV_FG;
3728a40a695Sgavinm 
3738a40a695Sgavinm struct ao_nb_cfg {
3748a40a695Sgavinm 	uint32_t cfg_revmask;
3758a40a695Sgavinm 	uint32_t *cfg_add_p;
3768a40a695Sgavinm 	uint32_t *cfg_remove_p;
3778a40a695Sgavinm };
3788a40a695Sgavinm 
3798a40a695Sgavinm static const struct ao_nb_cfg ao_cfg_extra[] = {
38020c794b3Sgavinm 	{ AO_F_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG },
3818a40a695Sgavinm 	{ X86_CHIPREV_UNKNOWN, NULL, NULL }
3828a40a695Sgavinm };
3837aec1d6eScindi 
3847aec1d6eScindi /*
3857aec1d6eScindi  * Bits to be used if we configure the NorthBridge (NB) Watchdog.  The watchdog
3867aec1d6eScindi  * triggers a machine check exception when no response to an NB system access
3878a40a695Sgavinm  * occurs within a specified time interval.
3887aec1d6eScindi  */
3897aec1d6eScindi uint32_t ao_nb_cfg_wdog =
3907aec1d6eScindi     AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
3917aec1d6eScindi     AMD_NB_CFG_WDOGTMRBASESEL_1MS;
3927aec1d6eScindi 
3938a40a695Sgavinm /*
3948a40a695Sgavinm  * The default watchdog policy is to enable it (at the above rate) if it
3958a40a695Sgavinm  * is disabled;  if it is enabled then we leave it enabled at the rate
3968a40a695Sgavinm  * chosen by the BIOS.
3978a40a695Sgavinm  */
3988a40a695Sgavinm enum {
3998a40a695Sgavinm 	AO_NB_WDOG_LEAVEALONE,		/* Don't touch watchdog config */
4008a40a695Sgavinm 	AO_NB_WDOG_DISABLE,		/* Always disable watchdog */
4018a40a695Sgavinm 	AO_NB_WDOG_ENABLE_IF_DISABLED,	/* If disabled, enable at our rate */
4028a40a695Sgavinm 	AO_NB_WDOG_ENABLE_FORCE_RATE	/* Enable and set our rate */
4038a40a695Sgavinm } ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED;
4048a40a695Sgavinm 
4057aec1d6eScindi static void
ao_nb_cfg(ao_ms_data_t * ao,uint32_t rev)40620c794b3Sgavinm ao_nb_cfg(ao_ms_data_t *ao, uint32_t rev)
4077aec1d6eScindi {
4088a40a695Sgavinm 	const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0];
4098031591dSSrihari Venkatesan 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
4107aec1d6eScindi 	uint32_t val;
4117aec1d6eScindi 
4127aec1d6eScindi 	/*
4137aec1d6eScindi 	 * Read the NorthBridge (NB) configuration register in PCI space,
4147aec1d6eScindi 	 * modify the settings accordingly, and store the new value back.
41520c794b3Sgavinm 	 * Note that the stashed BIOS config value aos_bcfg_nb_cfg is used
41620c794b3Sgavinm 	 * in ereport payload population to determine ECC syndrome type for
41720c794b3Sgavinm 	 * memory errors.
4187aec1d6eScindi 	 */
41920c794b3Sgavinm 	ao->ao_ms_shared->aos_bcfg_nb_cfg = val =
4208031591dSSrihari Venkatesan 	    ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
4217aec1d6eScindi 
4228a40a695Sgavinm 	switch (ao_nb_watchdog_policy) {
4238a40a695Sgavinm 	case AO_NB_WDOG_LEAVEALONE:
4248a40a695Sgavinm 		break;
4258a40a695Sgavinm 
4268a40a695Sgavinm 	case AO_NB_WDOG_DISABLE:
4278a40a695Sgavinm 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
4288a40a695Sgavinm 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
4298a40a695Sgavinm 		val |= AMD_NB_CFG_WDOGTMRDIS;
4308a40a695Sgavinm 		break;
4318a40a695Sgavinm 
4328a40a695Sgavinm 	default:
4338a40a695Sgavinm 		cmn_err(CE_NOTE, "ao_nb_watchdog_policy=%d unrecognised, "
4348a40a695Sgavinm 		    "using default policy", ao_nb_watchdog_policy);
4358a40a695Sgavinm 		/*FALLTHRU*/
4368a40a695Sgavinm 
4378a40a695Sgavinm 	case AO_NB_WDOG_ENABLE_IF_DISABLED:
43825f47677Sgavinm 		if (!(val & AMD_NB_CFG_WDOGTMRDIS))
4398a40a695Sgavinm 			break;	/* if enabled leave rate intact */
4408a40a695Sgavinm 		/*FALLTHRU*/
4418a40a695Sgavinm 
4428a40a695Sgavinm 	case AO_NB_WDOG_ENABLE_FORCE_RATE:
4437aec1d6eScindi 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
4447aec1d6eScindi 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
4457aec1d6eScindi 		val &= ~AMD_NB_CFG_WDOGTMRDIS;
4467aec1d6eScindi 		val |= ao_nb_cfg_wdog;
4478a40a695Sgavinm 		break;
4487aec1d6eScindi 	}
4497aec1d6eScindi 
4508a40a695Sgavinm 	/*
4518a40a695Sgavinm 	 * Now apply bit adds and removes, first those common to all revs
4528a40a695Sgavinm 	 * and then the revision-specific ones.
4538a40a695Sgavinm 	 */
4548a40a695Sgavinm 	val &= ~ao_nb_cfg_remove_cmn;
4558a40a695Sgavinm 	val |= ao_nb_cfg_add_cmn;
4568a40a695Sgavinm 
4578a40a695Sgavinm 	while (nbcp->cfg_revmask != X86_CHIPREV_UNKNOWN) {
458*22e4c3acSKeith M Wesolowski 		if (chiprev_matches(rev, nbcp->cfg_revmask)) {
4598a40a695Sgavinm 			val &= ~(*nbcp->cfg_remove_p);
4608a40a695Sgavinm 			val |= *nbcp->cfg_add_p;
4618a40a695Sgavinm 		}
4628a40a695Sgavinm 		nbcp++;
4638a40a695Sgavinm 	}
4647aec1d6eScindi 
4658031591dSSrihari Venkatesan 	ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG, val);
466bb86c342Sgavinm }
467bb86c342Sgavinm 
468bb86c342Sgavinm static void
ao_dram_cfg(ao_ms_data_t * ao,uint32_t rev)46920c794b3Sgavinm ao_dram_cfg(ao_ms_data_t *ao, uint32_t rev)
470bb86c342Sgavinm {
4718031591dSSrihari Venkatesan 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
472bb86c342Sgavinm 	union mcreg_dramcfg_lo dcfglo;
473bb86c342Sgavinm 
47420c794b3Sgavinm 	ao->ao_ms_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) =
4758031591dSSrihari Venkatesan 	    ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO);
47620c794b3Sgavinm 	ao->ao_ms_shared->aos_bcfg_dcfg_hi =
4778031591dSSrihari Venkatesan 	    ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGHI);
478bb86c342Sgavinm #ifdef OPTERON_ERRATUM_172
479*22e4c3acSKeith M Wesolowski 	if (chiprev_matches(rev, AO_F_REVS_FG) &&
48020c794b3Sgavinm 	    MCREG_FIELD_F_revFG(&dcfglo, ParEn)) {
48120c794b3Sgavinm 		MCREG_FIELD_F_revFG(&dcfglo, ParEn) = 0;
4828031591dSSrihari Venkatesan 		ao_pcicfg_write(procnodeid, MC_FUNC_DRAMCTL,
4838031591dSSrihari Venkatesan 		    MC_DC_REG_DRAMCFGLO, MCREG_VAL32(&dcfglo));
484bb86c342Sgavinm 	}
485bb86c342Sgavinm #endif
4867aec1d6eScindi }
4877aec1d6eScindi 
4888a40a695Sgavinm /*
4898a40a695Sgavinm  * This knob exists in case any platform has a problem with our default
4908a40a695Sgavinm  * policy of disabling any interrupt registered in the online spare
4918a40a695Sgavinm  * control register.  Setting this may cause Solaris and external entities
4928a40a695Sgavinm  * who also have an interest in this register to argue over available
4938a40a695Sgavinm  * telemetry (so setting it is generally not recommended).
4948a40a695Sgavinm  */
4958a40a695Sgavinm int ao_nb_cfg_sparectl_noseize = 0;
4968a40a695Sgavinm 
4978a40a695Sgavinm /*
4988a40a695Sgavinm  * Setup the online spare control register (revs F and G).  We disable
4998a40a695Sgavinm  * any interrupt registered by the BIOS and zero all error counts.
5008a40a695Sgavinm  */
5018a40a695Sgavinm static void
ao_sparectl_cfg(ao_ms_data_t * ao)50220c794b3Sgavinm ao_sparectl_cfg(ao_ms_data_t *ao)
5038a40a695Sgavinm {
5048031591dSSrihari Venkatesan 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
5058a40a695Sgavinm 	union mcreg_sparectl sparectl;
5068a40a695Sgavinm 	int chan, cs;
5078a40a695Sgavinm 
50820c794b3Sgavinm 	ao->ao_ms_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) =
5098031591dSSrihari Venkatesan 	    ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
5108a40a695Sgavinm 
5118a40a695Sgavinm 	if (ao_nb_cfg_sparectl_noseize)
5128a40a695Sgavinm 		return;	/* stash BIOS value, but no changes */
5138a40a695Sgavinm 
5148a40a695Sgavinm 	/*
5158a40a695Sgavinm 	 * If the BIOS has requested SMI interrupt type for ECC count
5168a40a695Sgavinm 	 * overflow for a chip-select or channel force those off.
5178a40a695Sgavinm 	 */
51820c794b3Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0;
51920c794b3Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0;
5208a40a695Sgavinm 
5218a40a695Sgavinm 	/*
5228a40a695Sgavinm 	 * Zero EccErrCnt and write this back to all chan/cs combinations.
5238a40a695Sgavinm 	 */
52420c794b3Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1;
52520c794b3Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, EccErrCnt) = 0;
5268a40a695Sgavinm 	for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) {
52720c794b3Sgavinm 		MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = chan;
5288a40a695Sgavinm 
5298a40a695Sgavinm 		for (cs = 0; cs < MC_CHIP_NCS; cs++) {
53020c794b3Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramCs) = cs;
5318031591dSSrihari Venkatesan 			ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
532bb86c342Sgavinm 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
5338a40a695Sgavinm 		}
5348a40a695Sgavinm 	}
5358a40a695Sgavinm }
5368a40a695Sgavinm 
53720c794b3Sgavinm int ao_forgive_uc = 0;		/* For test/debug only */
53820c794b3Sgavinm int ao_forgive_pcc = 0;		/* For test/debug only */
53920c794b3Sgavinm int ao_fake_poison = 0;		/* For test/debug only */
5408a40a695Sgavinm 
54120c794b3Sgavinm 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)54220c794b3Sgavinm ao_ms_error_action(cmi_hdl_t hdl, int ismc, int banknum,
54320c794b3Sgavinm     uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
54420c794b3Sgavinm {
54520c794b3Sgavinm 	const ao_error_disp_t *aed;
54620c794b3Sgavinm 	uint32_t retval = 0;
54720c794b3Sgavinm 	uint8_t when;
54820c794b3Sgavinm 	int en;
5497aec1d6eScindi 
55020c794b3Sgavinm 	if (ao_forgive_uc)
55120c794b3Sgavinm 		retval |= CMS_ERRSCOPE_CLEARED_UC;
5528a40a695Sgavinm 
55320c794b3Sgavinm 	if (ao_forgive_pcc)
55420c794b3Sgavinm 		retval |= CMS_ERRSCOPE_CURCONTEXT_OK;
5557aec1d6eScindi 
55620c794b3Sgavinm 	if (ao_fake_poison && status & MSR_MC_STATUS_UC)
55720c794b3Sgavinm 		retval |= CMS_ERRSCOPE_POISONED;
5587aec1d6eScindi 
55920c794b3Sgavinm 	if (retval)
56020c794b3Sgavinm 		return (retval);
5617aec1d6eScindi 
562c84b7bbeSAdrian Frost 	aed = ao_ms_disp_match(hdl, ismc, banknum, status, addr, misc,
563c84b7bbeSAdrian Frost 	    mslogout);
5647aec1d6eScindi 
5657aec1d6eScindi 	/*
56620c794b3Sgavinm 	 * If we do not recognise the error let the cpu module apply
56720c794b3Sgavinm 	 * the generic criteria to decide how to react.
5687aec1d6eScindi 	 */
56920c794b3Sgavinm 	if (aed == NULL)
57020c794b3Sgavinm 		return (0);
5717aec1d6eScindi 
57220c794b3Sgavinm 	en = (status & MSR_MC_STATUS_EN) != 0;
5737aec1d6eScindi 
57420c794b3Sgavinm 	if ((when = aed->aed_panic_when) == AO_AED_PANIC_NEVER)
57520c794b3Sgavinm 		retval |= CMS_ERRSCOPE_IGNORE_ERR;
5767aec1d6eScindi 
57720c794b3Sgavinm 	if ((when & AO_AED_PANIC_ALWAYS) ||
57820c794b3Sgavinm 	    ((when & AO_AED_PANIC_IFMCE) && (en || ismc)))
57920c794b3Sgavinm 		retval |= CMS_ERRSCOPE_FORCE_FATAL;
5807aec1d6eScindi 
58120c794b3Sgavinm 	/*
58220c794b3Sgavinm 	 * The original AMD implementation would panic on a machine check
58320c794b3Sgavinm 	 * (not a poll) if the status overflow bit was set, with an
58420c794b3Sgavinm 	 * exception for the case of rev F or later with an NB error
58520c794b3Sgavinm 	 * indicating CECC.  This came from the perception that the
58620c794b3Sgavinm 	 * overflow bit was not correctly managed on rev E and earlier, for
58720c794b3Sgavinm 	 * example that repeated correctable memeory errors did not set
58820c794b3Sgavinm 	 * OVER but somehow clear CECC.
58920c794b3Sgavinm 	 *
59020c794b3Sgavinm 	 * We will leave the generic support to evaluate overflow errors
59120c794b3Sgavinm 	 * and decide to panic on their individual merits, e.g., if PCC
59220c794b3Sgavinm 	 * is set and so on.  The AMD docs do say (as Intel does) that
59320c794b3Sgavinm 	 * the status information is *all* from the higher-priority
59420c794b3Sgavinm 	 * error in the case of an overflow, so it is at least as serious
59520c794b3Sgavinm 	 * as the original and we can decide panic etc based on it.
59620c794b3Sgavinm 	 */
5977aec1d6eScindi 
59820c794b3Sgavinm 	return (retval);
5997aec1d6eScindi }
6007aec1d6eScindi 
60120c794b3Sgavinm /*
60220c794b3Sgavinm  * Will need to change for family 0x10
60320c794b3Sgavinm  */
6047aec1d6eScindi static uint_t
ao_ereport_synd(ao_ms_data_t * ao,uint64_t status,uint_t * typep,int is_nb)60520c794b3Sgavinm ao_ereport_synd(ao_ms_data_t *ao, uint64_t status, uint_t *typep,
6068a40a695Sgavinm     int is_nb)
6077aec1d6eScindi {
6087aec1d6eScindi 	if (is_nb) {
60920c794b3Sgavinm 		if (ao->ao_ms_shared->aos_bcfg_nb_cfg &
61020c794b3Sgavinm 		    AMD_NB_CFG_CHIPKILLECCEN) {
6117aec1d6eScindi 			*typep = AMD_SYNDTYPE_CHIPKILL;
61220c794b3Sgavinm 			return (AMD_NB_STAT_CKSYND(status));
6137aec1d6eScindi 		} else {
6147aec1d6eScindi 			*typep = AMD_SYNDTYPE_ECC;
61520c794b3Sgavinm 			return (AMD_BANK_SYND(status));
6167aec1d6eScindi 		}
6177aec1d6eScindi 	} else {
6187aec1d6eScindi 		*typep = AMD_SYNDTYPE_ECC;
61920c794b3Sgavinm 		return (AMD_BANK_SYND(status));
6207aec1d6eScindi 	}
6217aec1d6eScindi }
6227aec1d6eScindi 
62320c794b3Sgavinm static nvlist_t *
ao_ereport_create_resource_elem(cmi_hdl_t hdl,nv_alloc_t * nva,mc_unum_t * unump,int dimmnum)624074bb90dSTom Pothier ao_ereport_create_resource_elem(cmi_hdl_t hdl, nv_alloc_t *nva,
625074bb90dSTom Pothier     mc_unum_t *unump, int dimmnum)
6267aec1d6eScindi {
62720c794b3Sgavinm 	nvlist_t *nvl, *snvl;
628074bb90dSTom Pothier 	nvlist_t *board_list = NULL;
6297aec1d6eScindi 
63020c794b3Sgavinm 	if ((nvl = fm_nvlist_create(nva)) == NULL)	/* freed by caller */
63120c794b3Sgavinm 		return (NULL);
63220c794b3Sgavinm 
63320c794b3Sgavinm 	if ((snvl = fm_nvlist_create(nva)) == NULL) {
63420c794b3Sgavinm 		fm_nvlist_destroy(nvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
63520c794b3Sgavinm 		return (NULL);
63620c794b3Sgavinm 	}
6377aec1d6eScindi 
6387aec1d6eScindi 	(void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
6397aec1d6eScindi 	    unump->unum_offset);
6407aec1d6eScindi 
641074bb90dSTom Pothier 	if (!x86gentopo_legacy) {
642074bb90dSTom Pothier 		board_list = cmi_hdl_smb_bboard(hdl);
643074bb90dSTom Pothier 
644074bb90dSTom Pothier 		if (board_list == NULL) {
645074bb90dSTom Pothier 			fm_nvlist_destroy(nvl,
646074bb90dSTom Pothier 			    nva ? FM_NVA_RETAIN : FM_NVA_FREE);
647074bb90dSTom Pothier 			fm_nvlist_destroy(snvl,
648074bb90dSTom Pothier 			    nva ? FM_NVA_RETAIN : FM_NVA_FREE);
649074bb90dSTom Pothier 			return (NULL);
650074bb90dSTom Pothier 		}
651074bb90dSTom Pothier 
652074bb90dSTom Pothier 		fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, snvl,
653074bb90dSTom Pothier 		    board_list, 4,
654074bb90dSTom Pothier 		    "chip", cmi_hdl_smb_chipid(hdl),
655074bb90dSTom Pothier 		    "memory-controller", unump->unum_mc,
656074bb90dSTom Pothier 		    "dimm", unump->unum_dimms[dimmnum],
657074bb90dSTom Pothier 		    "rank", unump->unum_rank);
658074bb90dSTom Pothier 	} else {
659074bb90dSTom Pothier 		fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, snvl, 5,
660074bb90dSTom Pothier 		    "motherboard", unump->unum_board,
661074bb90dSTom Pothier 		    "chip", unump->unum_chip,
662074bb90dSTom Pothier 		    "memory-controller", unump->unum_mc,
663074bb90dSTom Pothier 		    "dimm", unump->unum_dimms[dimmnum],
664074bb90dSTom Pothier 		    "rank", unump->unum_rank);
665074bb90dSTom Pothier 	}
6667aec1d6eScindi 
66752d60c84Sgavinm 	fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
66820c794b3Sgavinm 
66920c794b3Sgavinm 	return (nvl);
6707aec1d6eScindi }
6717aec1d6eScindi 
6727aec1d6eScindi static void
ao_ereport_add_resource(cmi_hdl_t hdl,nvlist_t * payload,nv_alloc_t * nva,mc_unum_t * unump)673074bb90dSTom Pothier ao_ereport_add_resource(cmi_hdl_t hdl, nvlist_t *payload, nv_alloc_t *nva,
674074bb90dSTom Pothier     mc_unum_t *unump)
6757aec1d6eScindi {
6767aec1d6eScindi 
6777aec1d6eScindi 	nvlist_t *elems[MC_UNUM_NDIMM];
6787aec1d6eScindi 	int nelems = 0;
6797aec1d6eScindi 	int i;
6807aec1d6eScindi 
6817aec1d6eScindi 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
6828a40a695Sgavinm 		if (unump->unum_dimms[i] == MC_INVALNUM)
6837aec1d6eScindi 			break;
68420c794b3Sgavinm 
685074bb90dSTom Pothier 		if ((elems[nelems] = ao_ereport_create_resource_elem(hdl, nva,
68620c794b3Sgavinm 		    unump, i)) == NULL)
68720c794b3Sgavinm 			break;
68820c794b3Sgavinm 
68920c794b3Sgavinm 		nelems++;
6907aec1d6eScindi 	}
6917aec1d6eScindi 
69220c794b3Sgavinm 	if (nelems == 0)
69320c794b3Sgavinm 		return;
69420c794b3Sgavinm 
6957aec1d6eScindi 	fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
696fab5495cSgavinm 	    DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
6977aec1d6eScindi 
6987aec1d6eScindi 	for (i = 0; i < nelems; i++)
69952d60c84Sgavinm 		fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
7007aec1d6eScindi }
7017aec1d6eScindi 
70220c794b3Sgavinm /*ARGSUSED*/
70320c794b3Sgavinm 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)70420c794b3Sgavinm ao_ms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport,
70520c794b3Sgavinm     nv_alloc_t *nva, int banknum, uint64_t status, uint64_t addr,
70620c794b3Sgavinm     uint64_t misc, void *mslogout, cms_cookie_t mscookie)
7077aec1d6eScindi {
70820c794b3Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
70920c794b3Sgavinm 	const ao_error_disp_t *aed = mscookie;
7107aec1d6eScindi 	uint_t synd, syndtype;
71120c794b3Sgavinm 	uint64_t members;
7127aec1d6eScindi 
71320c794b3Sgavinm 	if (aed == NULL)
71420c794b3Sgavinm 		return;
7157aec1d6eScindi 
71620c794b3Sgavinm 	members = aed->aed_ereport_members;
7177aec1d6eScindi 
71820c794b3Sgavinm 	synd = ao_ereport_synd(ao, status, &syndtype,
71920c794b3Sgavinm 	    banknum == AMD_MCA_BANK_NB);
7208a40a695Sgavinm 
7217aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
72220c794b3Sgavinm 		fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND,
7237aec1d6eScindi 		    DATA_TYPE_UINT16, synd, NULL);
7247aec1d6eScindi 	}
7257aec1d6eScindi 
7267aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) {
72720c794b3Sgavinm 		fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE,
7287aec1d6eScindi 		    DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ?
7295667185bSSrihari Venkatesan 		    "C4" : "E"), NULL);
7307aec1d6eScindi 	}
7317aec1d6eScindi 
7327aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) {
7337aec1d6eScindi 		mc_unum_t unum;
7347aec1d6eScindi 
73520c794b3Sgavinm 		if (((aed->aed_flags & AO_AED_FLAGS_ADDRTYPE) ==
73620c794b3Sgavinm 		    AO_AED_F_PHYSICAL) && (status & MSR_MC_STATUS_ADDRV) &&
73720c794b3Sgavinm 		    cmi_mc_patounum(addr, aed->aed_addrvalid_hi,
73820c794b3Sgavinm 		    aed->aed_addrvalid_lo, synd, syndtype, &unum) ==
73920c794b3Sgavinm 		    CMI_SUCCESS)
740074bb90dSTom Pothier 			ao_ereport_add_resource(hdl, ereport, nva, &unum);
7417aec1d6eScindi 	}
7427aec1d6eScindi }
7437aec1d6eScindi 
74420c794b3Sgavinm /*ARGSUSED*/
74520c794b3Sgavinm boolean_t
ao_ms_ereport_includestack(cmi_hdl_t hdl,cms_cookie_t mscookie)74620c794b3Sgavinm ao_ms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
7477aec1d6eScindi {
74820c794b3Sgavinm 	const ao_error_disp_t *aed = mscookie;
7497aec1d6eScindi 
75020c794b3Sgavinm 	if (aed == NULL)
75120c794b3Sgavinm 		return (0);
7527aec1d6eScindi 
75320c794b3Sgavinm 	return ((aed->aed_ereport_members &
75420c794b3Sgavinm 	    FM_EREPORT_PAYLOAD_FLAG_STACK) != 0);
75520c794b3Sgavinm }
7567aec1d6eScindi 
75720c794b3Sgavinm cms_errno_t
ao_ms_msrinject(cmi_hdl_t hdl,uint_t msr,uint64_t val)75820c794b3Sgavinm ao_ms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
75920c794b3Sgavinm {
76020c794b3Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
76120c794b3Sgavinm 	cms_errno_t rv = CMSERR_BADMSRWRITE;
76252d60c84Sgavinm 
76320c794b3Sgavinm 	ao_bankstatus_prewrite(hdl, ao);
76420c794b3Sgavinm 	if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
76520c794b3Sgavinm 		rv = CMS_SUCCESS;
76620c794b3Sgavinm 	ao_bankstatus_postwrite(hdl, ao);
7677aec1d6eScindi 
76820c794b3Sgavinm 	return (rv);
7697aec1d6eScindi }
7707aec1d6eScindi 
7717aec1d6eScindi /*ARGSUSED*/
77220c794b3Sgavinm uint64_t
ao_ms_mcgctl_val(cmi_hdl_t hdl,int nbanks,uint64_t def)77320c794b3Sgavinm ao_ms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
7747aec1d6eScindi {
77520c794b3Sgavinm 	return ((1ULL << nbanks) - 1);
7767aec1d6eScindi }
7777aec1d6eScindi 
77820c794b3Sgavinm boolean_t
ao_ms_bankctl_skipinit(cmi_hdl_t hdl,int banknum)77920c794b3Sgavinm ao_ms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
7807aec1d6eScindi {
78120c794b3Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
7828a40a695Sgavinm 
78320c794b3Sgavinm 	if (banknum != AMD_MCA_BANK_NB)
78420c794b3Sgavinm 		return (B_FALSE);
7858a40a695Sgavinm 
786a4e4e13fSgavinm 	/*
787a4e4e13fSgavinm 	 * If we are the first to atomically set the "I'll do it" bit
788a4e4e13fSgavinm 	 * then return B_FALSE (do not skip), otherwise skip with B_TRUE.
789a4e4e13fSgavinm 	 */
790a4e4e13fSgavinm 	return (ao_chip_once(ao, AO_CFGONCE_NBMCA) == B_TRUE ?
791a4e4e13fSgavinm 	    B_FALSE : B_TRUE);
7927aec1d6eScindi }
7937aec1d6eScindi 
79420c794b3Sgavinm uint64_t
ao_ms_bankctl_val(cmi_hdl_t hdl,int banknum,uint64_t def)79520c794b3Sgavinm ao_ms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
7967aec1d6eScindi {
79720c794b3Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
79820c794b3Sgavinm 	const struct ao_ctl_init *extrap;
79920c794b3Sgavinm 	const ao_bank_cfg_t *bankcfg;
80020c794b3Sgavinm 	uint64_t mcictl;
801*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
8027aec1d6eScindi 
80320c794b3Sgavinm 	if (banknum >= sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]))
80420c794b3Sgavinm 		return (def);
8057aec1d6eScindi 
80620c794b3Sgavinm 	bankcfg = &ao_bank_cfgs[banknum];
80720c794b3Sgavinm 	extrap = bankcfg->bank_ctl_init_extra;
8087aec1d6eScindi 
80920c794b3Sgavinm 	mcictl = bankcfg->bank_ctl_init_cmn;
81020c794b3Sgavinm 
81120c794b3Sgavinm 	while (extrap != NULL && extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) {
812*22e4c3acSKeith M Wesolowski 		if (chiprev_matches(rev, extrap->ctl_revmask))
81320c794b3Sgavinm 			mcictl |= extrap->ctl_bits;
81420c794b3Sgavinm 		extrap++;
81520c794b3Sgavinm 	}
81620c794b3Sgavinm 
81720c794b3Sgavinm 	return (mcictl);
8187aec1d6eScindi }
8197aec1d6eScindi 
820e4b86885SCheng Sean Ye /*ARGSUSED*/
8217aec1d6eScindi void
ao_bankstatus_prewrite(cmi_hdl_t hdl,ao_ms_data_t * ao)82220c794b3Sgavinm ao_bankstatus_prewrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
8237aec1d6eScindi {
824e4b86885SCheng Sean Ye #ifndef __xpv
82520c794b3Sgavinm 	uint64_t hwcr;
8267aec1d6eScindi 
82720c794b3Sgavinm 	if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
82820c794b3Sgavinm 		return;
8297aec1d6eScindi 
83020c794b3Sgavinm 	ao->ao_ms_hwcr_val = hwcr;
8317aec1d6eScindi 
83220c794b3Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
83320c794b3Sgavinm 		hwcr |= AMD_HWCR_MCI_STATUS_WREN;
83420c794b3Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
83520c794b3Sgavinm 	}
836e4b86885SCheng Sean Ye #endif
83720c794b3Sgavinm }
8388a40a695Sgavinm 
839e4b86885SCheng Sean Ye /*ARGSUSED*/
84020c794b3Sgavinm void
ao_bankstatus_postwrite(cmi_hdl_t hdl,ao_ms_data_t * ao)84120c794b3Sgavinm ao_bankstatus_postwrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
84220c794b3Sgavinm {
843e4b86885SCheng Sean Ye #ifndef __xpv
84420c794b3Sgavinm 	uint64_t hwcr = ao->ao_ms_hwcr_val;
8457aec1d6eScindi 
84620c794b3Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
84720c794b3Sgavinm 		hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
84820c794b3Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
84920c794b3Sgavinm 	}
850e4b86885SCheng Sean Ye #endif
85120c794b3Sgavinm }
8528a40a695Sgavinm 
85320c794b3Sgavinm void
ao_ms_mca_init(cmi_hdl_t hdl,int nbanks)85420c794b3Sgavinm ao_ms_mca_init(cmi_hdl_t hdl, int nbanks)
85520c794b3Sgavinm {
85620c794b3Sgavinm 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
857*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
85820c794b3Sgavinm 	ao_ms_mca_t *mca = &ao->ao_ms_mca;
85920c794b3Sgavinm 	uint64_t *maskp;
86020c794b3Sgavinm 	int i;
8618a40a695Sgavinm 
86220c794b3Sgavinm 	maskp = mca->ao_mca_bios_cfg.bcfg_bank_mask = kmem_zalloc(nbanks *
86320c794b3Sgavinm 	    sizeof (uint64_t), KM_SLEEP);
8648a40a695Sgavinm 
8658a40a695Sgavinm 	/*
86620c794b3Sgavinm 	 * Read the bank ctl mask MSRs, but only as many as we know
86720c794b3Sgavinm 	 * certainly exist - don't calculate the register address.
86820c794b3Sgavinm 	 * Also initialize the MCi_MISC register where required.
8698a40a695Sgavinm 	 */
87020c794b3Sgavinm 	for (i = 0; i < MIN(nbanks, ao_nbanks); i++) {
87120c794b3Sgavinm 		(void) cmi_hdl_rdmsr(hdl, ao_bank_cfgs[i].bank_ctl_mask,
87220c794b3Sgavinm 		    maskp++);
87320c794b3Sgavinm 		if (ao_bank_cfgs[i].bank_misc_initfunc != NULL)
87420c794b3Sgavinm 			ao_bank_cfgs[i].bank_misc_initfunc(hdl, ao, rev);
8758a40a695Sgavinm 
8768a40a695Sgavinm 	}
8778a40a695Sgavinm 
87820c794b3Sgavinm 	if (ao_chip_once(ao, AO_CFGONCE_NBCFG) == B_TRUE) {
87920c794b3Sgavinm 		ao_nb_cfg(ao, rev);
8808a40a695Sgavinm 
881*22e4c3acSKeith M Wesolowski 		if (chiprev_matches(rev, AO_F_REVS_FG))
88220c794b3Sgavinm 			ao_sparectl_cfg(ao);
8838a40a695Sgavinm 	}
8847aec1d6eScindi 
88520c794b3Sgavinm 	if (ao_chip_once(ao, AO_CFGONCE_DRAMCFG) == B_TRUE)
88620c794b3Sgavinm 		ao_dram_cfg(ao, rev);
8877aec1d6eScindi 
8888031591dSSrihari Venkatesan 	ao_procnode_scrubber_enable(hdl, ao);
8897aec1d6eScindi }
8907aec1d6eScindi 
891bc946ef3Ssethg /*
892bc946ef3Ssethg  * Note that although this cpu module is loaded before the PSMs are
893bc946ef3Ssethg  * loaded (and hence before acpica is loaded), this function is
894bc946ef3Ssethg  * called from post_startup(), after PSMs are initialized and acpica
895bc946ef3Ssethg  * is loaded.
896bc946ef3Ssethg  */
897bc946ef3Ssethg static int
ao_acpi_find_smicmd(int * asd_port)898bc946ef3Ssethg ao_acpi_find_smicmd(int *asd_port)
899bc946ef3Ssethg {
900db2bae30SDana Myers 	ACPI_TABLE_FADT *fadt = NULL;
901bc946ef3Ssethg 
902bc946ef3Ssethg 	/*
903db2bae30SDana Myers 	 * AcpiGetTable works even if ACPI is disabled, so a failure
904bc946ef3Ssethg 	 * here means we weren't able to retreive a pointer to the FADT.
905bc946ef3Ssethg 	 */
906db2bae30SDana Myers 	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
907db2bae30SDana Myers 	    AE_OK)
908bc946ef3Ssethg 		return (-1);
909bc946ef3Ssethg 
910bc946ef3Ssethg 	ASSERT(fadt != NULL);
911bc946ef3Ssethg 
912db2bae30SDana Myers 	*asd_port = fadt->SmiCommand;
913bc946ef3Ssethg 	return (0);
914bc946ef3Ssethg }
915bc946ef3Ssethg 
9167aec1d6eScindi /*ARGSUSED*/
9177aec1d6eScindi void
ao_ms_post_startup(cmi_hdl_t hdl)91820c794b3Sgavinm ao_ms_post_startup(cmi_hdl_t hdl)
9197aec1d6eScindi {
9207aec1d6eScindi 	const struct ao_smi_disable *asd;
9217aec1d6eScindi 	id_t id;
922bc946ef3Ssethg 	int rv = -1, asd_port;
9237aec1d6eScindi 
9247aec1d6eScindi 	smbios_system_t sy;
9257aec1d6eScindi 	smbios_bios_t sb;
9267aec1d6eScindi 	smbios_info_t si;
9277aec1d6eScindi 
9287aec1d6eScindi 	/*
9297aec1d6eScindi 	 * Fetch the System and BIOS vendor strings from SMBIOS and see if they
9307aec1d6eScindi 	 * match a value in our table.  If so, disable SMI error polling.  This
9317aec1d6eScindi 	 * is grotesque and should be replaced by self-describing vendor-
9327aec1d6eScindi 	 * specific SMBIOS data or a specification enhancement instead.
9337aec1d6eScindi 	 */
9347aec1d6eScindi 	if (ao_mca_smi_disable && ksmbios != NULL &&
9357aec1d6eScindi 	    smbios_info_bios(ksmbios, &sb) != SMB_ERR &&
9367aec1d6eScindi 	    (id = smbios_info_system(ksmbios, &sy)) != SMB_ERR &&
9377aec1d6eScindi 	    smbios_info_common(ksmbios, id, &si) != SMB_ERR) {
9387aec1d6eScindi 
9397aec1d6eScindi 		for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) {
9407aec1d6eScindi 			if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer,
9417aec1d6eScindi 			    strlen(asd->asd_sys_vendor)) != 0 ||
9424156fc34Sgavinm 			    strncmp(asd->asd_sys_product, si.smbi_product,
9434156fc34Sgavinm 			    strlen(asd->asd_sys_product)) != 0 ||
9447aec1d6eScindi 			    strncmp(asd->asd_bios_vendor, sb.smbb_vendor,
9457aec1d6eScindi 			    strlen(asd->asd_bios_vendor)) != 0)
9467aec1d6eScindi 				continue;
9477aec1d6eScindi 
948bc946ef3Ssethg 			/*
949bc946ef3Ssethg 			 * Look for the SMI_CMD port in the ACPI FADT,
950bc946ef3Ssethg 			 * if the port is 0, this platform doesn't support
951bc946ef3Ssethg 			 * SMM, so there is no SMI error polling to disable.
952bc946ef3Ssethg 			 */
953bc946ef3Ssethg 			if ((rv = ao_acpi_find_smicmd(&asd_port)) == 0 &&
954bc946ef3Ssethg 			    asd_port != 0) {
955bc946ef3Ssethg 				cmn_err(CE_CONT, "?SMI polling disabled in "
956bc946ef3Ssethg 				    "favor of Solaris Fault Management for "
957bc946ef3Ssethg 				    "AMD Processors\n");
958bc946ef3Ssethg 
959a6d65bf4Ssethg 				outb(asd_port, asd->asd_code);
960bc946ef3Ssethg 
961bc946ef3Ssethg 			} else if (rv < 0) {
962bc946ef3Ssethg 				cmn_err(CE_CONT, "?Solaris Fault Management "
963bc946ef3Ssethg 				    "for AMD Processors could not disable SMI "
964bc946ef3Ssethg 				    "polling because an error occurred while "
965bc946ef3Ssethg 				    "trying to determine the SMI command port "
966bc946ef3Ssethg 				    "from the ACPI FADT table\n");
967bc946ef3Ssethg 			}
9687aec1d6eScindi 			break;
9697aec1d6eScindi 		}
9707aec1d6eScindi 	}
9717aec1d6eScindi }
972