xref: /illumos-gate/usr/src/uts/i86pc/cpu/authenticamd/authamd_main.c (revision 22e4c3ac083467e1e6241dedfea03e25c101eedf)
120c794b3Sgavinm /*
220c794b3Sgavinm  * CDDL HEADER START
320c794b3Sgavinm  *
420c794b3Sgavinm  * The contents of this file are subject to the terms of the
520c794b3Sgavinm  * Common Development and Distribution License (the "License").
620c794b3Sgavinm  * You may not use this file except in compliance with the License.
720c794b3Sgavinm  *
820c794b3Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
920c794b3Sgavinm  * or http://www.opensolaris.org/os/licensing.
1020c794b3Sgavinm  * See the License for the specific language governing permissions
1120c794b3Sgavinm  * and limitations under the License.
1220c794b3Sgavinm  *
1320c794b3Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
1420c794b3Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1520c794b3Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
1620c794b3Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
1720c794b3Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
1820c794b3Sgavinm  *
1920c794b3Sgavinm  * CDDL HEADER END
2020c794b3Sgavinm  */
2120c794b3Sgavinm 
2220c794b3Sgavinm /*
23c84b7bbeSAdrian Frost  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24*22e4c3acSKeith M Wesolowski  * Copyright 2022 Oxide Computer Co.
2520c794b3Sgavinm  */
2620c794b3Sgavinm 
2720c794b3Sgavinm /*
2820c794b3Sgavinm  * "Generic AMD" model-specific support.  If no more-specific support can
2920c794b3Sgavinm  * be found, or such modules declines to initialize, then for AuthenticAMD
3020c794b3Sgavinm  * cpus this module can have a crack at providing some AMD model-specific
3120c794b3Sgavinm  * support that at least goes beyond common MCA architectural features
3220c794b3Sgavinm  * if not down to the nitty-gritty level for a particular model.  We
3320c794b3Sgavinm  * are layered on top of a cpu module, likely cpu.generic, so there is no
3420c794b3Sgavinm  * need for us to perform common architecturally-accessible functions.
3520c794b3Sgavinm  */
3620c794b3Sgavinm 
3720c794b3Sgavinm #include <sys/types.h>
3820c794b3Sgavinm #include <sys/cmn_err.h>
3920c794b3Sgavinm #include <sys/modctl.h>
4020c794b3Sgavinm #include <sys/cpu_module.h>
4120c794b3Sgavinm #include <sys/mca_x86.h>
4220c794b3Sgavinm #include <sys/pci_cfgspace.h>
4320c794b3Sgavinm #include <sys/x86_archext.h>
4420c794b3Sgavinm #include <sys/mc_amd.h>
4520c794b3Sgavinm #include <sys/fm/protocol.h>
4620c794b3Sgavinm #include <sys/fm/cpu/GENAMD.h>
47074bb90dSTom Pothier #include <sys/fm/smb/fmsmb.h>
48074bb90dSTom Pothier #include <sys/fm/util.h>
4920c794b3Sgavinm #include <sys/nvpair.h>
5020c794b3Sgavinm #include <sys/controlregs.h>
5120c794b3Sgavinm #include <sys/pghw.h>
5220c794b3Sgavinm #include <sys/sunddi.h>
5325f47677Sgavinm #include <sys/sysmacros.h>
5420c794b3Sgavinm #include <sys/cpu_module_ms_impl.h>
5520c794b3Sgavinm 
5620c794b3Sgavinm #include "authamd.h"
5720c794b3Sgavinm 
58074bb90dSTom Pothier extern int x86gentopo_legacy; /* x86 generic topo support */
59074bb90dSTom Pothier 
6020c794b3Sgavinm int authamd_ms_support_disable = 0;
6120c794b3Sgavinm 
6220c794b3Sgavinm #define	AUTHAMD_F_REVS_BCDE \
63*22e4c3acSKeith M Wesolowski 	(X86_CHIPREV_AMD_LEGACY_F_REV_B | X86_CHIPREV_AMD_LEGACY_F_REV_C0 | \
64*22e4c3acSKeith M Wesolowski 	X86_CHIPREV_AMD_LEGACY_F_REV_CG | X86_CHIPREV_AMD_LEGACY_F_REV_D | \
65*22e4c3acSKeith M Wesolowski 	X86_CHIPREV_AMD_LEGACY_F_REV_E)
6620c794b3Sgavinm 
6720c794b3Sgavinm #define	AUTHAMD_F_REVS_FG \
68*22e4c3acSKeith M Wesolowski 	(X86_CHIPREV_AMD_LEGACY_F_REV_F | X86_CHIPREV_AMD_LEGACY_F_REV_G)
6920c794b3Sgavinm 
7020c794b3Sgavinm #define	AUTHAMD_10_REVS_AB \
71*22e4c3acSKeith M Wesolowski 	(X86_CHIPREV_AMD_LEGACY_10_REV_A | X86_CHIPREV_AMD_LEGACY_10_REV_B)
7220c794b3Sgavinm 
7320c794b3Sgavinm /*
7420c794b3Sgavinm  * Bitmasks of support for various features.  Try to enable features
7520c794b3Sgavinm  * via inclusion in one of these bitmasks and check that at the
7620c794b3Sgavinm  * feature imlementation - that way new family support may often simply
7720c794b3Sgavinm  * simply need to update these bitmasks.
7820c794b3Sgavinm  */
7920c794b3Sgavinm 
8020c794b3Sgavinm /*
81a4e4e13fSgavinm  * Models that include an on-chip NorthBridge.
82a4e4e13fSgavinm  */
83a4e4e13fSgavinm #define	AUTHAMD_NBONCHIP(rev) \
84*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_B) || \
85*22e4c3acSKeith M Wesolowski 	chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
86a4e4e13fSgavinm 
87a4e4e13fSgavinm /*
8820c794b3Sgavinm  * Families/revisions for which we can recognise main memory ECC errors.
8920c794b3Sgavinm  */
9020c794b3Sgavinm #define	AUTHAMD_MEMECC_RECOGNISED(rev) \
91*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_B) || \
92*22e4c3acSKeith M Wesolowski 	chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
9320c794b3Sgavinm 
9420c794b3Sgavinm /*
9520c794b3Sgavinm  * Families/revisions that have an Online Spare Control Register
9620c794b3Sgavinm  */
9720c794b3Sgavinm #define	AUTHAMD_HAS_ONLINESPARECTL(rev) \
98*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_F) || \
99*22e4c3acSKeith M Wesolowski 	chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
10020c794b3Sgavinm 
10120c794b3Sgavinm /*
10225f47677Sgavinm  * Families/revisions for which we will perform NB MCA Config changes
10325f47677Sgavinm  */
10425f47677Sgavinm #define	AUTHAMD_DO_NBMCACFG(rev) \
105*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_B) || \
106*22e4c3acSKeith M Wesolowski 	chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
10725f47677Sgavinm 
10825f47677Sgavinm /*
10925f47677Sgavinm  * Families/revisions that have chip cache scrubbers.
11025f47677Sgavinm  */
11125f47677Sgavinm #define	AUTHAMD_HAS_CHIPSCRUB(rev) \
112*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_B) || \
113*22e4c3acSKeith M Wesolowski 	chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
11425f47677Sgavinm 
11525f47677Sgavinm /*
11620c794b3Sgavinm  * Families/revisions that have a NB misc register or registers -
11720c794b3Sgavinm  * evaluates to 0 if no support, otherwise the number of MC4_MISCj.
11820c794b3Sgavinm  */
11920c794b3Sgavinm #define	AUTHAMD_NBMISC_NUM(rev) \
120*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_F)? 1 : \
121*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A) ? 3 : 0))
12220c794b3Sgavinm 
12320c794b3Sgavinm /*
12420c794b3Sgavinm  * Families/revision for which we wish not to machine check for GART
12520c794b3Sgavinm  * table walk errors - bit 10 of NB CTL.
12620c794b3Sgavinm  */
12720c794b3Sgavinm #define	AUTHAMD_NOGARTTBLWLK_MC(rev) \
128*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_B) || \
129*22e4c3acSKeith M Wesolowski 	chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
13020c794b3Sgavinm 
13120c794b3Sgavinm /*
13225f47677Sgavinm  * Families/revisions that are potentially L3 capable
13325f47677Sgavinm  */
13425f47677Sgavinm #define	AUTHAMD_L3CAPABLE(rev) \
135*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
13625f47677Sgavinm 
13725f47677Sgavinm /*
1385667185bSSrihari Venkatesan  * Families/revisions that support x8 ChipKill ECC
1395667185bSSrihari Venkatesan  */
1405667185bSSrihari Venkatesan #define	AUTHAMD_SUPPORTS_X8ECC(rev) \
141*22e4c3acSKeith M Wesolowski 	(chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_D0))
1425667185bSSrihari Venkatesan 
1435667185bSSrihari Venkatesan /*
14420c794b3Sgavinm  * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED
14520c794b3Sgavinm  * revisions as:
14620c794b3Sgavinm  *
14720c794b3Sgavinm  *	- being reported by the NB
14820c794b3Sgavinm  *	- being a compound bus/interconnect error (external to chip)
14920c794b3Sgavinm  *	- having LL of LG
15020c794b3Sgavinm  *	- having II of MEM (but could still be a master/target abort)
15120c794b3Sgavinm  *	- having CECC or UECC set
15220c794b3Sgavinm  *
15320c794b3Sgavinm  * We do not check the extended error code (first nibble of the
15420c794b3Sgavinm  * model-specific error code on AMD) since this has changed from
15520c794b3Sgavinm  * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10).
15620c794b3Sgavinm  * Instead we use CECC/UECC to separate off the master/target
15720c794b3Sgavinm  * abort cases.
15820c794b3Sgavinm  *
15920c794b3Sgavinm  * We insist that the detector be the NorthBridge bank;  although
16020c794b3Sgavinm  * IC/DC can report some main memory errors, they do not capture
16120c794b3Sgavinm  * an address at sufficient resolution to be useful and the NB will
16220c794b3Sgavinm  * report most errors.
16320c794b3Sgavinm  */
16420c794b3Sgavinm #define	AUTHAMD_IS_MEMECCERR(bank, status) \
16520c794b3Sgavinm 	((bank) == AMD_MCA_BANK_NB && \
16620c794b3Sgavinm 	MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \
16720c794b3Sgavinm 	MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \
16820c794b3Sgavinm 	MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \
16920c794b3Sgavinm 	((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC)))
17020c794b3Sgavinm 
17120c794b3Sgavinm static authamd_error_disp_t authamd_memce_disp = {
17220c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD,
17320c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD_MEM_CE,
17420c794b3Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE
17520c794b3Sgavinm };
17620c794b3Sgavinm 
17720c794b3Sgavinm static authamd_error_disp_t authamd_memue_disp = {
17820c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD,
17920c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD_MEM_UE,
18020c794b3Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE
18120c794b3Sgavinm };
18220c794b3Sgavinm 
18320c794b3Sgavinm static authamd_error_disp_t authamd_ckmemce_disp = {
18420c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD,
18520c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD_CKMEM_CE,
18620c794b3Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE
18720c794b3Sgavinm };
18820c794b3Sgavinm 
18920c794b3Sgavinm static authamd_error_disp_t authamd_ckmemue_disp = {
19020c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD,
19120c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD_CKMEM_UE,
19220c794b3Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE
19320c794b3Sgavinm };
19420c794b3Sgavinm 
19520c794b3Sgavinm /*
19620c794b3Sgavinm  * We recognise GART walk errors as:
19720c794b3Sgavinm  *
19820c794b3Sgavinm  *	- being reported by the NB
19920c794b3Sgavinm  *	- being a compound TLB error
20020c794b3Sgavinm  *	- having LL of LG and TT of GEN
20120c794b3Sgavinm  *	- having UC set
20220c794b3Sgavinm  *	- possibly having PCC set (if source CPU)
20320c794b3Sgavinm  */
20420c794b3Sgavinm #define	AUTHAMD_IS_GARTERR(bank, status) \
20520c794b3Sgavinm 	((bank) == AMD_MCA_BANK_NB && \
20620c794b3Sgavinm 	MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \
20720c794b3Sgavinm 	MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \
20820c794b3Sgavinm 	MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \
20920c794b3Sgavinm 	(status) & MSR_MC_STATUS_UC)
21020c794b3Sgavinm 
21120c794b3Sgavinm static authamd_error_disp_t authamd_gart_disp = {
21220c794b3Sgavinm 	FM_EREPORT_CPU_GENAMD,			/* use generic subclass */
21320c794b3Sgavinm 	FM_EREPORT_CPU_GENADM_GARTTBLWLK,	/* use generic leafclass */
21420c794b3Sgavinm 	0					/* no additional payload */
21520c794b3Sgavinm };
21620c794b3Sgavinm 
21720c794b3Sgavinm 
2188031591dSSrihari Venkatesan static struct authamd_nodeshared *authamd_shared[AUTHAMD_MAX_NODES];
21920c794b3Sgavinm 
22020c794b3Sgavinm static int
authamd_chip_once(authamd_data_t * authamd,enum authamd_cfgonce_bitnum what)22120c794b3Sgavinm authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what)
22220c794b3Sgavinm {
2235667185bSSrihari Venkatesan 	return (atomic_set_long_excl(&authamd->amd_shared->ans_cfgonce,
22420c794b3Sgavinm 	    what) == 0 ?  B_TRUE : B_FALSE);
22520c794b3Sgavinm }
22620c794b3Sgavinm 
22720c794b3Sgavinm static void
authamd_pcicfg_write(uint_t procnodeid,uint_t func,uint_t reg,uint32_t val)2288031591dSSrihari Venkatesan authamd_pcicfg_write(uint_t procnodeid, uint_t func, uint_t reg, uint32_t val)
22920c794b3Sgavinm {
2308031591dSSrihari Venkatesan 	ASSERT(procnodeid + 24 <= 31);
23120c794b3Sgavinm 	ASSERT((func & 7) == func);
2325667185bSSrihari Venkatesan 	ASSERT((reg & 3) == 0 && reg < 4096);
23320c794b3Sgavinm 
2348031591dSSrihari Venkatesan 	cmi_pci_putl(0, procnodeid + 24, func, reg, 0, val);
23520c794b3Sgavinm }
23620c794b3Sgavinm 
23720c794b3Sgavinm static uint32_t
authamd_pcicfg_read(uint_t procnodeid,uint_t func,uint_t reg)2388031591dSSrihari Venkatesan authamd_pcicfg_read(uint_t procnodeid, uint_t func, uint_t reg)
23920c794b3Sgavinm {
2408031591dSSrihari Venkatesan 	ASSERT(procnodeid + 24 <= 31);
24120c794b3Sgavinm 	ASSERT((func & 7) == func);
2425667185bSSrihari Venkatesan 	ASSERT((reg & 3) == 0 && reg < 4096);
24320c794b3Sgavinm 
2448031591dSSrihari Venkatesan 	return (cmi_pci_getl(0, procnodeid + 24, func, reg, 0, 0));
24520c794b3Sgavinm }
24620c794b3Sgavinm 
24720c794b3Sgavinm void
authamd_bankstatus_prewrite(cmi_hdl_t hdl,authamd_data_t * authamd)24820c794b3Sgavinm authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd)
24920c794b3Sgavinm {
25020c794b3Sgavinm 	uint64_t hwcr;
25120c794b3Sgavinm 
25220c794b3Sgavinm 	if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
25320c794b3Sgavinm 		return;
25420c794b3Sgavinm 
25520c794b3Sgavinm 	authamd->amd_hwcr = hwcr;
25620c794b3Sgavinm 
25720c794b3Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
25820c794b3Sgavinm 		hwcr |= AMD_HWCR_MCI_STATUS_WREN;
25920c794b3Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
26020c794b3Sgavinm 	}
26120c794b3Sgavinm }
26220c794b3Sgavinm 
26320c794b3Sgavinm void
authamd_bankstatus_postwrite(cmi_hdl_t hdl,authamd_data_t * authamd)26420c794b3Sgavinm authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd)
26520c794b3Sgavinm {
26620c794b3Sgavinm 	uint64_t hwcr = authamd->amd_hwcr;
26720c794b3Sgavinm 
26820c794b3Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
26920c794b3Sgavinm 		hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
27020c794b3Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
27120c794b3Sgavinm 	}
27220c794b3Sgavinm }
27320c794b3Sgavinm 
27420c794b3Sgavinm /*
27520c794b3Sgavinm  * Read EccCnt repeatedly for all possible channel/chip-select combos:
27620c794b3Sgavinm  *
27720c794b3Sgavinm  *	- read sparectl register
27820c794b3Sgavinm  *	- if EccErrCntWrEn is set, clear that bit in the just-read value
27920c794b3Sgavinm  *	  and write it back to sparectl;  this *may* clobber the EccCnt
28020c794b3Sgavinm  *	  for the channel/chip-select combination currently selected, so
28120c794b3Sgavinm  *	  we leave this bit clear if we had to clear it
28220c794b3Sgavinm  *	- cycle through all channel/chip-select combinations writing each
28320c794b3Sgavinm  *	  combination to sparectl before reading the register back for
28420c794b3Sgavinm  *	  EccCnt for that combination;  since EccErrCntWrEn is clear
28520c794b3Sgavinm  *	  the writes to select what count to read will not themselves
28620c794b3Sgavinm  *	  zero any counts
28720c794b3Sgavinm  */
28820c794b3Sgavinm static int
authamd_read_ecccnt(authamd_data_t * authamd,struct authamd_logout * msl)28920c794b3Sgavinm authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl)
29020c794b3Sgavinm {
29120c794b3Sgavinm 	union mcreg_sparectl sparectl;
2925667185bSSrihari Venkatesan 	uint_t procnodeid = authamd->amd_shared->ans_procnodeid;
2935667185bSSrihari Venkatesan 	uint_t family = authamd->amd_shared->ans_family;
294*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
29520c794b3Sgavinm 	int chan, cs;
29620c794b3Sgavinm 
29720c794b3Sgavinm 	/*
29820c794b3Sgavinm 	 * Check for feature support;  this macro will test down to the
29920c794b3Sgavinm 	 * family revision number, whereafter we'll switch on family
30020c794b3Sgavinm 	 * assuming that future revisions will use the same register
30120c794b3Sgavinm 	 * format.
30220c794b3Sgavinm 	 */
30320c794b3Sgavinm 	if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) {
30420c794b3Sgavinm 		bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt));
30520c794b3Sgavinm 		return (0);
30620c794b3Sgavinm 	}
30720c794b3Sgavinm 
30820c794b3Sgavinm 	MCREG_VAL32(&sparectl) =
3098031591dSSrihari Venkatesan 	    authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL,
3108031591dSSrihari Venkatesan 	    MC_CTL_REG_SPARECTL);
31120c794b3Sgavinm 
31220c794b3Sgavinm 	switch (family) {
31320c794b3Sgavinm 	case AUTHAMD_FAMILY_F:
31420c794b3Sgavinm 		MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0;
31520c794b3Sgavinm 		break;
31620c794b3Sgavinm 
31720c794b3Sgavinm 	case AUTHAMD_FAMILY_10:
31820c794b3Sgavinm 		MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0;
31920c794b3Sgavinm 		break;
32020c794b3Sgavinm 	}
32120c794b3Sgavinm 
32220c794b3Sgavinm 	for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) {
32320c794b3Sgavinm 		switch (family) {
32420c794b3Sgavinm 		case AUTHAMD_FAMILY_F:
32520c794b3Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) =
32620c794b3Sgavinm 			    chan;
32720c794b3Sgavinm 			break;
32820c794b3Sgavinm 
32920c794b3Sgavinm 		case AUTHAMD_FAMILY_10:
33020c794b3Sgavinm 			MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) =
33120c794b3Sgavinm 			    chan;
33220c794b3Sgavinm 			break;
33320c794b3Sgavinm 		}
33420c794b3Sgavinm 
33520c794b3Sgavinm 		for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) {
33620c794b3Sgavinm 			switch (family) {
33720c794b3Sgavinm 			case AUTHAMD_FAMILY_F:
33820c794b3Sgavinm 				MCREG_FIELD_F_revFG(&sparectl,
33920c794b3Sgavinm 				    EccErrCntDramCs) = cs;
34020c794b3Sgavinm 				break;
34120c794b3Sgavinm 
34220c794b3Sgavinm 			case AUTHAMD_FAMILY_10:
34320c794b3Sgavinm 				MCREG_FIELD_10_revAB(&sparectl,
34420c794b3Sgavinm 				    EccErrCntDramCs) = cs;
34520c794b3Sgavinm 				break;
34620c794b3Sgavinm 			}
34720c794b3Sgavinm 
3488031591dSSrihari Venkatesan 			authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
34920c794b3Sgavinm 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
35020c794b3Sgavinm 
3518031591dSSrihari Venkatesan 			MCREG_VAL32(&sparectl) = authamd_pcicfg_read(procnodeid,
35220c794b3Sgavinm 			    MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
35320c794b3Sgavinm 
35420c794b3Sgavinm 			switch (family) {
35520c794b3Sgavinm 			case AUTHAMD_FAMILY_F:
35620c794b3Sgavinm 				msl->aal_eccerrcnt[chan][cs] =
35720c794b3Sgavinm 				    MCREG_FIELD_F_revFG(&sparectl, EccErrCnt);
35820c794b3Sgavinm 				break;
35920c794b3Sgavinm 			case AUTHAMD_FAMILY_10:
36020c794b3Sgavinm 				msl->aal_eccerrcnt[chan][cs] =
36120c794b3Sgavinm 				    MCREG_FIELD_10_revAB(&sparectl, EccErrCnt);
36220c794b3Sgavinm 				break;
36320c794b3Sgavinm 			}
36420c794b3Sgavinm 		}
36520c794b3Sgavinm 	}
36620c794b3Sgavinm 
36720c794b3Sgavinm 	return (1);
36820c794b3Sgavinm }
36920c794b3Sgavinm 
37020c794b3Sgavinm /*
37120c794b3Sgavinm  * Clear EccCnt for all possible channel/chip-select combos:
37220c794b3Sgavinm  *
37320c794b3Sgavinm  *	- set EccErrCntWrEn in sparectl, if necessary
37420c794b3Sgavinm  *	- write 0 to EccCnt for all channel/chip-select combinations
37520c794b3Sgavinm  *	- clear EccErrCntWrEn
37620c794b3Sgavinm  *
37720c794b3Sgavinm  * If requested also disable the interrupts taken on counter overflow
37820c794b3Sgavinm  * and on swap done.
37920c794b3Sgavinm  */
38020c794b3Sgavinm static void
authamd_clear_ecccnt(authamd_data_t * authamd,boolean_t clrint)38120c794b3Sgavinm authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint)
38220c794b3Sgavinm {
38320c794b3Sgavinm 	union mcreg_sparectl sparectl;
3845667185bSSrihari Venkatesan 	uint_t procnodeid = authamd->amd_shared->ans_procnodeid;
3855667185bSSrihari Venkatesan 	uint_t family = authamd->amd_shared->ans_family;
386*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
38720c794b3Sgavinm 	int chan, cs;
38820c794b3Sgavinm 
38920c794b3Sgavinm 	if (!AUTHAMD_HAS_ONLINESPARECTL(rev))
39020c794b3Sgavinm 		return;
39120c794b3Sgavinm 
39220c794b3Sgavinm 	MCREG_VAL32(&sparectl) =
3938031591dSSrihari Venkatesan 	    authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL,
3948031591dSSrihari Venkatesan 	    MC_CTL_REG_SPARECTL);
39520c794b3Sgavinm 
39620c794b3Sgavinm 	switch (family) {
39720c794b3Sgavinm 	case AUTHAMD_FAMILY_F:
39820c794b3Sgavinm 		MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1;
39920c794b3Sgavinm 		if (clrint) {
40020c794b3Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0;
40120c794b3Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0;
40220c794b3Sgavinm 		}
40320c794b3Sgavinm 		break;
40420c794b3Sgavinm 
40520c794b3Sgavinm 	case AUTHAMD_FAMILY_10:
40620c794b3Sgavinm 		MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 1;
40720c794b3Sgavinm 		if (clrint) {
40820c794b3Sgavinm 			MCREG_FIELD_10_revAB(&sparectl, EccErrInt) = 0;
40920c794b3Sgavinm 			MCREG_FIELD_10_revAB(&sparectl, SwapDoneInt) = 0;
41020c794b3Sgavinm 		}
41120c794b3Sgavinm 		break;
41220c794b3Sgavinm 	}
41320c794b3Sgavinm 
4148031591dSSrihari Venkatesan 	authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
41520c794b3Sgavinm 	    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
41620c794b3Sgavinm 
41720c794b3Sgavinm 	for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) {
41820c794b3Sgavinm 		switch (family) {
41920c794b3Sgavinm 		case AUTHAMD_FAMILY_F:
42020c794b3Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) =
42120c794b3Sgavinm 			    chan;
42220c794b3Sgavinm 			break;
42320c794b3Sgavinm 
42420c794b3Sgavinm 		case AUTHAMD_FAMILY_10:
42520c794b3Sgavinm 			MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) =
42620c794b3Sgavinm 			    chan;
42720c794b3Sgavinm 			break;
42820c794b3Sgavinm 		}
42920c794b3Sgavinm 
43020c794b3Sgavinm 		for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) {
43120c794b3Sgavinm 			switch (family) {
43220c794b3Sgavinm 			case AUTHAMD_FAMILY_F:
43320c794b3Sgavinm 				MCREG_FIELD_F_revFG(&sparectl,
43420c794b3Sgavinm 				    EccErrCntDramCs) = cs;
43520c794b3Sgavinm 				MCREG_FIELD_F_revFG(&sparectl,
43620c794b3Sgavinm 				    EccErrCnt) = 0;
43720c794b3Sgavinm 				break;
43820c794b3Sgavinm 
43920c794b3Sgavinm 			case AUTHAMD_FAMILY_10:
44020c794b3Sgavinm 				MCREG_FIELD_10_revAB(&sparectl,
44120c794b3Sgavinm 				    EccErrCntDramCs) = cs;
44220c794b3Sgavinm 				MCREG_FIELD_10_revAB(&sparectl,
44320c794b3Sgavinm 				    EccErrCnt) = 0;
44420c794b3Sgavinm 				break;
44520c794b3Sgavinm 			}
44620c794b3Sgavinm 
4478031591dSSrihari Venkatesan 			authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
44820c794b3Sgavinm 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
44920c794b3Sgavinm 		}
45020c794b3Sgavinm 	}
45120c794b3Sgavinm }
45220c794b3Sgavinm 
453a24e89c4SKuriakose Kuruvilla 
454a24e89c4SKuriakose Kuruvilla /*
455a24e89c4SKuriakose Kuruvilla  * Return
456a24e89c4SKuriakose Kuruvilla  *	1: supported
457a24e89c4SKuriakose Kuruvilla  *	0: unsupported
458a24e89c4SKuriakose Kuruvilla  */
459a24e89c4SKuriakose Kuruvilla static int
authamd_supported(cmi_hdl_t hdl)4608031591dSSrihari Venkatesan authamd_supported(cmi_hdl_t hdl)
461a24e89c4SKuriakose Kuruvilla {
4628031591dSSrihari Venkatesan 	uint_t family = cmi_hdl_family(hdl);
463a24e89c4SKuriakose Kuruvilla 
4648031591dSSrihari Venkatesan 	switch (family) {
4658031591dSSrihari Venkatesan 	case AUTHAMD_FAMILY_6:
4668031591dSSrihari Venkatesan 	case AUTHAMD_FAMILY_F:
4678031591dSSrihari Venkatesan 	case AUTHAMD_FAMILY_10:
468a24e89c4SKuriakose Kuruvilla 		return (1);
4698031591dSSrihari Venkatesan 	default:
470a24e89c4SKuriakose Kuruvilla 		return (0);
471a24e89c4SKuriakose Kuruvilla 	}
4728031591dSSrihari Venkatesan }
473a24e89c4SKuriakose Kuruvilla 
47420c794b3Sgavinm /*
47520c794b3Sgavinm  * cms_init entry point.
47620c794b3Sgavinm  *
47720c794b3Sgavinm  * This module provides broad model-specific support for AMD families
47820c794b3Sgavinm  * 0x6, 0xf and 0x10.  Future families will have to be evaluated once their
47920c794b3Sgavinm  * documentation is available.
48020c794b3Sgavinm  */
48120c794b3Sgavinm int
authamd_init(cmi_hdl_t hdl,void ** datap)48220c794b3Sgavinm authamd_init(cmi_hdl_t hdl, void **datap)
48320c794b3Sgavinm {
48420c794b3Sgavinm 	uint_t chipid = cmi_hdl_chipid(hdl);
4858031591dSSrihari Venkatesan 	uint_t procnodeid = cmi_hdl_procnodeid(hdl);
4868031591dSSrihari Venkatesan 	struct authamd_nodeshared *sp, *osp;
48720c794b3Sgavinm 	uint_t family = cmi_hdl_family(hdl);
488*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = cmi_hdl_chiprev(hdl);
48920c794b3Sgavinm 	authamd_data_t *authamd;
49020c794b3Sgavinm 	uint64_t cap;
49120c794b3Sgavinm 
492a24e89c4SKuriakose Kuruvilla 	if (authamd_ms_support_disable ||
4938031591dSSrihari Venkatesan 	    !authamd_supported(hdl))
49420c794b3Sgavinm 		return (ENOTSUP);
49520c794b3Sgavinm 
4967417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_MCA))
49720c794b3Sgavinm 		return (ENOTSUP);
49820c794b3Sgavinm 
49920c794b3Sgavinm 	if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS)
50020c794b3Sgavinm 		return (ENOTSUP);
50120c794b3Sgavinm 
50220c794b3Sgavinm 	if (!(cap & MCG_CAP_CTL_P))
50320c794b3Sgavinm 		return (ENOTSUP);
50420c794b3Sgavinm 
50520c794b3Sgavinm 	authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP);
50620c794b3Sgavinm 	cmi_hdl_hold(hdl);	/* release in fini */
50720c794b3Sgavinm 	authamd->amd_hdl = hdl;
50820c794b3Sgavinm 
5098031591dSSrihari Venkatesan 	if ((sp = authamd_shared[procnodeid]) == NULL) {
5108031591dSSrihari Venkatesan 		sp = kmem_zalloc(sizeof (struct authamd_nodeshared), KM_SLEEP);
5115667185bSSrihari Venkatesan 		sp->ans_chipid = chipid;
5125667185bSSrihari Venkatesan 		sp->ans_procnodeid = procnodeid;
5135667185bSSrihari Venkatesan 		sp->ans_family = family;
5145667185bSSrihari Venkatesan 		sp->ans_rev = rev;
515a3c46958Sgavinm 		membar_producer();
516a3c46958Sgavinm 
5178031591dSSrihari Venkatesan 		osp = atomic_cas_ptr(&authamd_shared[procnodeid], NULL, sp);
51820c794b3Sgavinm 		if (osp != NULL) {
5198031591dSSrihari Venkatesan 			kmem_free(sp, sizeof (struct authamd_nodeshared));
52020c794b3Sgavinm 			sp = osp;
52120c794b3Sgavinm 		}
52220c794b3Sgavinm 	}
52320c794b3Sgavinm 	authamd->amd_shared = sp;
52420c794b3Sgavinm 
52520c794b3Sgavinm 	return (0);
52620c794b3Sgavinm }
52720c794b3Sgavinm 
52820c794b3Sgavinm /*
52920c794b3Sgavinm  * cms_logout_size entry point.
53020c794b3Sgavinm  */
53120c794b3Sgavinm /*ARGSUSED*/
53220c794b3Sgavinm size_t
authamd_logout_size(cmi_hdl_t hdl)53320c794b3Sgavinm authamd_logout_size(cmi_hdl_t hdl)
53420c794b3Sgavinm {
53520c794b3Sgavinm 	return (sizeof (struct authamd_logout));
53620c794b3Sgavinm }
53720c794b3Sgavinm 
53820c794b3Sgavinm /*
53920c794b3Sgavinm  * cms_mcgctl_val entry point
54020c794b3Sgavinm  *
54120c794b3Sgavinm  * Instead of setting all bits to 1 we can set just those for the
54220c794b3Sgavinm  * error detector banks known to exist.
54320c794b3Sgavinm  */
54420c794b3Sgavinm /*ARGSUSED*/
54520c794b3Sgavinm uint64_t
authamd_mcgctl_val(cmi_hdl_t hdl,int nbanks,uint64_t proposed)54620c794b3Sgavinm authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed)
54720c794b3Sgavinm {
54820c794b3Sgavinm 	return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed);
54920c794b3Sgavinm }
55020c794b3Sgavinm 
55120c794b3Sgavinm /*
55220c794b3Sgavinm  * cms_bankctl_skipinit entry point
55320c794b3Sgavinm  *
55420c794b3Sgavinm  * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC)
55520c794b3Sgavinm  * may produce spurious machine checks.
556a4e4e13fSgavinm  *
557a4e4e13fSgavinm  * Only allow a single core to setup the NorthBridge MCi_CTL register.
55820c794b3Sgavinm  */
55920c794b3Sgavinm /*ARGSUSED*/
56020c794b3Sgavinm boolean_t
authamd_bankctl_skipinit(cmi_hdl_t hdl,int bank)56120c794b3Sgavinm authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank)
56220c794b3Sgavinm {
56320c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
564*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
56520c794b3Sgavinm 
5665667185bSSrihari Venkatesan 	if (authamd->amd_shared->ans_family == AUTHAMD_FAMILY_6)
567a4e4e13fSgavinm 		return (bank == 0 ?  B_TRUE : B_FALSE);
568a4e4e13fSgavinm 
569a4e4e13fSgavinm 	if (AUTHAMD_NBONCHIP(rev) && bank == AMD_MCA_BANK_NB) {
570a4e4e13fSgavinm 		return (authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCA) ==
571a4e4e13fSgavinm 		    B_TRUE ? B_FALSE : B_TRUE);
572a4e4e13fSgavinm 	}
573a4e4e13fSgavinm 
574a4e4e13fSgavinm 	return (B_FALSE);
57520c794b3Sgavinm }
57620c794b3Sgavinm 
57720c794b3Sgavinm /*
57820c794b3Sgavinm  * cms_bankctl_val entry point
57920c794b3Sgavinm  */
58020c794b3Sgavinm uint64_t
authamd_bankctl_val(cmi_hdl_t hdl,int bank,uint64_t proposed)58120c794b3Sgavinm authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed)
58220c794b3Sgavinm {
58320c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
584*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
58520c794b3Sgavinm 	uint64_t val = proposed;
58620c794b3Sgavinm 
58720c794b3Sgavinm 	/*
58820c794b3Sgavinm 	 * The Intel MCA says we can write all 1's to enable #MC for
58920c794b3Sgavinm 	 * all errors, and AMD docs say much the same.  But, depending
59020c794b3Sgavinm 	 * perhaps on other config registers, taking machine checks
59120c794b3Sgavinm 	 * for some errors such as GART TLB errors and master/target
59220c794b3Sgavinm 	 * aborts may be bad - they set UC and sometime also PCC, but
59320c794b3Sgavinm 	 * we should not always panic for these error types.
59420c794b3Sgavinm 	 *
59520c794b3Sgavinm 	 * Our cms_error_action entry point can suppress such panics,
59620c794b3Sgavinm 	 * however we can also use the cms_bankctl_val entry point to
59720c794b3Sgavinm 	 * veto enabling of some of the known villains in the first place.
59820c794b3Sgavinm 	 */
59920c794b3Sgavinm 	if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev))
60020c794b3Sgavinm 		val &= ~AMD_NB_EN_GARTTBLWK;
60120c794b3Sgavinm 
60220c794b3Sgavinm 	return (val);
60320c794b3Sgavinm }
60420c794b3Sgavinm 
60520c794b3Sgavinm /*
60625f47677Sgavinm  * Bits to add to NB MCA config (after watchdog config).
60725f47677Sgavinm  */
60825f47677Sgavinm uint32_t authamd_nb_mcacfg_add = AMD_NB_CFG_ADD_CMN;
60925f47677Sgavinm 
61025f47677Sgavinm /*
61125f47677Sgavinm  * Bits to remove from NB MCA config (after watchdog config)
61225f47677Sgavinm  */
61325f47677Sgavinm uint32_t authamd_nb_mcacfg_remove = AMD_NB_CFG_REMOVE_CMN;
61425f47677Sgavinm 
61525f47677Sgavinm /*
61625f47677Sgavinm  * NB Watchdog policy, and rate we use if enabling.
61725f47677Sgavinm  */
61825f47677Sgavinm enum {
61925f47677Sgavinm 	AUTHAMD_NB_WDOG_LEAVEALONE,
62025f47677Sgavinm 	AUTHAMD_NB_WDOG_DISABLE,
62125f47677Sgavinm 	AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED,
62225f47677Sgavinm 	AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE
62325f47677Sgavinm } authamd_nb_watchdog_policy = AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED;
62425f47677Sgavinm 
62525f47677Sgavinm uint32_t authamd_nb_mcacfg_wdog = AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
62625f47677Sgavinm     AMD_NB_CFG_WDOGTMRBASESEL_1MS;
62725f47677Sgavinm 
62825f47677Sgavinm /*
62925f47677Sgavinm  * Per-core cache scrubbing policy and rates.
63025f47677Sgavinm  */
63125f47677Sgavinm enum {
63225f47677Sgavinm 	AUTHAMD_SCRUB_BIOSDEFAULT,	/* leave as BIOS configured */
63325f47677Sgavinm 	AUTHAMD_SCRUB_FIXED,		/* assign our chosen rate */
63425f47677Sgavinm 	AUTHAMD_SCRUB_MAX		/* use higher of ours and BIOS rate */
63525f47677Sgavinm } authamd_scrub_policy = AUTHAMD_SCRUB_MAX;
63625f47677Sgavinm 
63725f47677Sgavinm uint32_t authamd_scrub_rate_dcache = 0xf;	/* 64K per 0.67 seconds */
63825f47677Sgavinm uint32_t authamd_scrub_rate_l2cache = 0xe;	/* 1MB per 5.3 seconds */
63925f47677Sgavinm uint32_t authamd_scrub_rate_l3cache = 0xd;	/* 1MB per 2.7 seconds */
64025f47677Sgavinm 
64125f47677Sgavinm static uint32_t
authamd_scrubrate(uint32_t osrate,uint32_t biosrate,const char * varnm)64225f47677Sgavinm authamd_scrubrate(uint32_t osrate, uint32_t biosrate, const char *varnm)
64325f47677Sgavinm {
64425f47677Sgavinm 	uint32_t rate;
64525f47677Sgavinm 
64625f47677Sgavinm 	if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) {
64725f47677Sgavinm 		cmn_err(CE_WARN, "%s is too large, resetting to 0x%x\n",
64825f47677Sgavinm 		    varnm, AMD_NB_SCRUBCTL_RATE_MAX);
64925f47677Sgavinm 		osrate = AMD_NB_SCRUBCTL_RATE_MAX;
65025f47677Sgavinm 	}
65125f47677Sgavinm 
65225f47677Sgavinm 	switch (authamd_scrub_policy) {
65325f47677Sgavinm 	case AUTHAMD_SCRUB_FIXED:
65425f47677Sgavinm 		rate = osrate;
65525f47677Sgavinm 		break;
65625f47677Sgavinm 
65725f47677Sgavinm 	default:
65825f47677Sgavinm 		cmn_err(CE_WARN, "Unknown authamd_scrub_policy %d - "
65925f47677Sgavinm 		    "using default policy of AUTHAMD_SCRUB_MAX",
66025f47677Sgavinm 		    authamd_scrub_policy);
66125f47677Sgavinm 		/*FALLTHRU*/
66225f47677Sgavinm 
66325f47677Sgavinm 	case AUTHAMD_SCRUB_MAX:
66425f47677Sgavinm 		if (osrate != 0 && biosrate != 0)
66525f47677Sgavinm 			rate = MIN(osrate, biosrate);	/* small is fast */
66625f47677Sgavinm 		else
66725f47677Sgavinm 			rate = osrate ? osrate : biosrate;
66825f47677Sgavinm 	}
66925f47677Sgavinm 
67025f47677Sgavinm 	return (rate);
67125f47677Sgavinm }
67225f47677Sgavinm 
67325f47677Sgavinm /*
67420c794b3Sgavinm  * cms_mca_init entry point.
67520c794b3Sgavinm  */
67620c794b3Sgavinm /*ARGSUSED*/
67720c794b3Sgavinm void
authamd_mca_init(cmi_hdl_t hdl,int nbanks)67820c794b3Sgavinm authamd_mca_init(cmi_hdl_t hdl, int nbanks)
67920c794b3Sgavinm {
68020c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
681*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
6825667185bSSrihari Venkatesan 	uint_t procnodeid = authamd->amd_shared->ans_procnodeid;
68320c794b3Sgavinm 
68420c794b3Sgavinm 	/*
68520c794b3Sgavinm 	 * On chips with a NB online spare control register take control
68620c794b3Sgavinm 	 * and clear ECC counts.
68720c794b3Sgavinm 	 */
68820c794b3Sgavinm 	if (AUTHAMD_HAS_ONLINESPARECTL(rev) &&
68920c794b3Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) {
69020c794b3Sgavinm 		authamd_clear_ecccnt(authamd, B_TRUE);
69120c794b3Sgavinm 	}
69220c794b3Sgavinm 
69320c794b3Sgavinm 	/*
69420c794b3Sgavinm 	 * And since we are claiming the telemetry stop the BIOS receiving
69520c794b3Sgavinm 	 * an SMI on NB threshold overflow.
69620c794b3Sgavinm 	 */
69720c794b3Sgavinm 	if (AUTHAMD_NBMISC_NUM(rev) &&
69820c794b3Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) {
69920c794b3Sgavinm 		union mcmsr_nbmisc nbm;
70020c794b3Sgavinm 		int i;
70120c794b3Sgavinm 
70220c794b3Sgavinm 		authamd_bankstatus_prewrite(hdl, authamd);
70320c794b3Sgavinm 
70420c794b3Sgavinm 		for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) {
70520c794b3Sgavinm 			if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i),
70620c794b3Sgavinm 			    (uint64_t *)&nbm) != CMI_SUCCESS)
70720c794b3Sgavinm 				continue;
70820c794b3Sgavinm 
709*22e4c3acSKeith M Wesolowski 			if (chiprev_at_least(rev,
710*22e4c3acSKeith M Wesolowski 			    X86_CHIPREV_AMD_LEGACY_F_REV_F) &&
71120c794b3Sgavinm 			    MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) &&
71220c794b3Sgavinm 			    MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) {
71320c794b3Sgavinm 				MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0;
714*22e4c3acSKeith M Wesolowski 			} else if (chiprev_at_least(rev,
715*22e4c3acSKeith M Wesolowski 			    X86_CHIPREV_AMD_LEGACY_10_REV_A) &&
71620c794b3Sgavinm 			    MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) &&
71720c794b3Sgavinm 			    MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) {
71820c794b3Sgavinm 				MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0;
71920c794b3Sgavinm 			}
72020c794b3Sgavinm 
72120c794b3Sgavinm 			(void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i),
72220c794b3Sgavinm 			    MCMSR_VAL(&nbm));
72320c794b3Sgavinm 		}
72420c794b3Sgavinm 
72520c794b3Sgavinm 		authamd_bankstatus_postwrite(hdl, authamd);
72620c794b3Sgavinm 	}
72725f47677Sgavinm 
72825f47677Sgavinm 	/*
72925f47677Sgavinm 	 * NB MCA Configuration Register.
73025f47677Sgavinm 	 */
73125f47677Sgavinm 	if (AUTHAMD_DO_NBMCACFG(rev) &&
73225f47677Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCACFG)) {
7338031591dSSrihari Venkatesan 		uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL,
73425f47677Sgavinm 		    MC_CTL_REG_NBCFG);
73525f47677Sgavinm 
73625f47677Sgavinm 		switch (authamd_nb_watchdog_policy) {
73725f47677Sgavinm 		case AUTHAMD_NB_WDOG_LEAVEALONE:
73825f47677Sgavinm 			break;
73925f47677Sgavinm 
74025f47677Sgavinm 		case AUTHAMD_NB_WDOG_DISABLE:
74125f47677Sgavinm 			val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK |
74225f47677Sgavinm 			    AMD_NB_CFG_WDOGTMRCNTSEL_MASK);
74325f47677Sgavinm 			val |= AMD_NB_CFG_WDOGTMRDIS;
74425f47677Sgavinm 			break;
74525f47677Sgavinm 
74625f47677Sgavinm 		default:
74725f47677Sgavinm 			cmn_err(CE_NOTE, "authamd_nb_watchdog_policy=%d "
74825f47677Sgavinm 			    "unrecognised, using default policy",
74925f47677Sgavinm 			    authamd_nb_watchdog_policy);
75025f47677Sgavinm 			/*FALLTHRU*/
75125f47677Sgavinm 
75225f47677Sgavinm 		case AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED:
75325f47677Sgavinm 			if (!(val & AMD_NB_CFG_WDOGTMRDIS))
75425f47677Sgavinm 				break;	/* if enabled leave rate intact */
75525f47677Sgavinm 			/*FALLTHRU*/
75625f47677Sgavinm 
75725f47677Sgavinm 		case AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE:
75825f47677Sgavinm 			val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK |
75925f47677Sgavinm 			    AMD_NB_CFG_WDOGTMRCNTSEL_MASK |
76025f47677Sgavinm 			    AMD_NB_CFG_WDOGTMRDIS);
76125f47677Sgavinm 			val |= authamd_nb_mcacfg_wdog;
76225f47677Sgavinm 			break;
76325f47677Sgavinm 		}
76425f47677Sgavinm 
76525f47677Sgavinm 		/*
76625f47677Sgavinm 		 * Bit 0 of the NB MCA Config register is reserved on family
76725f47677Sgavinm 		 * 0x10.
76825f47677Sgavinm 		 */
769*22e4c3acSKeith M Wesolowski 		if (chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_10_REV_A))
77025f47677Sgavinm 			authamd_nb_mcacfg_add &= ~AMD_NB_CFG_CPUECCERREN;
77125f47677Sgavinm 
77225f47677Sgavinm 		val &= ~authamd_nb_mcacfg_remove;
77325f47677Sgavinm 		val |= authamd_nb_mcacfg_add;
77425f47677Sgavinm 
7758031591dSSrihari Venkatesan 		authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
7768031591dSSrihari Venkatesan 		    MC_CTL_REG_NBCFG, val);
77725f47677Sgavinm 	}
77825f47677Sgavinm 
77925f47677Sgavinm 	/*
78025f47677Sgavinm 	 * Cache scrubbing.  We can't enable DRAM scrubbing since
78125f47677Sgavinm 	 * we don't know the DRAM base for this node.
78225f47677Sgavinm 	 */
78325f47677Sgavinm 	if (AUTHAMD_HAS_CHIPSCRUB(rev) &&
78425f47677Sgavinm 	    authamd_scrub_policy != AUTHAMD_SCRUB_BIOSDEFAULT &&
78525f47677Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_CACHESCRUB)) {
7868031591dSSrihari Venkatesan 		uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL,
78725f47677Sgavinm 		    MC_CTL_REG_SCRUBCTL);
78825f47677Sgavinm 		int l3cap = 0;
78925f47677Sgavinm 
79025f47677Sgavinm 		if (AUTHAMD_L3CAPABLE(rev)) {
7918031591dSSrihari Venkatesan 			l3cap = (authamd_pcicfg_read(procnodeid,
7928031591dSSrihari Venkatesan 			    MC_FUNC_MISCCTL, MC_CTL_REG_NBCAP) &
7938031591dSSrihari Venkatesan 			    MC_NBCAP_L3CAPABLE) != 0;
79425f47677Sgavinm 		}
79525f47677Sgavinm 
79625f47677Sgavinm 		authamd_scrub_rate_dcache =
79725f47677Sgavinm 		    authamd_scrubrate(authamd_scrub_rate_dcache,
79825f47677Sgavinm 		    (val & AMD_NB_SCRUBCTL_DC_MASK) >> AMD_NB_SCRUBCTL_DC_SHIFT,
79925f47677Sgavinm 		    "authamd_scrub_rate_dcache");
80025f47677Sgavinm 
80125f47677Sgavinm 		authamd_scrub_rate_l2cache =
80225f47677Sgavinm 		    authamd_scrubrate(authamd_scrub_rate_l2cache,
80325f47677Sgavinm 		    (val & AMD_NB_SCRUBCTL_L2_MASK) >> AMD_NB_SCRUBCTL_L2_SHIFT,
80425f47677Sgavinm 		    "authamd_scrub_rate_l2cache");
80525f47677Sgavinm 
80625f47677Sgavinm 		authamd_scrub_rate_l3cache = l3cap ?
80725f47677Sgavinm 		    authamd_scrubrate(authamd_scrub_rate_l3cache,
80825f47677Sgavinm 		    (val & AMD_NB_SCRUBCTL_L3_MASK) >> AMD_NB_SCRUBCTL_L3_SHIFT,
80925f47677Sgavinm 		    "authamd_scrub_rate_l3cache") : 0;
81025f47677Sgavinm 
81125f47677Sgavinm 		val = AMD_NB_MKSCRUBCTL(authamd_scrub_rate_l3cache,
81225f47677Sgavinm 		    authamd_scrub_rate_dcache, authamd_scrub_rate_l2cache,
81325f47677Sgavinm 		    val & AMD_NB_SCRUBCTL_DRAM_MASK);
81425f47677Sgavinm 
8158031591dSSrihari Venkatesan 		authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
81625f47677Sgavinm 		    MC_CTL_REG_SCRUBCTL, val);
81725f47677Sgavinm 	}
81825f47677Sgavinm 
8195667185bSSrihari Venkatesan 	/*
8205667185bSSrihari Venkatesan 	 * ECC symbol size. Defaults to 4.
8215667185bSSrihari Venkatesan 	 * Set to 8 on systems that support x8 ECC and have it enabled.
8225667185bSSrihari Venkatesan 	 */
8235667185bSSrihari Venkatesan 	if (authamd_chip_once(authamd, AUTHAMD_CFGONCE_ECCSYMSZ)) {
8245667185bSSrihari Venkatesan 		authamd->amd_shared->ans_eccsymsz = "C4";
8255667185bSSrihari Venkatesan 		if (AUTHAMD_SUPPORTS_X8ECC(rev) &&
8265667185bSSrihari Venkatesan 		    (authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL,
8275667185bSSrihari Venkatesan 		    MC_CTL_REG_EXTNBCFG) & MC_EXTNBCFG_ECCSYMSZ))
8285667185bSSrihari Venkatesan 			authamd->amd_shared->ans_eccsymsz = "C8";
8295667185bSSrihari Venkatesan 	}
83025f47677Sgavinm }
83125f47677Sgavinm 
83225f47677Sgavinm /*
83325f47677Sgavinm  * cms_poll_ownermask entry point.
83425f47677Sgavinm  */
83525f47677Sgavinm uint64_t
authamd_poll_ownermask(cmi_hdl_t hdl,hrtime_t pintvl)83625f47677Sgavinm authamd_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl)
83725f47677Sgavinm {
83825f47677Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
8395667185bSSrihari Venkatesan 	struct authamd_nodeshared *ansp = authamd->amd_shared;
84025f47677Sgavinm 	hrtime_t now = gethrtime_waitfree();
8415667185bSSrihari Venkatesan 	hrtime_t last = ansp->ans_poll_timestamp;
84225f47677Sgavinm 	int dopoll = 0;
84325f47677Sgavinm 
84425f47677Sgavinm 	if (now - last > 2 * pintvl || last == 0) {
8455667185bSSrihari Venkatesan 		ansp->ans_pollowner = hdl;
84625f47677Sgavinm 		dopoll = 1;
8475667185bSSrihari Venkatesan 	} else if (ansp->ans_pollowner == hdl) {
84825f47677Sgavinm 		dopoll = 1;
84925f47677Sgavinm 	}
85025f47677Sgavinm 
85125f47677Sgavinm 	if (dopoll)
8525667185bSSrihari Venkatesan 		ansp->ans_poll_timestamp = now;
85325f47677Sgavinm 
85425f47677Sgavinm 	return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB));
85525f47677Sgavinm 
85620c794b3Sgavinm }
85720c794b3Sgavinm 
85820c794b3Sgavinm /*
85920c794b3Sgavinm  * cms_bank_logout entry point.
86020c794b3Sgavinm  */
86120c794b3Sgavinm /*ARGSUSED*/
86220c794b3Sgavinm void
authamd_bank_logout(cmi_hdl_t hdl,int bank,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout)86320c794b3Sgavinm authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status,
86420c794b3Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
86520c794b3Sgavinm {
86620c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
86720c794b3Sgavinm 	struct authamd_logout *msl = mslogout;
868*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
86920c794b3Sgavinm 
87020c794b3Sgavinm 	if (msl == NULL)
87120c794b3Sgavinm 		return;
87220c794b3Sgavinm 
87320c794b3Sgavinm 	/*
87420c794b3Sgavinm 	 * For main memory ECC errors on revisions with an Online Spare
87520c794b3Sgavinm 	 * Control Register grab the ECC counts by channel and chip-select
87620c794b3Sgavinm 	 * and reset them to 0.
87720c794b3Sgavinm 	 */
87820c794b3Sgavinm 	if (AUTHAMD_MEMECC_RECOGNISED(rev) &&
87920c794b3Sgavinm 	    AUTHAMD_IS_MEMECCERR(bank, status) &&
88020c794b3Sgavinm 	    AUTHAMD_HAS_ONLINESPARECTL(rev)) {
88120c794b3Sgavinm 		if (authamd_read_ecccnt(authamd, msl))
88220c794b3Sgavinm 			authamd_clear_ecccnt(authamd, B_FALSE);
88320c794b3Sgavinm 	}
88420c794b3Sgavinm }
88520c794b3Sgavinm 
88620c794b3Sgavinm /*
88720c794b3Sgavinm  * cms_error_action entry point
88820c794b3Sgavinm  */
88920c794b3Sgavinm 
89020c794b3Sgavinm int authamd_forgive_uc = 0;	/* For test/debug only */
89120c794b3Sgavinm int authamd_forgive_pcc = 0;	/* For test/debug only */
89220c794b3Sgavinm int authamd_fake_poison = 0;	/* For test/debug only */
89320c794b3Sgavinm 
89420c794b3Sgavinm /*ARGSUSED*/
89520c794b3Sgavinm uint32_t
authamd_error_action(cmi_hdl_t hdl,int ismc,int bank,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout)89620c794b3Sgavinm authamd_error_action(cmi_hdl_t hdl, int ismc, int bank,
89720c794b3Sgavinm     uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
89820c794b3Sgavinm {
89920c794b3Sgavinm 	authamd_error_disp_t *disp;
90020c794b3Sgavinm 	uint32_t rv = 0;
90120c794b3Sgavinm 
90220c794b3Sgavinm 	if (authamd_forgive_uc)
90320c794b3Sgavinm 		rv |= CMS_ERRSCOPE_CLEARED_UC;
90420c794b3Sgavinm 
90520c794b3Sgavinm 	if (authamd_forgive_pcc)
90620c794b3Sgavinm 		rv |= CMS_ERRSCOPE_CURCONTEXT_OK;
90720c794b3Sgavinm 
90820c794b3Sgavinm 	if (authamd_fake_poison && status & MSR_MC_STATUS_UC)
90920c794b3Sgavinm 		rv |= CMS_ERRSCOPE_POISONED;
91020c794b3Sgavinm 
91120c794b3Sgavinm 	if (rv)
91220c794b3Sgavinm 		return (rv);
91320c794b3Sgavinm 
914c84b7bbeSAdrian Frost 	disp = authamd_disp_match(hdl, ismc, bank, status, addr, misc,
915c84b7bbeSAdrian Frost 	    mslogout);
91620c794b3Sgavinm 
91720c794b3Sgavinm 	if (disp == &authamd_gart_disp) {
91820c794b3Sgavinm 		/*
91920c794b3Sgavinm 		 * GART walk errors set UC and possibly PCC (if source CPU)
92020c794b3Sgavinm 		 * but should not be regarded as terminal.
92120c794b3Sgavinm 		 */
92220c794b3Sgavinm 		return (CMS_ERRSCOPE_IGNORE_ERR);
92320c794b3Sgavinm 	}
92420c794b3Sgavinm 
92520c794b3Sgavinm 	/*
92620c794b3Sgavinm 	 * May also want to consider master abort and target abort.  These
92720c794b3Sgavinm 	 * also set UC and PCC (if src CPU) but the requester gets -1
92820c794b3Sgavinm 	 * and I believe the IO stuff in Solaris will handle that.
92920c794b3Sgavinm 	 */
93020c794b3Sgavinm 
93120c794b3Sgavinm 	return (rv);
93220c794b3Sgavinm }
93320c794b3Sgavinm 
93420c794b3Sgavinm /*
93520c794b3Sgavinm  * cms_disp_match entry point
93620c794b3Sgavinm  */
93720c794b3Sgavinm /*ARGSUSED*/
93820c794b3Sgavinm cms_cookie_t
authamd_disp_match(cmi_hdl_t hdl,int ismc,int bank,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout)939c84b7bbeSAdrian Frost authamd_disp_match(cmi_hdl_t hdl, int ismc, int bank, uint64_t status,
94020c794b3Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
94120c794b3Sgavinm {
94220c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
94320c794b3Sgavinm 	/* uint16_t errcode = MCAX86_ERRCODE(status); */
94420c794b3Sgavinm 	uint16_t exterrcode = AMD_EXT_ERRCODE(status);
945*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = authamd->amd_shared->ans_rev;
94620c794b3Sgavinm 
94720c794b3Sgavinm 	/*
94820c794b3Sgavinm 	 * Recognise main memory ECC errors
94920c794b3Sgavinm 	 */
95020c794b3Sgavinm 	if (AUTHAMD_MEMECC_RECOGNISED(rev) &&
95120c794b3Sgavinm 	    AUTHAMD_IS_MEMECCERR(bank, status)) {
95220c794b3Sgavinm 		if (status & AMD_BANK_STAT_CECC) {
95320c794b3Sgavinm 			return (exterrcode == 0 ? &authamd_memce_disp :
95420c794b3Sgavinm 			    &authamd_ckmemce_disp);
95520c794b3Sgavinm 		} else if (status & AMD_BANK_STAT_UECC) {
95620c794b3Sgavinm 			return (exterrcode == 0 ? &authamd_memue_disp :
95720c794b3Sgavinm 			    &authamd_ckmemue_disp);
95820c794b3Sgavinm 		}
95920c794b3Sgavinm 	}
96020c794b3Sgavinm 
96120c794b3Sgavinm 	/*
96220c794b3Sgavinm 	 * Recognise GART walk errors
96320c794b3Sgavinm 	 */
96420c794b3Sgavinm 	if (AUTHAMD_NOGARTTBLWLK_MC(rev) && AUTHAMD_IS_GARTERR(bank, status))
96520c794b3Sgavinm 		return (&authamd_gart_disp);
96620c794b3Sgavinm 
96720c794b3Sgavinm 	return (NULL);
96820c794b3Sgavinm }
96920c794b3Sgavinm 
97020c794b3Sgavinm /*
97120c794b3Sgavinm  * cms_ereport_class entry point
97220c794b3Sgavinm  */
97320c794b3Sgavinm /*ARGSUSED*/
97420c794b3Sgavinm void
authamd_ereport_class(cmi_hdl_t hdl,cms_cookie_t mscookie,const char ** cpuclsp,const char ** leafclsp)97520c794b3Sgavinm authamd_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
97620c794b3Sgavinm     const char **cpuclsp, const char **leafclsp)
97720c794b3Sgavinm {
97820c794b3Sgavinm 	const authamd_error_disp_t *aed = mscookie;
97920c794b3Sgavinm 
98020c794b3Sgavinm 	if (aed == NULL)
98120c794b3Sgavinm 		return;
98220c794b3Sgavinm 
98320c794b3Sgavinm 	if (aed->aad_subclass != NULL)
98420c794b3Sgavinm 		*cpuclsp = aed->aad_subclass;
98520c794b3Sgavinm 	if (aed->aad_leafclass != NULL)
98620c794b3Sgavinm 		*leafclsp = aed->aad_leafclass;
98720c794b3Sgavinm }
98820c794b3Sgavinm 
98920c794b3Sgavinm /*ARGSUSED*/
99020c794b3Sgavinm static void
authamd_ereport_add_resource(cmi_hdl_t hdl,authamd_data_t * authamd,nvlist_t * ereport,nv_alloc_t * nva,void * mslogout)99120c794b3Sgavinm authamd_ereport_add_resource(cmi_hdl_t hdl, authamd_data_t *authamd,
99220c794b3Sgavinm     nvlist_t *ereport, nv_alloc_t *nva, void *mslogout)
99320c794b3Sgavinm {
99420c794b3Sgavinm 	nvlist_t *elems[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS];
99520c794b3Sgavinm 	uint8_t counts[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS];
99620c794b3Sgavinm 	authamd_logout_t *msl;
99720c794b3Sgavinm 	nvlist_t *nvl;
99820c794b3Sgavinm 	int nelems = 0;
9998031591dSSrihari Venkatesan 	int i, chan, cs, mc;
1000074bb90dSTom Pothier 	nvlist_t *board_list = NULL;
100120c794b3Sgavinm 
100220c794b3Sgavinm 	if ((msl = mslogout) == NULL)
100320c794b3Sgavinm 		return;
100420c794b3Sgavinm 
10058031591dSSrihari Venkatesan 	/* Assume all processors have the same number of nodes */
10065667185bSSrihari Venkatesan 	mc = authamd->amd_shared->ans_procnodeid %
10078031591dSSrihari Venkatesan 	    cpuid_get_procnodes_per_pkg(CPU);
10088031591dSSrihari Venkatesan 
100920c794b3Sgavinm 	for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) {
101020c794b3Sgavinm 		for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) {
101120c794b3Sgavinm 			if (msl->aal_eccerrcnt[chan][cs] == 0)
101220c794b3Sgavinm 				continue;
101320c794b3Sgavinm 
101420c794b3Sgavinm 			if ((nvl = fm_nvlist_create(nva)) == NULL)
101520c794b3Sgavinm 				continue;
101620c794b3Sgavinm 
101720c794b3Sgavinm 			elems[nelems] = nvl;
101820c794b3Sgavinm 			counts[nelems++] = msl->aal_eccerrcnt[chan][cs];
101920c794b3Sgavinm 
1020074bb90dSTom Pothier 			if (!x86gentopo_legacy) {
1021074bb90dSTom Pothier 				board_list = cmi_hdl_smb_bboard(hdl);
1022074bb90dSTom Pothier 				if (board_list == NULL)
1023074bb90dSTom Pothier 					continue;
1024074bb90dSTom Pothier 				fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION,
1025074bb90dSTom Pothier 				    NULL, NULL, board_list, 4,
1026074bb90dSTom Pothier 				    "chip", cmi_hdl_smb_chipid(hdl),
1027074bb90dSTom Pothier 				    "memory-controller", 0,
1028074bb90dSTom Pothier 				    "dram-channel", chan,
1029074bb90dSTom Pothier 				    "chip-select", cs);
1030074bb90dSTom Pothier 			} else {
1031074bb90dSTom Pothier 				fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION,
1032074bb90dSTom Pothier 				    NULL, NULL, 5,
103320c794b3Sgavinm 				    "motherboard", 0,
10345667185bSSrihari Venkatesan 				    "chip", authamd->amd_shared->ans_chipid,
10358031591dSSrihari Venkatesan 				    "memory-controller", mc,
103620c794b3Sgavinm 				    "dram-channel", chan,
103720c794b3Sgavinm 				    "chip-select", cs);
103820c794b3Sgavinm 			}
103920c794b3Sgavinm 		}
1040074bb90dSTom Pothier 	}
104120c794b3Sgavinm 
104220c794b3Sgavinm 	if (nelems == 0)
104320c794b3Sgavinm 		return;
104420c794b3Sgavinm 
104520c794b3Sgavinm 	fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE,
104620c794b3Sgavinm 	    DATA_TYPE_NVLIST_ARRAY, nelems, elems,
104720c794b3Sgavinm 	    NULL);
104820c794b3Sgavinm 
104920c794b3Sgavinm 	fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT,
105020c794b3Sgavinm 	    DATA_TYPE_UINT8_ARRAY, nelems, &counts[0],
105120c794b3Sgavinm 	    NULL);
105220c794b3Sgavinm 
105320c794b3Sgavinm 	for (i = 0; i < nelems; i++)
105420c794b3Sgavinm 		fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
105520c794b3Sgavinm }
105620c794b3Sgavinm 
105720c794b3Sgavinm /*
105820c794b3Sgavinm  * cms_ereport_add_logout entry point
105920c794b3Sgavinm  */
106020c794b3Sgavinm /*ARGSUSED*/
106120c794b3Sgavinm void
authamd_ereport_add_logout(cmi_hdl_t hdl,nvlist_t * ereport,nv_alloc_t * nva,int bank,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout,cms_cookie_t mscookie)106220c794b3Sgavinm authamd_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, nv_alloc_t *nva,
106320c794b3Sgavinm     int bank, uint64_t status, uint64_t addr, uint64_t misc,
106420c794b3Sgavinm     void *mslogout, cms_cookie_t mscookie)
106520c794b3Sgavinm {
106620c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
106720c794b3Sgavinm 	const authamd_error_disp_t *aed = mscookie;
106820c794b3Sgavinm 	uint64_t members;
106920c794b3Sgavinm 
107020c794b3Sgavinm 	if (aed == NULL)
107120c794b3Sgavinm 		return;
107220c794b3Sgavinm 
107320c794b3Sgavinm 	members = aed->aad_ereport_members;
107420c794b3Sgavinm 
107520c794b3Sgavinm 	if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND) {
107620c794b3Sgavinm 		fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND,
107720c794b3Sgavinm 		    DATA_TYPE_UINT16, (uint16_t)AMD_BANK_SYND(status),
107820c794b3Sgavinm 		    NULL);
107920c794b3Sgavinm 
108020c794b3Sgavinm 		if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) {
108120c794b3Sgavinm 			fm_payload_set(ereport,
108220c794b3Sgavinm 			    FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE,
108320c794b3Sgavinm 			    DATA_TYPE_STRING, "E",
108420c794b3Sgavinm 			    NULL);
108520c794b3Sgavinm 		}
108620c794b3Sgavinm 	}
108720c794b3Sgavinm 
108820c794b3Sgavinm 	if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND) {
108920c794b3Sgavinm 		fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND,
109020c794b3Sgavinm 		    DATA_TYPE_UINT16, (uint16_t)AMD_NB_STAT_CKSYND(status),
109120c794b3Sgavinm 		    NULL);
109220c794b3Sgavinm 
109320c794b3Sgavinm 		if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) {
109420c794b3Sgavinm 			fm_payload_set(ereport,
109520c794b3Sgavinm 			    FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE,
10965667185bSSrihari Venkatesan 			    DATA_TYPE_STRING, authamd->amd_shared->ans_eccsymsz,
109720c794b3Sgavinm 			    NULL);
109820c794b3Sgavinm 		}
109920c794b3Sgavinm 	}
110020c794b3Sgavinm 
110120c794b3Sgavinm 	if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE &&
110220c794b3Sgavinm 	    status & MSR_MC_STATUS_ADDRV) {
110320c794b3Sgavinm 		authamd_ereport_add_resource(hdl, authamd, ereport, nva,
110420c794b3Sgavinm 		    mslogout);
110520c794b3Sgavinm 	}
110620c794b3Sgavinm }
110720c794b3Sgavinm 
110820c794b3Sgavinm /*
110920c794b3Sgavinm  * cms_msrinject entry point
111020c794b3Sgavinm  */
111120c794b3Sgavinm cms_errno_t
authamd_msrinject(cmi_hdl_t hdl,uint_t msr,uint64_t val)111220c794b3Sgavinm authamd_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
111320c794b3Sgavinm {
111420c794b3Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
111520c794b3Sgavinm 	cms_errno_t rv = CMSERR_BADMSRWRITE;
111620c794b3Sgavinm 
111720c794b3Sgavinm 	authamd_bankstatus_prewrite(hdl, authamd);
111820c794b3Sgavinm 	if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
111920c794b3Sgavinm 		rv = CMS_SUCCESS;
112020c794b3Sgavinm 	authamd_bankstatus_postwrite(hdl, authamd);
112120c794b3Sgavinm 
112220c794b3Sgavinm 	return (rv);
112320c794b3Sgavinm }
112420c794b3Sgavinm 
1125c84b7bbeSAdrian Frost cms_api_ver_t _cms_api_version = CMS_API_VERSION_2;
112620c794b3Sgavinm 
112720c794b3Sgavinm const cms_ops_t _cms_ops = {
112820c794b3Sgavinm 	authamd_init,			/* cms_init */
112920c794b3Sgavinm 	NULL,				/* cms_post_startup */
113020c794b3Sgavinm 	NULL,				/* cms_post_mpstartup */
113120c794b3Sgavinm 	authamd_logout_size,		/* cms_logout_size */
113220c794b3Sgavinm 	authamd_mcgctl_val,		/* cms_mcgctl_val */
113320c794b3Sgavinm 	authamd_bankctl_skipinit,	/* cms_bankctl_skipinit */
113420c794b3Sgavinm 	authamd_bankctl_val,		/* cms_bankctl_val */
113520c794b3Sgavinm 	NULL,				/* cms_bankstatus_skipinit */
113620c794b3Sgavinm 	NULL,				/* cms_bankstatus_val */
113720c794b3Sgavinm 	authamd_mca_init,		/* cms_mca_init */
113825f47677Sgavinm 	authamd_poll_ownermask,		/* cms_poll_ownermask */
113920c794b3Sgavinm 	authamd_bank_logout,		/* cms_bank_logout */
114020c794b3Sgavinm 	authamd_error_action,		/* cms_error_action */
114120c794b3Sgavinm 	authamd_disp_match,		/* cms_disp_match */
114220c794b3Sgavinm 	authamd_ereport_class,		/* cms_ereport_class */
114320c794b3Sgavinm 	NULL,				/* cms_ereport_detector */
114420c794b3Sgavinm 	NULL,				/* cms_ereport_includestack */
114520c794b3Sgavinm 	authamd_ereport_add_logout,	/* cms_ereport_add_logout */
114620c794b3Sgavinm 	authamd_msrinject,		/* cms_msrinject */
114720c794b3Sgavinm 	NULL,				/* cms_fini */
114820c794b3Sgavinm };
114920c794b3Sgavinm 
115020c794b3Sgavinm static struct modlcpu modlcpu = {
115120c794b3Sgavinm 	&mod_cpuops,
115220c794b3Sgavinm 	"Generic AMD model-specific MCA"
115320c794b3Sgavinm };
115420c794b3Sgavinm 
115520c794b3Sgavinm static struct modlinkage modlinkage = {
115620c794b3Sgavinm 	MODREV_1,
115720c794b3Sgavinm 	(void *)&modlcpu,
115820c794b3Sgavinm 	NULL
115920c794b3Sgavinm };
116020c794b3Sgavinm 
116120c794b3Sgavinm int
_init(void)116220c794b3Sgavinm _init(void)
116320c794b3Sgavinm {
116420c794b3Sgavinm 	return (mod_install(&modlinkage));
116520c794b3Sgavinm }
116620c794b3Sgavinm 
116720c794b3Sgavinm int
_info(struct modinfo * modinfop)116820c794b3Sgavinm _info(struct modinfo *modinfop)
116920c794b3Sgavinm {
117020c794b3Sgavinm 	return (mod_info(&modlinkage, modinfop));
117120c794b3Sgavinm }
117220c794b3Sgavinm 
117320c794b3Sgavinm int
_fini(void)117420c794b3Sgavinm _fini(void)
117520c794b3Sgavinm {
117620c794b3Sgavinm 	return (mod_remove(&modlinkage));
117720c794b3Sgavinm }
1178