xref: /illumos-gate/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c (revision 7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fe)
1*7aec1d6eScindi /*
2*7aec1d6eScindi  * CDDL HEADER START
3*7aec1d6eScindi  *
4*7aec1d6eScindi  * The contents of this file are subject to the terms of the
5*7aec1d6eScindi  * Common Development and Distribution License, Version 1.0 only
6*7aec1d6eScindi  * (the "License").  You may not use this file except in compliance
7*7aec1d6eScindi  * with the License.
8*7aec1d6eScindi  *
9*7aec1d6eScindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7aec1d6eScindi  * or http://www.opensolaris.org/os/licensing.
11*7aec1d6eScindi  * See the License for the specific language governing permissions
12*7aec1d6eScindi  * and limitations under the License.
13*7aec1d6eScindi  *
14*7aec1d6eScindi  * When distributing Covered Code, include this CDDL HEADER in each
15*7aec1d6eScindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7aec1d6eScindi  * If applicable, add the following below this CDDL HEADER, with the
17*7aec1d6eScindi  * fields enclosed by brackets "[]" replaced with your own identifying
18*7aec1d6eScindi  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7aec1d6eScindi  *
20*7aec1d6eScindi  * CDDL HEADER END
21*7aec1d6eScindi  */
22*7aec1d6eScindi 
23*7aec1d6eScindi /*
24*7aec1d6eScindi  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25*7aec1d6eScindi  * Use is subject to license terms.
26*7aec1d6eScindi  */
27*7aec1d6eScindi 
28*7aec1d6eScindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*7aec1d6eScindi 
30*7aec1d6eScindi #include <sys/types.h>
31*7aec1d6eScindi #include <sys/regset.h>
32*7aec1d6eScindi #include <sys/privregs.h>
33*7aec1d6eScindi #include <sys/pci_impl.h>
34*7aec1d6eScindi #include <sys/cpuvar.h>
35*7aec1d6eScindi #include <sys/x86_archext.h>
36*7aec1d6eScindi #include <sys/cmn_err.h>
37*7aec1d6eScindi #include <sys/systm.h>
38*7aec1d6eScindi #include <sys/sysmacros.h>
39*7aec1d6eScindi #include <sys/chip.h>
40*7aec1d6eScindi #include <sys/cyclic.h>
41*7aec1d6eScindi #include <sys/cpu_module_impl.h>
42*7aec1d6eScindi #include <sys/pci_cfgspace_impl.h>
43*7aec1d6eScindi #include <sys/sysevent.h>
44*7aec1d6eScindi #include <sys/smbios.h>
45*7aec1d6eScindi #include <sys/mca_x86.h>
46*7aec1d6eScindi #include <sys/mca_amd.h>
47*7aec1d6eScindi #include <sys/mc.h>
48*7aec1d6eScindi #include <sys/psw.h>
49*7aec1d6eScindi #include <sys/ddi.h>
50*7aec1d6eScindi #include <sys/sunddi.h>
51*7aec1d6eScindi #include <sys/sdt.h>
52*7aec1d6eScindi #include <sys/fm/util.h>
53*7aec1d6eScindi #include <sys/fm/protocol.h>
54*7aec1d6eScindi #include <sys/fm/cpu/AMD.h>
55*7aec1d6eScindi 
56*7aec1d6eScindi #include "ao.h"
57*7aec1d6eScindi #include "ao_mca_disp.h"
58*7aec1d6eScindi 
59*7aec1d6eScindi errorq_t *ao_mca_queue;			/* machine-check ereport queue */
60*7aec1d6eScindi int ao_mca_stack_flag = 1;		/* record stack trace in ereports */
61*7aec1d6eScindi int ao_mca_smi_disable = 1;		/* attempt to disable SMI polling */
62*7aec1d6eScindi 
63*7aec1d6eScindi ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT] = {
64*7aec1d6eScindi 	{ AMD_MSR_DC_STATUS, AMD_MSR_DC_ADDR },
65*7aec1d6eScindi 	{ AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR },
66*7aec1d6eScindi 	{ AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR },
67*7aec1d6eScindi 	{ AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR },
68*7aec1d6eScindi 	{ AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR }
69*7aec1d6eScindi };
70*7aec1d6eScindi 
71*7aec1d6eScindi typedef struct ao_bank_cfg {
72*7aec1d6eScindi 	uint_t bank_ctl;
73*7aec1d6eScindi 	uint_t bank_ctl_mask;
74*7aec1d6eScindi 	uint64_t bank_ctl_init;
75*7aec1d6eScindi 	uint_t bank_status;
76*7aec1d6eScindi 	uint_t bank_addr;
77*7aec1d6eScindi } ao_bank_cfg_t;
78*7aec1d6eScindi 
79*7aec1d6eScindi static const ao_bank_cfg_t ao_bank_cfgs[] = {
80*7aec1d6eScindi 	{ AMD_MSR_DC_CTL, AMD_MSR_DC_MASK, AMD_DC_CTL_INIT, AMD_MSR_DC_STATUS,
81*7aec1d6eScindi 	    AMD_MSR_DC_ADDR },
82*7aec1d6eScindi 	{ AMD_MSR_IC_CTL, AMD_MSR_IC_MASK, AMD_IC_CTL_INIT, AMD_MSR_IC_STATUS,
83*7aec1d6eScindi 	    AMD_MSR_IC_ADDR },
84*7aec1d6eScindi 	{ AMD_MSR_BU_CTL, AMD_MSR_BU_MASK, AMD_BU_CTL_INIT, AMD_MSR_BU_STATUS,
85*7aec1d6eScindi 	    AMD_MSR_BU_ADDR },
86*7aec1d6eScindi 	{ AMD_MSR_LS_CTL, AMD_MSR_LS_MASK, AMD_LS_CTL_INIT, AMD_MSR_LS_STATUS,
87*7aec1d6eScindi 	    AMD_MSR_LS_ADDR },
88*7aec1d6eScindi 	{ AMD_MSR_NB_CTL, AMD_MSR_NB_MASK, AMD_NB_CTL_INIT, AMD_MSR_NB_STATUS,
89*7aec1d6eScindi 	    AMD_MSR_NB_ADDR }
90*7aec1d6eScindi };
91*7aec1d6eScindi 
92*7aec1d6eScindi static const ao_error_disp_t ao_disp_unknown = {
93*7aec1d6eScindi 	FM_EREPORT_CPU_AMD_UNKNOWN,
94*7aec1d6eScindi 	FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_UNKNOWN
95*7aec1d6eScindi };
96*7aec1d6eScindi 
97*7aec1d6eScindi /*
98*7aec1d6eScindi  * This is quite awful but necessary to work around x86 system vendor's view of
99*7aec1d6eScindi  * the world.  Other operating systems (you know who you are) don't understand
100*7aec1d6eScindi  * Opteron-specific error handling, so BIOS and system vendors often hide these
101*7aec1d6eScindi  * conditions from them by using SMI polling to copy out any errors from the
102*7aec1d6eScindi  * machine-check registers.  When Solaris runs on a system with this feature,
103*7aec1d6eScindi  * we want to disable the SMI polling so we can use FMA instead.  Sadly, there
104*7aec1d6eScindi  * isn't even a standard self-describing way to express the whole situation,
105*7aec1d6eScindi  * so we have to resort to hard-coded values.  This should all be changed to
106*7aec1d6eScindi  * be a self-describing vendor-specific SMBIOS structure in the future.
107*7aec1d6eScindi  */
108*7aec1d6eScindi static const struct ao_smi_disable {
109*7aec1d6eScindi 	const char *asd_sys_vendor;	/* SMB_TYPE_SYSTEM vendor prefix */
110*7aec1d6eScindi 	const char *asd_bios_vendor;	/* SMB_TYPE_BIOS vendor prefix */
111*7aec1d6eScindi 	uint32_t asd_port;		/* output port for SMI disable */
112*7aec1d6eScindi 	uint32_t asd_code;		/* output code for SMI disable */
113*7aec1d6eScindi } ao_smi_disable[] = {
114*7aec1d6eScindi 	{ "Sun Microsystems", "American Megatrends", 0x502F, 0x59 },
115*7aec1d6eScindi 	{ NULL, NULL, 0, 0 }
116*7aec1d6eScindi };
117*7aec1d6eScindi 
118*7aec1d6eScindi static int
119*7aec1d6eScindi ao_disp_match_r4(uint16_t ref, uint8_t r4)
120*7aec1d6eScindi {
121*7aec1d6eScindi 	static const uint16_t ao_r4_map[] = {
122*7aec1d6eScindi 		AO_MCA_R4_BIT_GEN,	/* AMD_ERRCODE_R4_GEN */
123*7aec1d6eScindi 		AO_MCA_R4_BIT_RD,	/* AMD_ERRCODE_R4_RD */
124*7aec1d6eScindi 		AO_MCA_R4_BIT_WR,	/* AMD_ERRCODE_R4_WR */
125*7aec1d6eScindi 		AO_MCA_R4_BIT_DRD,	/* AMD_ERRCODE_R4_DRD */
126*7aec1d6eScindi 		AO_MCA_R4_BIT_DWR,	/* AMD_ERRCODE_R4_DWR */
127*7aec1d6eScindi 		AO_MCA_R4_BIT_IRD,	/* AMD_ERRCODE_R4_IRD */
128*7aec1d6eScindi 		AO_MCA_R4_BIT_PREFETCH,	/* AMD_ERRCODE_R4_PREFETCH */
129*7aec1d6eScindi 		AO_MCA_R4_BIT_EVICT,	/* AMD_ERRCODE_R4_EVICT */
130*7aec1d6eScindi 		AO_MCA_R4_BIT_SNOOP	/* AMD_ERRCODE_R4_SNOOP */
131*7aec1d6eScindi 	};
132*7aec1d6eScindi 
133*7aec1d6eScindi 	ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t));
134*7aec1d6eScindi 
135*7aec1d6eScindi 	return ((ref & ao_r4_map[r4]) != 0);
136*7aec1d6eScindi }
137*7aec1d6eScindi 
138*7aec1d6eScindi static int
139*7aec1d6eScindi ao_disp_match_pp(uint8_t ref, uint8_t pp)
140*7aec1d6eScindi {
141*7aec1d6eScindi 	static const uint8_t ao_pp_map[] = {
142*7aec1d6eScindi 		AO_MCA_PP_BIT_SRC,	/* AMD_ERRCODE_PP_SRC */
143*7aec1d6eScindi 		AO_MCA_PP_BIT_RSP,	/* AMD_ERRCODE_PP_RSP */
144*7aec1d6eScindi 		AO_MCA_PP_BIT_OBS,	/* AMD_ERRCODE_PP_OBS */
145*7aec1d6eScindi 		AO_MCA_PP_BIT_GEN	/* AMD_ERRCODE_PP_GEN */
146*7aec1d6eScindi 	};
147*7aec1d6eScindi 
148*7aec1d6eScindi 	ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t));
149*7aec1d6eScindi 
150*7aec1d6eScindi 	return ((ref & ao_pp_map[pp]) != 0);
151*7aec1d6eScindi }
152*7aec1d6eScindi 
153*7aec1d6eScindi static int
154*7aec1d6eScindi ao_disp_match_ii(uint8_t ref, uint8_t ii)
155*7aec1d6eScindi {
156*7aec1d6eScindi 	static const uint8_t ao_ii_map[] = {
157*7aec1d6eScindi 		AO_MCA_II_BIT_MEM,	/* AMD_ERRCODE_II_MEM */
158*7aec1d6eScindi 		0,
159*7aec1d6eScindi 		AO_MCA_II_BIT_IO,	/* AMD_ERRCODE_II_IO */
160*7aec1d6eScindi 		AO_MCA_II_BIT_GEN	/* AMD_ERRCODE_II_GEN */
161*7aec1d6eScindi 	};
162*7aec1d6eScindi 
163*7aec1d6eScindi 	ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t));
164*7aec1d6eScindi 
165*7aec1d6eScindi 	return ((ref & ao_ii_map[ii]) != 0);
166*7aec1d6eScindi }
167*7aec1d6eScindi 
168*7aec1d6eScindi static uint8_t
169*7aec1d6eScindi bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
170*7aec1d6eScindi {
171*7aec1d6eScindi 	uint8_t val = (*codep & mask) >> shift;
172*7aec1d6eScindi 	*codep &= ~mask;
173*7aec1d6eScindi 	return (val);
174*7aec1d6eScindi }
175*7aec1d6eScindi 
176*7aec1d6eScindi #define	BIT_STRIP(codep, name) \
177*7aec1d6eScindi 	bit_strip(codep, AMD_ERRCODE_##name##_MASK, AMD_ERRCODE_##name##_SHIFT)
178*7aec1d6eScindi 
179*7aec1d6eScindi static int
180*7aec1d6eScindi ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status)
181*7aec1d6eScindi {
182*7aec1d6eScindi 	uint16_t code = status & AMD_ERRCODE_MASK;
183*7aec1d6eScindi 	uint8_t extcode = (status & AMD_ERREXT_MASK) >> AMD_ERREXT_SHIFT;
184*7aec1d6eScindi 	uint64_t stat_mask = aed->aed_stat_mask;
185*7aec1d6eScindi 	uint64_t stat_mask_res = aed->aed_stat_mask_res;
186*7aec1d6eScindi 
187*7aec1d6eScindi 	/*
188*7aec1d6eScindi 	 * If the bank's status register indicates overflow, then we can no
189*7aec1d6eScindi 	 * longer rely on the value of CECC: our experience with actual fault
190*7aec1d6eScindi 	 * injection has shown that multiple CE's overwriting each other shows
191*7aec1d6eScindi 	 * AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero.  This
192*7aec1d6eScindi 	 * should be clarified in a future BKDG or by the Revision Guide.
193*7aec1d6eScindi 	 */
194*7aec1d6eScindi 	if (status & AMD_BANK_STAT_OVER) {
195*7aec1d6eScindi 		stat_mask &= ~AMD_BANK_STAT_CECC;
196*7aec1d6eScindi 		stat_mask_res &= ~AMD_BANK_STAT_CECC;
197*7aec1d6eScindi 	}
198*7aec1d6eScindi 
199*7aec1d6eScindi 	if ((status & stat_mask) != stat_mask_res)
200*7aec1d6eScindi 		return (0);
201*7aec1d6eScindi 
202*7aec1d6eScindi 	/*
203*7aec1d6eScindi 	 * r4 and pp bits are stored separately, so we mask off and compare them
204*7aec1d6eScindi 	 * for the code types that use them.  Once we've taken the r4 and pp
205*7aec1d6eScindi 	 * bits out of the equation, we can directly compare the resulting code
206*7aec1d6eScindi 	 * with the one stored in the ao_error_disp_t.
207*7aec1d6eScindi 	 */
208*7aec1d6eScindi 	if (AMD_ERRCODE_ISMEM(code)) {
209*7aec1d6eScindi 		uint8_t r4 = BIT_STRIP(&code, R4);
210*7aec1d6eScindi 
211*7aec1d6eScindi 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4))
212*7aec1d6eScindi 			return (0);
213*7aec1d6eScindi 
214*7aec1d6eScindi 	} else if (AMD_ERRCODE_ISBUS(code)) {
215*7aec1d6eScindi 		uint8_t r4 = BIT_STRIP(&code, R4);
216*7aec1d6eScindi 		uint8_t pp = BIT_STRIP(&code, PP);
217*7aec1d6eScindi 		uint8_t ii = BIT_STRIP(&code, II);
218*7aec1d6eScindi 
219*7aec1d6eScindi 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) ||
220*7aec1d6eScindi 		    !ao_disp_match_pp(aed->aed_stat_pp_bits, pp) ||
221*7aec1d6eScindi 		    !ao_disp_match_ii(aed->aed_stat_ii_bits, ii))
222*7aec1d6eScindi 			return (0);
223*7aec1d6eScindi 	}
224*7aec1d6eScindi 
225*7aec1d6eScindi 	return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode);
226*7aec1d6eScindi }
227*7aec1d6eScindi 
228*7aec1d6eScindi static const ao_error_disp_t *
229*7aec1d6eScindi ao_disp_match(uint_t bankno, uint64_t status)
230*7aec1d6eScindi {
231*7aec1d6eScindi 	const ao_error_disp_t *aed;
232*7aec1d6eScindi 
233*7aec1d6eScindi 	for (aed = ao_error_disp[bankno]; aed->aed_stat_mask != 0; aed++) {
234*7aec1d6eScindi 		if (ao_disp_match_one(aed, status))
235*7aec1d6eScindi 			return (aed);
236*7aec1d6eScindi 	}
237*7aec1d6eScindi 
238*7aec1d6eScindi 	return (&ao_disp_unknown);
239*7aec1d6eScindi }
240*7aec1d6eScindi 
241*7aec1d6eScindi void
242*7aec1d6eScindi ao_pcicfg_write(uint_t chipid, uint_t func, uint_t reg, uint32_t val)
243*7aec1d6eScindi {
244*7aec1d6eScindi 	ASSERT(chipid + 24 <= 31);
245*7aec1d6eScindi 	ASSERT((func & 7) == func);
246*7aec1d6eScindi 	ASSERT((reg & 3) == 0 && reg < 256);
247*7aec1d6eScindi 
248*7aec1d6eScindi 	pci_mech1_putl(0, chipid + 24, func, reg, val);
249*7aec1d6eScindi }
250*7aec1d6eScindi 
251*7aec1d6eScindi uint32_t
252*7aec1d6eScindi ao_pcicfg_read(uint_t chipid, uint_t func, uint_t reg)
253*7aec1d6eScindi {
254*7aec1d6eScindi 	ASSERT(chipid + 24 <= 31);
255*7aec1d6eScindi 	ASSERT((func & 7) == func);
256*7aec1d6eScindi 	ASSERT((reg & 3) == 0 && reg < 256);
257*7aec1d6eScindi 
258*7aec1d6eScindi 	return (pci_mech1_getl(0, chipid + 24, func, reg));
259*7aec1d6eScindi }
260*7aec1d6eScindi 
261*7aec1d6eScindi /*
262*7aec1d6eScindi  * Setup individual bank detectors after stashing their bios settings.
263*7aec1d6eScindi  */
264*7aec1d6eScindi static void
265*7aec1d6eScindi ao_bank_cfg(ao_mca_t *mca)
266*7aec1d6eScindi {
267*7aec1d6eScindi 	ao_bios_cfg_t *bioscfg = &mca->ao_mca_bios_cfg;
268*7aec1d6eScindi 	const ao_bank_cfg_t *bankcfg = ao_bank_cfgs;
269*7aec1d6eScindi 	int i;
270*7aec1d6eScindi 
271*7aec1d6eScindi 	for (i = 0; i < AMD_MCA_BANK_COUNT; i++, bankcfg++) {
272*7aec1d6eScindi 		bioscfg->bcfg_bank_ctl[i] = rdmsr(bankcfg->bank_ctl);
273*7aec1d6eScindi 		bioscfg->bcfg_bank_mask[i] = rdmsr(bankcfg->bank_ctl_mask);
274*7aec1d6eScindi 		wrmsr(bankcfg->bank_ctl, bankcfg->bank_ctl_init);
275*7aec1d6eScindi 	}
276*7aec1d6eScindi }
277*7aec1d6eScindi 
278*7aec1d6eScindi /*
279*7aec1d6eScindi  * Bits to be added to the NorthBridge (NB) configuration register.
280*7aec1d6eScindi  * See BKDG 3.29 Section 3.6.4.2 for more information.
281*7aec1d6eScindi  */
282*7aec1d6eScindi uint32_t ao_nb_cfg_add =
283*7aec1d6eScindi     AMD_NB_CFG_NBMCATOMSTCPUEN |
284*7aec1d6eScindi     AMD_NB_CFG_DISPCICFGCPUERRRSP |
285*7aec1d6eScindi     AMD_NB_CFG_SYNCONUCECCEN |
286*7aec1d6eScindi     AMD_NB_CFG_CPUECCERREN;
287*7aec1d6eScindi 
288*7aec1d6eScindi /*
289*7aec1d6eScindi  * Bits to be cleared from the NorthBridge (NB) configuration register.
290*7aec1d6eScindi  * See BKDG 3.29 Section 3.6.4.2 for more information.
291*7aec1d6eScindi  */
292*7aec1d6eScindi uint32_t ao_nb_cfg_remove =
293*7aec1d6eScindi     AMD_NB_CFG_IORDDATERREN |
294*7aec1d6eScindi     AMD_NB_CFG_SYNCONANYERREN |
295*7aec1d6eScindi     AMD_NB_CFG_SYNCONWDOGEN |
296*7aec1d6eScindi     AMD_NB_CFG_IOERRDIS |
297*7aec1d6eScindi     AMD_NB_CFG_IOMSTABORTDIS |
298*7aec1d6eScindi     AMD_NB_CFG_SYNCPKTPROPDIS |
299*7aec1d6eScindi     AMD_NB_CFG_SYNCPKTGENDIS;
300*7aec1d6eScindi 
301*7aec1d6eScindi /*
302*7aec1d6eScindi  * Bits to be used if we configure the NorthBridge (NB) Watchdog.  The watchdog
303*7aec1d6eScindi  * triggers a machine check exception when no response to an NB system access
304*7aec1d6eScindi  * occurs within a specified time interval.  If the BIOS (i.e. platform design)
305*7aec1d6eScindi  * has enabled the watchdog, we leave its rate alone.  If the BIOS has not
306*7aec1d6eScindi  * enabled the watchdog, we enable it and set the rate to one specified below.
307*7aec1d6eScindi  * To disable the watchdog, add the AMD_NB_CFG_WDOGTMRDIS bit to ao_nb_cfg_add.
308*7aec1d6eScindi  */
309*7aec1d6eScindi uint32_t ao_nb_cfg_wdog =
310*7aec1d6eScindi     AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
311*7aec1d6eScindi     AMD_NB_CFG_WDOGTMRBASESEL_1MS;
312*7aec1d6eScindi 
313*7aec1d6eScindi static void
314*7aec1d6eScindi ao_nb_cfg(ao_mca_t *mca)
315*7aec1d6eScindi {
316*7aec1d6eScindi 	uint_t chipid = chip_plat_get_chipid(CPU);
317*7aec1d6eScindi 	uint32_t val;
318*7aec1d6eScindi 
319*7aec1d6eScindi 	if (chip_plat_get_clogid(CPU) != 0)
320*7aec1d6eScindi 		return; /* only configure NB once per CPU */
321*7aec1d6eScindi 
322*7aec1d6eScindi 	/*
323*7aec1d6eScindi 	 * Read the NorthBridge (NB) configuration register in PCI space,
324*7aec1d6eScindi 	 * modify the settings accordingly, and store the new value back.
325*7aec1d6eScindi 	 */
326*7aec1d6eScindi 	mca->ao_mca_bios_cfg.bcfg_nb_cfg = val =
327*7aec1d6eScindi 	    ao_pcicfg_read(chipid, AMD_NB_FUNC, AMD_NB_REG_CFG);
328*7aec1d6eScindi 
329*7aec1d6eScindi 	/*
330*7aec1d6eScindi 	 * If the watchdog was disabled, enable it according to the policy
331*7aec1d6eScindi 	 * described above.  Then apply the ao_nb_cfg_[add|remove] masks.
332*7aec1d6eScindi 	 */
333*7aec1d6eScindi 	if (val & AMD_NB_CFG_WDOGTMRDIS) {
334*7aec1d6eScindi 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
335*7aec1d6eScindi 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
336*7aec1d6eScindi 		val &= ~AMD_NB_CFG_WDOGTMRDIS;
337*7aec1d6eScindi 		val |= ao_nb_cfg_wdog;
338*7aec1d6eScindi 	}
339*7aec1d6eScindi 
340*7aec1d6eScindi 	val &= ~ao_nb_cfg_remove;
341*7aec1d6eScindi 	val |= ao_nb_cfg_add;
342*7aec1d6eScindi 
343*7aec1d6eScindi 	ao_pcicfg_write(chipid, AMD_NB_FUNC, AMD_NB_REG_CFG, val);
344*7aec1d6eScindi }
345*7aec1d6eScindi 
346*7aec1d6eScindi /*
347*7aec1d6eScindi  * Capture the machine-check exception state into our per-CPU logout area, and
348*7aec1d6eScindi  * dispatch a copy of the logout area to our error queue for ereport creation.
349*7aec1d6eScindi  * If 'rp' is non-NULL, we're being called from trap context; otherwise we're
350*7aec1d6eScindi  * being polled or poked by the injector.  We return the number of errors
351*7aec1d6eScindi  * found through 'np', and a boolean indicating whether the error is fatal.
352*7aec1d6eScindi  * The caller is expected to call fm_panic() if we return fatal (non-zero).
353*7aec1d6eScindi  */
354*7aec1d6eScindi int
355*7aec1d6eScindi ao_mca_logout(ao_cpu_logout_t *acl, struct regs *rp, int *np)
356*7aec1d6eScindi {
357*7aec1d6eScindi 	int i, fatal, n = 0;
358*7aec1d6eScindi 
359*7aec1d6eScindi 	acl->acl_timestamp = gethrtime_waitfree();
360*7aec1d6eScindi 	acl->acl_mcg_status = rdmsr(IA32_MSR_MCG_STATUS);
361*7aec1d6eScindi 	acl->acl_ip = rp ? rp->r_pc : 0;
362*7aec1d6eScindi 	acl->acl_flags = 0;
363*7aec1d6eScindi 
364*7aec1d6eScindi 	/*
365*7aec1d6eScindi 	 * Iterate over the banks of machine-check registers, read the address
366*7aec1d6eScindi 	 * and status registers into the logout area, and clear them as we go.
367*7aec1d6eScindi 	 */
368*7aec1d6eScindi 	for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
369*7aec1d6eScindi 		ao_bank_logout_t *abl = &acl->acl_banks[i];
370*7aec1d6eScindi 
371*7aec1d6eScindi 		abl->abl_addr = rdmsr(ao_bank_regs[i].abr_addr);
372*7aec1d6eScindi 		abl->abl_status = rdmsr(ao_bank_regs[i].abr_status);
373*7aec1d6eScindi 
374*7aec1d6eScindi 		if (abl->abl_status & AMD_BANK_STAT_VALID)
375*7aec1d6eScindi 			wrmsr(ao_bank_regs[i].abr_status, 0);
376*7aec1d6eScindi 	}
377*7aec1d6eScindi 
378*7aec1d6eScindi 	if (rp == NULL || !USERMODE(rp->r_cs))
379*7aec1d6eScindi 		acl->acl_flags |= AO_ACL_F_PRIV;
380*7aec1d6eScindi 
381*7aec1d6eScindi 	if (ao_mca_stack_flag)
382*7aec1d6eScindi 		acl->acl_stackdepth = getpcstack(acl->acl_stack, FM_STK_DEPTH);
383*7aec1d6eScindi 	else
384*7aec1d6eScindi 		acl->acl_stackdepth = 0;
385*7aec1d6eScindi 
386*7aec1d6eScindi 	/*
387*7aec1d6eScindi 	 * Clear MCG_STATUS, indicating that machine-check trap processing is
388*7aec1d6eScindi 	 * complete.  Once we do this, another machine-check trap can occur.
389*7aec1d6eScindi 	 */
390*7aec1d6eScindi 	wrmsr(IA32_MSR_MCG_STATUS, 0);
391*7aec1d6eScindi 
392*7aec1d6eScindi 	/*
393*7aec1d6eScindi 	 * If we took a machine-check trap, then the error is fatal if the
394*7aec1d6eScindi 	 * return instruction pointer is not valid in the global register.
395*7aec1d6eScindi 	 */
396*7aec1d6eScindi 	fatal = rp != NULL && !(acl->acl_mcg_status & MCG_STATUS_RIPV);
397*7aec1d6eScindi 
398*7aec1d6eScindi 	/*
399*7aec1d6eScindi 	 * Now iterate over the saved logout area, determining whether the
400*7aec1d6eScindi 	 * error that we saw is fatal or not based upon our dispositions
401*7aec1d6eScindi 	 * and the hardware's indicators of whether or not we can resume.
402*7aec1d6eScindi 	 */
403*7aec1d6eScindi 	for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
404*7aec1d6eScindi 		ao_bank_logout_t *abl = &acl->acl_banks[i];
405*7aec1d6eScindi 		const ao_error_disp_t *aed;
406*7aec1d6eScindi 
407*7aec1d6eScindi 		if (!(abl->abl_status & AMD_BANK_STAT_VALID))
408*7aec1d6eScindi 			continue;
409*7aec1d6eScindi 
410*7aec1d6eScindi 		aed = ao_disp_match(i, abl->abl_status);
411*7aec1d6eScindi 		fatal |= (aed->aed_panic_when != AO_AED_PANIC_NEVER);
412*7aec1d6eScindi 
413*7aec1d6eScindi 		/*
414*7aec1d6eScindi 		 * If we are taking a machine-check exception and the overflow
415*7aec1d6eScindi 		 * bit is set or our context is corrupt, then we must die.
416*7aec1d6eScindi 		 * NOTE: This code assumes that if the overflow bit is set and
417*7aec1d6eScindi 		 * we didn't take a #mc exception (i.e. the poller found it),
418*7aec1d6eScindi 		 * then multiple correctable errors overwrote each other.
419*7aec1d6eScindi 		 * This will need to change if we eventually use the Opteron
420*7aec1d6eScindi 		 * Rev E exception mechanism for detecting correctable errors.
421*7aec1d6eScindi 		 */
422*7aec1d6eScindi 		if (rp != NULL && (abl->abl_status &
423*7aec1d6eScindi 		    (AMD_BANK_STAT_OVER | AMD_BANK_STAT_PCC)))
424*7aec1d6eScindi 			fatal = 1;
425*7aec1d6eScindi 
426*7aec1d6eScindi 		/*
427*7aec1d6eScindi 		 * If we are taking a machine-check exception and we don't
428*7aec1d6eScindi 		 * recognize the error case at all, then assume it's fatal.
429*7aec1d6eScindi 		 * This will need to change if we eventually use the Opteron
430*7aec1d6eScindi 		 * Rev E exception mechanism for detecting correctable errors.
431*7aec1d6eScindi 		 */
432*7aec1d6eScindi 		if (rp != NULL && aed == &ao_disp_unknown)
433*7aec1d6eScindi 			fatal = 1;
434*7aec1d6eScindi 
435*7aec1d6eScindi 		n++;
436*7aec1d6eScindi 	}
437*7aec1d6eScindi 
438*7aec1d6eScindi 	if (n > 0) {
439*7aec1d6eScindi 		errorq_dispatch(ao_mca_queue, acl, sizeof (ao_cpu_logout_t),
440*7aec1d6eScindi 		    fatal && cmi_panic_on_uncorrectable_error ?
441*7aec1d6eScindi 		    ERRORQ_SYNC : ERRORQ_ASYNC);
442*7aec1d6eScindi 	}
443*7aec1d6eScindi 
444*7aec1d6eScindi 	if (np != NULL)
445*7aec1d6eScindi 		*np = n; /* return number of errors found to caller */
446*7aec1d6eScindi 
447*7aec1d6eScindi 	return (fatal);
448*7aec1d6eScindi }
449*7aec1d6eScindi 
450*7aec1d6eScindi static uint_t
451*7aec1d6eScindi ao_ereport_synd(ao_mca_t *mca,
452*7aec1d6eScindi     const ao_bank_logout_t *abl, uint_t *typep, int is_nb)
453*7aec1d6eScindi {
454*7aec1d6eScindi 	if (is_nb) {
455*7aec1d6eScindi 		if ((mca->ao_mca_bios_cfg.bcfg_nb_cfg &
456*7aec1d6eScindi 		    AMD_NB_CFG_CHIPKILLECCEN) != 0) {
457*7aec1d6eScindi 			*typep = AMD_SYNDTYPE_CHIPKILL;
458*7aec1d6eScindi 			return (AMD_NB_STAT_CKSYND(abl->abl_status));
459*7aec1d6eScindi 		} else {
460*7aec1d6eScindi 			*typep = AMD_SYNDTYPE_ECC;
461*7aec1d6eScindi 			return (AMD_BANK_SYND(abl->abl_status));
462*7aec1d6eScindi 		}
463*7aec1d6eScindi 	} else {
464*7aec1d6eScindi 		*typep = AMD_SYNDTYPE_ECC;
465*7aec1d6eScindi 		return (AMD_BANK_SYND(abl->abl_status));
466*7aec1d6eScindi 	}
467*7aec1d6eScindi }
468*7aec1d6eScindi 
469*7aec1d6eScindi static void
470*7aec1d6eScindi ao_ereport_create_resource_elem(nvlist_t **nvlp, nv_alloc_t *nva,
471*7aec1d6eScindi     mc_unum_t *unump, int dimmnum)
472*7aec1d6eScindi {
473*7aec1d6eScindi 	nvlist_t *snvl;
474*7aec1d6eScindi 	*nvlp = fm_nvlist_create(nva);		/* freed by caller */
475*7aec1d6eScindi 
476*7aec1d6eScindi 	snvl = fm_nvlist_create(nva);
477*7aec1d6eScindi 
478*7aec1d6eScindi 	(void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
479*7aec1d6eScindi 	    unump->unum_offset);
480*7aec1d6eScindi 
481*7aec1d6eScindi 	fm_fmri_hc_set(*nvlp, FM_HC_SCHEME_VERSION, NULL, snvl, 4,
482*7aec1d6eScindi 	    "motherboard", unump->unum_board,
483*7aec1d6eScindi 	    "chip", unump->unum_chip,
484*7aec1d6eScindi 	    "memory-controller", unump->unum_mc,
485*7aec1d6eScindi 	    "dimm", unump->unum_dimms[dimmnum]);
486*7aec1d6eScindi 
487*7aec1d6eScindi 	fm_nvlist_destroy(snvl, FM_NVA_FREE);
488*7aec1d6eScindi }
489*7aec1d6eScindi 
490*7aec1d6eScindi static void
491*7aec1d6eScindi ao_ereport_add_resource(nvlist_t *payload, nv_alloc_t *nva, mc_unum_t *unump)
492*7aec1d6eScindi {
493*7aec1d6eScindi 
494*7aec1d6eScindi 	nvlist_t *elems[MC_UNUM_NDIMM];
495*7aec1d6eScindi 	int nelems = 0;
496*7aec1d6eScindi 	int i;
497*7aec1d6eScindi 
498*7aec1d6eScindi 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
499*7aec1d6eScindi 		if (unump->unum_dimms[i] == -1)
500*7aec1d6eScindi 			break;
501*7aec1d6eScindi 		ao_ereport_create_resource_elem(&elems[nelems++], nva,
502*7aec1d6eScindi 		    unump, i);
503*7aec1d6eScindi 	}
504*7aec1d6eScindi 
505*7aec1d6eScindi 	fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
506*7aec1d6eScindi 	    DATA_TYPE_NVLIST_ARRAY, nelems, elems);
507*7aec1d6eScindi 
508*7aec1d6eScindi 	for (i = 0; i < nelems; i++)
509*7aec1d6eScindi 		fm_nvlist_destroy(elems[i], FM_NVA_FREE);
510*7aec1d6eScindi }
511*7aec1d6eScindi 
512*7aec1d6eScindi static void
513*7aec1d6eScindi ao_ereport_add_logout(ao_data_t *ao, nvlist_t *payload, nv_alloc_t *nva,
514*7aec1d6eScindi     const ao_cpu_logout_t *acl, uint_t bankno, const ao_error_disp_t *aed)
515*7aec1d6eScindi {
516*7aec1d6eScindi 	uint64_t members = aed->aed_ereport_members;
517*7aec1d6eScindi 	ao_mca_t *mca = &ao->ao_mca;
518*7aec1d6eScindi 	const ao_bank_logout_t *abl = &acl->acl_banks[bankno];
519*7aec1d6eScindi 	uint_t synd, syndtype;
520*7aec1d6eScindi 
521*7aec1d6eScindi 	synd = ao_ereport_synd(mca, abl, &syndtype, bankno == AMD_MCA_BANK_NB);
522*7aec1d6eScindi 
523*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_STAT) {
524*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_STAT,
525*7aec1d6eScindi 		    DATA_TYPE_UINT64, abl->abl_status, NULL);
526*7aec1d6eScindi 	}
527*7aec1d6eScindi 
528*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_NUM) {
529*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_NUM,
530*7aec1d6eScindi 		    DATA_TYPE_UINT8, bankno, NULL);
531*7aec1d6eScindi 	}
532*7aec1d6eScindi 
533*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_ADDR) {
534*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ADDR,
535*7aec1d6eScindi 		    DATA_TYPE_UINT64, abl->abl_addr, NULL);
536*7aec1d6eScindi 	}
537*7aec1d6eScindi 
538*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_ADDR_VALID) {
539*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ADDR_VALID,
540*7aec1d6eScindi 		    DATA_TYPE_BOOLEAN_VALUE, (abl->abl_status &
541*7aec1d6eScindi 		    AMD_BANK_STAT_ADDRV) ? B_TRUE : B_FALSE);
542*7aec1d6eScindi 	}
543*7aec1d6eScindi 
544*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
545*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SYND,
546*7aec1d6eScindi 		    DATA_TYPE_UINT16, synd, NULL);
547*7aec1d6eScindi 	}
548*7aec1d6eScindi 
549*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) {
550*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE,
551*7aec1d6eScindi 		    DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ?
552*7aec1d6eScindi 		    "C" : "E"), NULL);
553*7aec1d6eScindi 	}
554*7aec1d6eScindi 
555*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_IP) {
556*7aec1d6eScindi 		uint64_t ip = (acl->acl_mcg_status & MCG_STATUS_EIPV) ?
557*7aec1d6eScindi 		    acl->acl_ip : 0;
558*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_IP,
559*7aec1d6eScindi 		    DATA_TYPE_UINT64, ip, NULL);
560*7aec1d6eScindi 	}
561*7aec1d6eScindi 
562*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_PRIV) {
563*7aec1d6eScindi 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PRIV,
564*7aec1d6eScindi 		    DATA_TYPE_BOOLEAN_VALUE, (acl->acl_flags & AO_ACL_F_PRIV) ?
565*7aec1d6eScindi 		    B_TRUE : B_FALSE, NULL);
566*7aec1d6eScindi 	}
567*7aec1d6eScindi 
568*7aec1d6eScindi 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) {
569*7aec1d6eScindi 		mc_unum_t unum;
570*7aec1d6eScindi 		int addrvalid;
571*7aec1d6eScindi 
572*7aec1d6eScindi 		addrvalid = (members & FM_EREPORT_PAYLOAD_FLAG_ADDR) &&
573*7aec1d6eScindi 		    (members & FM_EREPORT_PAYLOAD_FLAG_ADDR_VALID) &&
574*7aec1d6eScindi 		    (abl->abl_status & AMD_BANK_STAT_ADDRV);
575*7aec1d6eScindi 
576*7aec1d6eScindi 		if (addrvalid && ao_mc_patounum(ao, abl->abl_addr, synd,
577*7aec1d6eScindi 		    syndtype, &unum))
578*7aec1d6eScindi 			ao_ereport_add_resource(payload, nva, &unum);
579*7aec1d6eScindi 	}
580*7aec1d6eScindi 
581*7aec1d6eScindi 	if (ao_mca_stack_flag && members & FM_EREPORT_PAYLOAD_FLAG_STACK) {
582*7aec1d6eScindi 		fm_payload_stack_add(payload, acl->acl_stack,
583*7aec1d6eScindi 		    acl->acl_stackdepth);
584*7aec1d6eScindi 	}
585*7aec1d6eScindi }
586*7aec1d6eScindi 
587*7aec1d6eScindi static void
588*7aec1d6eScindi ao_ereport_post(const ao_cpu_logout_t *acl,
589*7aec1d6eScindi     int bankno, const ao_error_disp_t *aed)
590*7aec1d6eScindi {
591*7aec1d6eScindi 	ao_data_t *ao = acl->acl_ao;
592*7aec1d6eScindi 	errorq_elem_t *eqep;
593*7aec1d6eScindi 	nvlist_t *ereport, *detector;
594*7aec1d6eScindi 	nv_alloc_t *nva = NULL;
595*7aec1d6eScindi 	char buf[FM_MAX_CLASS];
596*7aec1d6eScindi 
597*7aec1d6eScindi 	if (panicstr) {
598*7aec1d6eScindi 		if ((eqep = errorq_reserve(ereport_errorq)) == NULL)
599*7aec1d6eScindi 			return;
600*7aec1d6eScindi 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
601*7aec1d6eScindi 		nva = errorq_elem_nva(ereport_errorq, eqep);
602*7aec1d6eScindi 	} else {
603*7aec1d6eScindi 		ereport = fm_nvlist_create(nva);
604*7aec1d6eScindi 	}
605*7aec1d6eScindi 
606*7aec1d6eScindi 	/*
607*7aec1d6eScindi 	 * Create the scheme "cpu" FMRI
608*7aec1d6eScindi 	 */
609*7aec1d6eScindi 	detector = ao_fmri_create(ao, nva);
610*7aec1d6eScindi 
611*7aec1d6eScindi 	/*
612*7aec1d6eScindi 	 * Encode all the common data into the ereport.
613*7aec1d6eScindi 	 */
614*7aec1d6eScindi 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s.%s",
615*7aec1d6eScindi 	    FM_ERROR_CPU, "amd", aed->aed_class);
616*7aec1d6eScindi 
617*7aec1d6eScindi 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
618*7aec1d6eScindi 	    fm_ena_generate_cpu(acl->acl_timestamp, ao->ao_cpu->cpu_id,
619*7aec1d6eScindi 	    FM_ENA_FMT1), detector, NULL);
620*7aec1d6eScindi 
621*7aec1d6eScindi 	/*
622*7aec1d6eScindi 	 * Encode the error-specific data that was saved in the logout area.
623*7aec1d6eScindi 	 */
624*7aec1d6eScindi 	ao_ereport_add_logout(ao, ereport, nva, acl, bankno, aed);
625*7aec1d6eScindi 
626*7aec1d6eScindi 	if (panicstr) {
627*7aec1d6eScindi 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
628*7aec1d6eScindi 	} else {
629*7aec1d6eScindi 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
630*7aec1d6eScindi 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
631*7aec1d6eScindi 		fm_nvlist_destroy(detector, FM_NVA_FREE);
632*7aec1d6eScindi 	}
633*7aec1d6eScindi }
634*7aec1d6eScindi 
635*7aec1d6eScindi /*ARGSUSED*/
636*7aec1d6eScindi void
637*7aec1d6eScindi ao_mca_drain(void *ignored, const void *data, const errorq_elem_t *eqe)
638*7aec1d6eScindi {
639*7aec1d6eScindi 	const ao_cpu_logout_t *acl = data;
640*7aec1d6eScindi 	int i;
641*7aec1d6eScindi 
642*7aec1d6eScindi 	for (i = 0; i < AMD_MCA_BANK_COUNT; i++) {
643*7aec1d6eScindi 		const ao_bank_logout_t *abl = &acl->acl_banks[i];
644*7aec1d6eScindi 		const ao_error_disp_t *aed;
645*7aec1d6eScindi 
646*7aec1d6eScindi 		if (abl->abl_status & AMD_BANK_STAT_VALID) {
647*7aec1d6eScindi 			aed = ao_disp_match(i, abl->abl_status);
648*7aec1d6eScindi 			ao_ereport_post(acl, i, aed);
649*7aec1d6eScindi 		}
650*7aec1d6eScindi 	}
651*7aec1d6eScindi }
652*7aec1d6eScindi 
653*7aec1d6eScindi int
654*7aec1d6eScindi ao_mca_trap(void *data, struct regs *rp)
655*7aec1d6eScindi {
656*7aec1d6eScindi 	ao_data_t *ao = data;
657*7aec1d6eScindi 	ao_mca_t *mca = &ao->ao_mca;
658*7aec1d6eScindi 	ao_cpu_logout_t *acl = &mca->ao_mca_logout[AO_MCA_LOGOUT_EXCEPTION];
659*7aec1d6eScindi 	return (ao_mca_logout(acl, rp, NULL));
660*7aec1d6eScindi }
661*7aec1d6eScindi 
662*7aec1d6eScindi /*ARGSUSED*/
663*7aec1d6eScindi int
664*7aec1d6eScindi ao_mca_inject(void *data, cmi_mca_regs_t *regs, uint_t nregs)
665*7aec1d6eScindi {
666*7aec1d6eScindi 	uint64_t hwcr, oldhwcr;
667*7aec1d6eScindi 	int i;
668*7aec1d6eScindi 
669*7aec1d6eScindi 	oldhwcr = rdmsr(MSR_AMD_HWCR);
670*7aec1d6eScindi 	hwcr = oldhwcr | AMD_HWCR_MCI_STATUS_WREN;
671*7aec1d6eScindi 	wrmsr(MSR_AMD_HWCR, hwcr);
672*7aec1d6eScindi 
673*7aec1d6eScindi 	for (i = 0; i < nregs; i++)
674*7aec1d6eScindi 		wrmsr(regs[i].cmr_msrnum, regs[i].cmr_msrval);
675*7aec1d6eScindi 
676*7aec1d6eScindi 	wrmsr(MSR_AMD_HWCR, oldhwcr);
677*7aec1d6eScindi 	return (0);
678*7aec1d6eScindi }
679*7aec1d6eScindi 
680*7aec1d6eScindi void
681*7aec1d6eScindi ao_mca_init(void *data)
682*7aec1d6eScindi {
683*7aec1d6eScindi 	ao_data_t *ao = data;
684*7aec1d6eScindi 	ao_mca_t *mca = &ao->ao_mca;
685*7aec1d6eScindi 	uint64_t cap;
686*7aec1d6eScindi 	int i;
687*7aec1d6eScindi 
688*7aec1d6eScindi 	ao_mca_poll_init(mca);
689*7aec1d6eScindi 
690*7aec1d6eScindi 	ASSERT(x86_feature & X86_MCA);
691*7aec1d6eScindi 	cap = rdmsr(IA32_MSR_MCG_CAP);
692*7aec1d6eScindi 	ASSERT(cap & MCG_CAP_CTL_P);
693*7aec1d6eScindi 
694*7aec1d6eScindi 	/*
695*7aec1d6eScindi 	 * If the hardware's bank count is different than what we expect, then
696*7aec1d6eScindi 	 * we're running on some Opteron variant that we don't understand yet.
697*7aec1d6eScindi 	 */
698*7aec1d6eScindi 	if ((cap & MCG_CAP_COUNT_MASK) != AMD_MCA_BANK_COUNT) {
699*7aec1d6eScindi 		cmn_err(CE_WARN, "CPU %d has %llu MCA banks; expected %u: "
700*7aec1d6eScindi 		    "disabling MCA on this CPU", ao->ao_cpu->cpu_id,
701*7aec1d6eScindi 		    (u_longlong_t)cap & MCG_CAP_COUNT_MASK, AMD_MCA_BANK_COUNT);
702*7aec1d6eScindi 		return;
703*7aec1d6eScindi 	}
704*7aec1d6eScindi 
705*7aec1d6eScindi 	/*
706*7aec1d6eScindi 	 * Configure the logout areas.  We preset every logout area's acl_ao
707*7aec1d6eScindi 	 * pointer to refer back to our per-CPU state for errorq drain usage.
708*7aec1d6eScindi 	 */
709*7aec1d6eScindi 	for (i = 0; i < AO_MCA_LOGOUT_NUM; i++)
710*7aec1d6eScindi 		mca->ao_mca_logout[i].acl_ao = ao;
711*7aec1d6eScindi 
712*7aec1d6eScindi 	ao_bank_cfg(mca);
713*7aec1d6eScindi 	ao_nb_cfg(mca);
714*7aec1d6eScindi 
715*7aec1d6eScindi 	wrmsr(IA32_MSR_MCG_CTL, AMD_MCG_EN_ALL);
716*7aec1d6eScindi 
717*7aec1d6eScindi 	/*
718*7aec1d6eScindi 	 * Throw away all existing bank state.  We do this because some BIOSes,
719*7aec1d6eScindi 	 * perhaps during POST, do things to the machine that cause MCA state
720*7aec1d6eScindi 	 * to be updated.  If we interpret this state as an actual error, we
721*7aec1d6eScindi 	 * may end up indicting something that's not actually broken.
722*7aec1d6eScindi 	 */
723*7aec1d6eScindi 	for (i = 0; i < sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfg_t); i++)
724*7aec1d6eScindi 		wrmsr(ao_bank_cfgs[i].bank_status, 0ULL);
725*7aec1d6eScindi 
726*7aec1d6eScindi 	wrmsr(IA32_MSR_MCG_STATUS, 0ULL);
727*7aec1d6eScindi 	membar_producer();
728*7aec1d6eScindi 
729*7aec1d6eScindi 	setcr4(getcr4() | CR4_MCE); /* enable #mc exceptions */
730*7aec1d6eScindi }
731*7aec1d6eScindi 
732*7aec1d6eScindi /*ARGSUSED*/
733*7aec1d6eScindi void
734*7aec1d6eScindi ao_mca_post_init(void *data)
735*7aec1d6eScindi {
736*7aec1d6eScindi 	const struct ao_smi_disable *asd;
737*7aec1d6eScindi 	id_t id;
738*7aec1d6eScindi 
739*7aec1d6eScindi 	smbios_system_t sy;
740*7aec1d6eScindi 	smbios_bios_t sb;
741*7aec1d6eScindi 	smbios_info_t si;
742*7aec1d6eScindi 
743*7aec1d6eScindi 	/*
744*7aec1d6eScindi 	 * Fetch the System and BIOS vendor strings from SMBIOS and see if they
745*7aec1d6eScindi 	 * match a value in our table.  If so, disable SMI error polling.  This
746*7aec1d6eScindi 	 * is grotesque and should be replaced by self-describing vendor-
747*7aec1d6eScindi 	 * specific SMBIOS data or a specification enhancement instead.
748*7aec1d6eScindi 	 */
749*7aec1d6eScindi 	if (ao_mca_smi_disable && ksmbios != NULL &&
750*7aec1d6eScindi 	    smbios_info_bios(ksmbios, &sb) != SMB_ERR &&
751*7aec1d6eScindi 	    (id = smbios_info_system(ksmbios, &sy)) != SMB_ERR &&
752*7aec1d6eScindi 	    smbios_info_common(ksmbios, id, &si) != SMB_ERR) {
753*7aec1d6eScindi 
754*7aec1d6eScindi 		for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) {
755*7aec1d6eScindi 			if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer,
756*7aec1d6eScindi 			    strlen(asd->asd_sys_vendor)) != 0 ||
757*7aec1d6eScindi 			    strncmp(asd->asd_bios_vendor, sb.smbb_vendor,
758*7aec1d6eScindi 			    strlen(asd->asd_bios_vendor)) != 0)
759*7aec1d6eScindi 				continue;
760*7aec1d6eScindi 
761*7aec1d6eScindi 			cmn_err(CE_CONT, "?SMI polling disabled in favor of "
762*7aec1d6eScindi 			    "Solaris Fault Management for AMD Processors");
763*7aec1d6eScindi 
764*7aec1d6eScindi 			outl(asd->asd_port, asd->asd_code);
765*7aec1d6eScindi 			break;
766*7aec1d6eScindi 		}
767*7aec1d6eScindi 	}
768*7aec1d6eScindi 
769*7aec1d6eScindi 	ao_mca_poll_start();
770*7aec1d6eScindi }
771*7aec1d6eScindi 
772*7aec1d6eScindi /*
773*7aec1d6eScindi  * Called after a CPU has been marked with CPU_FAULTED.  Not called on the
774*7aec1d6eScindi  * faulted CPU.  cpu_lock is held.
775*7aec1d6eScindi  */
776*7aec1d6eScindi /*ARGSUSED*/
777*7aec1d6eScindi void
778*7aec1d6eScindi ao_faulted_enter(void *data)
779*7aec1d6eScindi {
780*7aec1d6eScindi 	/*
781*7aec1d6eScindi 	 * Nothing to do here.  We'd like to turn off the faulted CPU's
782*7aec1d6eScindi 	 * correctable error detectors, but that can only be done by the
783*7aec1d6eScindi 	 * faulted CPU itself.  cpu_get_state() will now return P_FAULTED,
784*7aec1d6eScindi 	 * allowing the poller to skip this CPU until it is re-enabled.
785*7aec1d6eScindi 	 */
786*7aec1d6eScindi }
787*7aec1d6eScindi 
788*7aec1d6eScindi /*
789*7aec1d6eScindi  * Called after the CPU_FAULTED bit has been cleared from a previously-faulted
790*7aec1d6eScindi  * CPU.  Not called on the faulted CPU.  cpu_lock is held.
791*7aec1d6eScindi  */
792*7aec1d6eScindi void
793*7aec1d6eScindi ao_faulted_exit(void *data)
794*7aec1d6eScindi {
795*7aec1d6eScindi 	ao_data_t *ao = data;
796*7aec1d6eScindi 
797*7aec1d6eScindi 	/*
798*7aec1d6eScindi 	 * We'd like to clear the faulted CPU's MCi_STATUS registers so as to
799*7aec1d6eScindi 	 * avoid generating ereports for errors which occurred while the CPU was
800*7aec1d6eScindi 	 * officially faulted.  Unfortunately, those registers can only be
801*7aec1d6eScindi 	 * cleared by the CPU itself, so we can't do it here.
802*7aec1d6eScindi 	 *
803*7aec1d6eScindi 	 * We're going to set the UNFAULTING bit on the formerly-faulted CPU's
804*7aec1d6eScindi 	 * MCA state.  This will tell the poller that the MCi_STATUS registers
805*7aec1d6eScindi 	 * can't yet be trusted.  The poller, which is the first thing we
806*7aec1d6eScindi 	 * control that'll execute on that CPU, will clear the registers, and
807*7aec1d6eScindi 	 * will then clear the bit.
808*7aec1d6eScindi 	 */
809*7aec1d6eScindi 
810*7aec1d6eScindi 	ao->ao_mca.ao_mca_flags |= AO_MCA_F_UNFAULTING;
811*7aec1d6eScindi }
812