xref: /illumos-gate/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c (revision 86e5bb7983583c7ce83d5235abc95df1fb570d5e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2022 Oxide Computer Co.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/regset.h>
29 #include <sys/privregs.h>
30 #include <sys/pci_impl.h>
31 #include <sys/cpuvar.h>
32 #include <sys/x86_archext.h>
33 #include <sys/cmn_err.h>
34 #include <sys/systm.h>
35 #include <sys/sysmacros.h>
36 #include <sys/pghw.h>
37 #include <sys/cyclic.h>
38 #include <sys/sysevent.h>
39 #include <sys/smbios.h>
40 #include <sys/mca_x86.h>
41 #include <sys/mca_amd.h>
42 #include <sys/mc.h>
43 #include <sys/mc_amd.h>
44 #include <sys/psw.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/sdt.h>
48 #include <sys/fm/util.h>
49 #include <sys/fm/protocol.h>
50 #include <sys/fm/cpu/AMD.h>
51 #include <sys/fm/smb/fmsmb.h>
52 #include <sys/acpi/acpi.h>
53 #include <sys/acpi/acpi_pci.h>
54 #include <sys/acpica.h>
55 #include <sys/cpu_module.h>
56 
57 #include "ao.h"
58 #include "ao_mca_disp.h"
59 
60 #define	AO_F_REVS_FG (X86_CHIPREV_AMD_LEGACY_F_REV_F | \
61 	X86_CHIPREV_AMD_LEGACY_F_REV_G)
62 
63 int ao_mca_smi_disable = 1;		/* attempt to disable SMI polling */
64 
65 extern int x86gentopo_legacy;	/* x86 generic topology support */
66 
67 struct ao_ctl_init {
68 	uint32_t ctl_revmask;	/* rev(s) to which this applies */
69 	uint64_t ctl_bits;	/* mca ctl reg bitmask to set */
70 };
71 
72 /*
73  * Additional NB MCA ctl initialization for revs F and G
74  */
75 static const struct ao_ctl_init ao_nb_ctl_init[] = {
76 	{ AO_F_REVS_FG, AMD_NB_CTL_INIT_REV_FG },
77 	{ X86_CHIPREV_UNKNOWN, 0 }
78 };
79 
80 typedef struct ao_bank_cfg {
81 	uint64_t bank_ctl_init_cmn;			/* Common init value */
82 	const struct ao_ctl_init *bank_ctl_init_extra;	/* Extra for each rev */
83 	void (*bank_misc_initfunc)(cmi_hdl_t, ao_ms_data_t *, uint32_t);
84 	uint_t bank_ctl_mask;
85 } ao_bank_cfg_t;
86 
87 static void nb_mcamisc_init(cmi_hdl_t, ao_ms_data_t *, uint32_t);
88 
89 static const ao_bank_cfg_t ao_bank_cfgs[] = {
90 	{ AMD_DC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_DC_MASK },
91 	{ AMD_IC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_IC_MASK },
92 	{ AMD_BU_CTL_INIT_CMN, NULL, NULL, AMD_MSR_BU_MASK },
93 	{ AMD_LS_CTL_INIT_CMN, NULL, NULL, AMD_MSR_LS_MASK },
94 	{ AMD_NB_CTL_INIT_CMN, &ao_nb_ctl_init[0], nb_mcamisc_init,
95 		AMD_MSR_NB_MASK },
96 };
97 
98 static int ao_nbanks = sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]);
99 
100 /*
101  * This is quite awful but necessary to work around x86 system vendor's view of
102  * the world.  Other operating systems (you know who you are) don't understand
103  * Opteron-specific error handling, so BIOS and system vendors often hide these
104  * conditions from them by using SMI polling to copy out any errors from the
105  * machine-check registers.  When Solaris runs on a system with this feature,
106  * we want to disable the SMI polling so we can use FMA instead.  Sadly, there
107  * isn't even a standard self-describing way to express the whole situation,
108  * so we have to resort to hard-coded values.  This should all be changed to
109  * be a self-describing vendor-specific SMBIOS structure in the future.
110  */
111 static const struct ao_smi_disable {
112 	const char *asd_sys_vendor;	/* SMB_TYPE_SYSTEM vendor prefix */
113 	const char *asd_sys_product;	/* SMB_TYPE_SYSTEM product prefix */
114 	const char *asd_bios_vendor;	/* SMB_TYPE_BIOS vendor prefix */
115 	uint8_t asd_code;		/* output code for SMI disable */
116 } ao_smi_disable[] = {
117 	{ "Sun Microsystems", "Galaxy12",
118 	    "American Megatrends", 0x59 },
119 	{ "Sun Microsystems", "Sun Fire X4100 Server",
120 	    "American Megatrends", 0x59 },
121 	{ "Sun Microsystems", "Sun Fire X4200 Server",
122 	    "American Megatrends", 0x59 },
123 	{ NULL, NULL, NULL, 0 }
124 };
125 
126 static int
127 ao_disp_match_r4(uint16_t ref, uint8_t r4)
128 {
129 	static const uint16_t ao_r4_map[] = {
130 		AO_MCA_R4_BIT_ERR,	/* MCAX86_ERRCODE_RRRR_ERR */
131 		AO_MCA_R4_BIT_RD,	/* MCAX86_ERRCODE_RRRR_RD */
132 		AO_MCA_R4_BIT_WR,	/* MCAX86_ERRCODE_RRRR_WR */
133 		AO_MCA_R4_BIT_DRD,	/* MCAX86_ERRCODE_RRRR_DRD */
134 		AO_MCA_R4_BIT_DWR,	/* MCAX86_ERRCODE_RRRR_DWR */
135 		AO_MCA_R4_BIT_IRD,	/* MCAX86_ERRCODE_RRRR_IRD */
136 		AO_MCA_R4_BIT_PREFETCH,	/* MCAX86_ERRCODE_RRRR_PREFETCH */
137 		AO_MCA_R4_BIT_EVICT,	/* MCAX86_ERRCODE_RRRR_EVICT */
138 		AO_MCA_R4_BIT_SNOOP	/* MCAX86_ERRCODE_RRRR_SNOOP */
139 	};
140 
141 	ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t));
142 
143 	return ((ref & ao_r4_map[r4]) != 0);
144 }
145 
146 static int
147 ao_disp_match_pp(uint8_t ref, uint8_t pp)
148 {
149 	static const uint8_t ao_pp_map[] = {
150 		AO_MCA_PP_BIT_SRC,	/* MCAX86_ERRCODE_PP_SRC */
151 		AO_MCA_PP_BIT_RES,	/* MCAX86_ERRCODE_PP_RES */
152 		AO_MCA_PP_BIT_OBS,	/* MCAX86_ERRCODE_PP_OBS */
153 		AO_MCA_PP_BIT_GEN	/* MCAX86_ERRCODE_PP_GEN */
154 	};
155 
156 	ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t));
157 
158 	return ((ref & ao_pp_map[pp]) != 0);
159 }
160 
161 static int
162 ao_disp_match_ii(uint8_t ref, uint8_t ii)
163 {
164 	static const uint8_t ao_ii_map[] = {
165 		AO_MCA_II_BIT_MEM,	/* MCAX86_ERRCODE_II_MEM */
166 		0,
167 		AO_MCA_II_BIT_IO,	/* MCAX86_ERRCODE_II_IO */
168 		AO_MCA_II_BIT_GEN	/* MCAX86_ERRCODE_II_GEN */
169 	};
170 
171 	ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t));
172 
173 	return ((ref & ao_ii_map[ii]) != 0);
174 }
175 
176 static uint8_t
177 bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
178 {
179 	uint8_t val = (*codep & mask) >> shift;
180 	*codep &= ~mask;
181 	return (val);
182 }
183 
184 #define	BIT_STRIP(codep, name) \
185 	bit_strip(codep, MCAX86_ERRCODE_##name##_MASK, \
186 	MCAX86_ERRCODE_##name##_SHIFT)
187 
188 /*ARGSUSED*/
189 static int
190 ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev,
191     int bankno)
192 {
193 	uint16_t code = MCAX86_ERRCODE(status);
194 	uint8_t extcode = AMD_EXT_ERRCODE(status);
195 	uint64_t stat_mask = aed->aed_stat_mask;
196 	uint64_t stat_mask_res = aed->aed_stat_mask_res;
197 
198 	/*
199 	 * If the bank's status register indicates overflow, then we can no
200 	 * longer rely on the value of CECC: our experience with actual fault
201 	 * injection has shown that multiple CE's overwriting each other shows
202 	 * AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero.  This
203 	 * should be clarified in a future BKDG or by the Revision Guide.
204 	 * This behaviour is fixed in revision F.
205 	 */
206 	if (bankno == AMD_MCA_BANK_NB &&
207 	    !chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_F) &&
208 	    status & MSR_MC_STATUS_OVER) {
209 		stat_mask &= ~AMD_BANK_STAT_CECC;
210 		stat_mask_res &= ~AMD_BANK_STAT_CECC;
211 	}
212 
213 	if ((status & stat_mask) != stat_mask_res)
214 		return (0);
215 
216 	/*
217 	 * r4 and pp bits are stored separately, so we mask off and compare them
218 	 * for the code types that use them.  Once we've taken the r4 and pp
219 	 * bits out of the equation, we can directly compare the resulting code
220 	 * with the one stored in the ao_error_disp_t.
221 	 */
222 	if (AMD_ERRCODE_ISMEM(code)) {
223 		uint8_t r4 = BIT_STRIP(&code, RRRR);
224 
225 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4))
226 			return (0);
227 
228 	} else if (AMD_ERRCODE_ISBUS(code)) {
229 		uint8_t r4 = BIT_STRIP(&code, RRRR);
230 		uint8_t pp = BIT_STRIP(&code, PP);
231 		uint8_t ii = BIT_STRIP(&code, II);
232 
233 		if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) ||
234 		    !ao_disp_match_pp(aed->aed_stat_pp_bits, pp) ||
235 		    !ao_disp_match_ii(aed->aed_stat_ii_bits, ii))
236 			return (0);
237 	}
238 
239 	return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode);
240 }
241 
242 /*ARGSUSED*/
243 cms_cookie_t
244 ao_ms_disp_match(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
245     uint64_t addr, uint64_t misc, void *mslogout)
246 {
247 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
248 	x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
249 	const ao_error_disp_t *aed;
250 
251 	for (aed = ao_error_disp[banknum]; aed->aed_stat_mask != 0; aed++) {
252 		if (ao_disp_match_one(aed, status, rev, banknum))
253 			return ((cms_cookie_t)aed);
254 	}
255 
256 	return (NULL);
257 }
258 
259 /*ARGSUSED*/
260 void
261 ao_ms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
262     const char **cpuclsp, const char **leafclsp)
263 {
264 	const ao_error_disp_t *aed = mscookie;
265 
266 	if (aed != NULL) {
267 		*cpuclsp = FM_EREPORT_CPU_AMD;
268 		*leafclsp = aed->aed_class;
269 	}
270 }
271 
272 static int
273 ao_chip_once(ao_ms_data_t *ao, enum ao_cfgonce_bitnum what)
274 {
275 	return (atomic_set_long_excl(&ao->ao_ms_shared->aos_cfgonce,
276 	    what) == 0 ?  B_TRUE : B_FALSE);
277 }
278 
279 /*
280  * This knob exists in case any platform has a problem with our default
281  * policy of disabling any interrupt registered in the NB MC4_MISC
282  * register.  Setting this may cause Solaris and external entities
283  * who also have an interest in this register to argue over available
284  * telemetry (so setting it is generally not recommended).
285  */
286 int ao_nb_cfg_mc4misc_noseize = 0;
287 
288 /*
289  * The BIOS may have setup to receive SMI on counter overflow.  It may also
290  * have locked various fields or made them read-only.  We will clear any
291  * SMI request and leave the register locked.  We will also clear the
292  * counter and enable counting - while we don't use the counter it is nice
293  * to have it enabled for verification and debug work.
294  */
295 static void
296 nb_mcamisc_init(cmi_hdl_t hdl, ao_ms_data_t *ao, uint32_t rev)
297 {
298 	uint64_t val, nval;
299 
300 	if (!chiprev_matches(rev, AO_F_REVS_FG))
301 		return;
302 
303 	if (cmi_hdl_rdmsr(hdl, AMD_MSR_NB_MISC, &val) != CMI_SUCCESS)
304 		return;
305 
306 	ao->ao_ms_shared->aos_bcfg_nb_misc = val;
307 
308 	if (ao_nb_cfg_mc4misc_noseize)
309 		return;		/* stash BIOS value, but no changes */
310 
311 
312 	/*
313 	 * The Valid bit tells us whether the CtrP bit is defined; if it
314 	 * is the CtrP bit tells us whether an ErrCount field is present.
315 	 * If not then there is nothing for us to do.
316 	 */
317 	if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP))
318 		return;
319 
320 
321 	nval = val;
322 	nval |= AMD_NB_MISC_CNTEN;		/* enable ECC error counting */
323 	nval &= ~AMD_NB_MISC_ERRCOUNT_MASK;	/* clear ErrCount */
324 	nval &= ~AMD_NB_MISC_OVRFLW;		/* clear Ovrflw */
325 	nval &= ~AMD_NB_MISC_INTTYPE_MASK;	/* no interrupt on overflow */
326 	nval |= AMD_NB_MISC_LOCKED;
327 
328 	if (nval != val) {
329 		uint64_t locked = val & AMD_NB_MISC_LOCKED;
330 
331 		if (locked)
332 			ao_bankstatus_prewrite(hdl, ao);
333 
334 		(void) cmi_hdl_wrmsr(hdl, AMD_MSR_NB_MISC, nval);
335 
336 		if (locked)
337 			ao_bankstatus_postwrite(hdl, ao);
338 	}
339 }
340 
341 /*
342  * NorthBridge (NB) MCA Configuration.
343  *
344  * We add and remove bits from the BIOS-configured value, rather than
345  * writing an absolute value.  The variables ao_nb_cfg_{add,remove}_cmn and
346  * ap_nb_cfg_{add,remove}_revFG are available for modification via kmdb
347  * and /etc/system.  The revision-specific adds and removes are applied
348  * after the common changes, and one write is made to the config register.
349  * These are not intended for watchdog configuration via these variables -
350  * use the watchdog policy below.
351  */
352 
353 /*
354  * Bits to be added to the NB configuration register - all revs.
355  */
356 uint32_t ao_nb_cfg_add_cmn = AMD_NB_CFG_ADD_CMN;
357 
358 /*
359  * Bits to be cleared from the NB configuration register - all revs.
360  */
361 uint32_t ao_nb_cfg_remove_cmn = AMD_NB_CFG_REMOVE_CMN;
362 
363 /*
364  * Bits to be added to the NB configuration register - revs F and G.
365  */
366 uint32_t ao_nb_cfg_add_revFG = AMD_NB_CFG_ADD_REV_FG;
367 
368 /*
369  * Bits to be cleared from the NB configuration register - revs F and G.
370  */
371 uint32_t ao_nb_cfg_remove_revFG = AMD_NB_CFG_REMOVE_REV_FG;
372 
373 struct ao_nb_cfg {
374 	uint32_t cfg_revmask;
375 	uint32_t *cfg_add_p;
376 	uint32_t *cfg_remove_p;
377 };
378 
379 static const struct ao_nb_cfg ao_cfg_extra[] = {
380 	{ AO_F_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG },
381 	{ X86_CHIPREV_UNKNOWN, NULL, NULL }
382 };
383 
384 /*
385  * Bits to be used if we configure the NorthBridge (NB) Watchdog.  The watchdog
386  * triggers a machine check exception when no response to an NB system access
387  * occurs within a specified time interval.
388  */
389 uint32_t ao_nb_cfg_wdog =
390     AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
391     AMD_NB_CFG_WDOGTMRBASESEL_1MS;
392 
393 /*
394  * The default watchdog policy is to enable it (at the above rate) if it
395  * is disabled;  if it is enabled then we leave it enabled at the rate
396  * chosen by the BIOS.
397  */
398 enum {
399 	AO_NB_WDOG_LEAVEALONE,		/* Don't touch watchdog config */
400 	AO_NB_WDOG_DISABLE,		/* Always disable watchdog */
401 	AO_NB_WDOG_ENABLE_IF_DISABLED,	/* If disabled, enable at our rate */
402 	AO_NB_WDOG_ENABLE_FORCE_RATE	/* Enable and set our rate */
403 } ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED;
404 
405 static void
406 ao_nb_cfg(ao_ms_data_t *ao, uint32_t rev)
407 {
408 	const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0];
409 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
410 	uint32_t val;
411 
412 	/*
413 	 * Read the NorthBridge (NB) configuration register in PCI space,
414 	 * modify the settings accordingly, and store the new value back.
415 	 * Note that the stashed BIOS config value aos_bcfg_nb_cfg is used
416 	 * in ereport payload population to determine ECC syndrome type for
417 	 * memory errors.
418 	 */
419 	ao->ao_ms_shared->aos_bcfg_nb_cfg = val =
420 	    ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
421 
422 	switch (ao_nb_watchdog_policy) {
423 	case AO_NB_WDOG_LEAVEALONE:
424 		break;
425 
426 	case AO_NB_WDOG_DISABLE:
427 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
428 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
429 		val |= AMD_NB_CFG_WDOGTMRDIS;
430 		break;
431 
432 	default:
433 		cmn_err(CE_NOTE, "ao_nb_watchdog_policy=%d unrecognised, "
434 		    "using default policy", ao_nb_watchdog_policy);
435 		/*FALLTHRU*/
436 
437 	case AO_NB_WDOG_ENABLE_IF_DISABLED:
438 		if (!(val & AMD_NB_CFG_WDOGTMRDIS))
439 			break;	/* if enabled leave rate intact */
440 		/*FALLTHRU*/
441 
442 	case AO_NB_WDOG_ENABLE_FORCE_RATE:
443 		val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
444 		val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
445 		val &= ~AMD_NB_CFG_WDOGTMRDIS;
446 		val |= ao_nb_cfg_wdog;
447 		break;
448 	}
449 
450 	/*
451 	 * Now apply bit adds and removes, first those common to all revs
452 	 * and then the revision-specific ones.
453 	 */
454 	val &= ~ao_nb_cfg_remove_cmn;
455 	val |= ao_nb_cfg_add_cmn;
456 
457 	while (nbcp->cfg_revmask != X86_CHIPREV_UNKNOWN) {
458 		if (chiprev_matches(rev, nbcp->cfg_revmask)) {
459 			val &= ~(*nbcp->cfg_remove_p);
460 			val |= *nbcp->cfg_add_p;
461 		}
462 		nbcp++;
463 	}
464 
465 	ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG, val);
466 }
467 
468 static void
469 ao_dram_cfg(ao_ms_data_t *ao, uint32_t rev)
470 {
471 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
472 	union mcreg_dramcfg_lo dcfglo;
473 
474 	ao->ao_ms_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) =
475 	    ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO);
476 	ao->ao_ms_shared->aos_bcfg_dcfg_hi =
477 	    ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGHI);
478 #ifdef OPTERON_ERRATUM_172
479 	if (chiprev_matches(rev, AO_F_REVS_FG) &&
480 	    MCREG_FIELD_F_revFG(&dcfglo, ParEn)) {
481 		MCREG_FIELD_F_revFG(&dcfglo, ParEn) = 0;
482 		ao_pcicfg_write(procnodeid, MC_FUNC_DRAMCTL,
483 		    MC_DC_REG_DRAMCFGLO, MCREG_VAL32(&dcfglo));
484 	}
485 #endif
486 }
487 
488 /*
489  * This knob exists in case any platform has a problem with our default
490  * policy of disabling any interrupt registered in the online spare
491  * control register.  Setting this may cause Solaris and external entities
492  * who also have an interest in this register to argue over available
493  * telemetry (so setting it is generally not recommended).
494  */
495 int ao_nb_cfg_sparectl_noseize = 0;
496 
497 /*
498  * Setup the online spare control register (revs F and G).  We disable
499  * any interrupt registered by the BIOS and zero all error counts.
500  */
501 static void
502 ao_sparectl_cfg(ao_ms_data_t *ao)
503 {
504 	uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
505 	union mcreg_sparectl sparectl;
506 	int chan, cs;
507 
508 	ao->ao_ms_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) =
509 	    ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
510 
511 	if (ao_nb_cfg_sparectl_noseize)
512 		return;	/* stash BIOS value, but no changes */
513 
514 	/*
515 	 * If the BIOS has requested SMI interrupt type for ECC count
516 	 * overflow for a chip-select or channel force those off.
517 	 */
518 	MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0;
519 	MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0;
520 
521 	/*
522 	 * Zero EccErrCnt and write this back to all chan/cs combinations.
523 	 */
524 	MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1;
525 	MCREG_FIELD_F_revFG(&sparectl, EccErrCnt) = 0;
526 	for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) {
527 		MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = chan;
528 
529 		for (cs = 0; cs < MC_CHIP_NCS; cs++) {
530 			MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramCs) = cs;
531 			ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
532 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
533 		}
534 	}
535 }
536 
537 int ao_forgive_uc = 0;		/* For test/debug only */
538 int ao_forgive_pcc = 0;		/* For test/debug only */
539 int ao_fake_poison = 0;		/* For test/debug only */
540 
541 uint32_t
542 ao_ms_error_action(cmi_hdl_t hdl, int ismc, int banknum,
543     uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
544 {
545 	const ao_error_disp_t *aed;
546 	uint32_t retval = 0;
547 	uint8_t when;
548 	int en;
549 
550 	if (ao_forgive_uc)
551 		retval |= CMS_ERRSCOPE_CLEARED_UC;
552 
553 	if (ao_forgive_pcc)
554 		retval |= CMS_ERRSCOPE_CURCONTEXT_OK;
555 
556 	if (ao_fake_poison && status & MSR_MC_STATUS_UC)
557 		retval |= CMS_ERRSCOPE_POISONED;
558 
559 	if (retval)
560 		return (retval);
561 
562 	aed = ao_ms_disp_match(hdl, ismc, banknum, status, addr, misc,
563 	    mslogout);
564 
565 	/*
566 	 * If we do not recognise the error let the cpu module apply
567 	 * the generic criteria to decide how to react.
568 	 */
569 	if (aed == NULL)
570 		return (0);
571 
572 	en = (status & MSR_MC_STATUS_EN) != 0;
573 
574 	if ((when = aed->aed_panic_when) == AO_AED_PANIC_NEVER)
575 		retval |= CMS_ERRSCOPE_IGNORE_ERR;
576 
577 	if ((when & AO_AED_PANIC_ALWAYS) ||
578 	    ((when & AO_AED_PANIC_IFMCE) && (en || ismc)))
579 		retval |= CMS_ERRSCOPE_FORCE_FATAL;
580 
581 	/*
582 	 * The original AMD implementation would panic on a machine check
583 	 * (not a poll) if the status overflow bit was set, with an
584 	 * exception for the case of rev F or later with an NB error
585 	 * indicating CECC.  This came from the perception that the
586 	 * overflow bit was not correctly managed on rev E and earlier, for
587 	 * example that repeated correctable memeory errors did not set
588 	 * OVER but somehow clear CECC.
589 	 *
590 	 * We will leave the generic support to evaluate overflow errors
591 	 * and decide to panic on their individual merits, e.g., if PCC
592 	 * is set and so on.  The AMD docs do say (as Intel does) that
593 	 * the status information is *all* from the higher-priority
594 	 * error in the case of an overflow, so it is at least as serious
595 	 * as the original and we can decide panic etc based on it.
596 	 */
597 
598 	return (retval);
599 }
600 
601 /*
602  * Will need to change for family 0x10
603  */
604 static uint_t
605 ao_ereport_synd(ao_ms_data_t *ao, uint64_t status, uint_t *typep,
606     int is_nb)
607 {
608 	if (is_nb) {
609 		if (ao->ao_ms_shared->aos_bcfg_nb_cfg &
610 		    AMD_NB_CFG_CHIPKILLECCEN) {
611 			*typep = AMD_SYNDTYPE_CHIPKILL;
612 			return (AMD_NB_STAT_CKSYND(status));
613 		} else {
614 			*typep = AMD_SYNDTYPE_ECC;
615 			return (AMD_BANK_SYND(status));
616 		}
617 	} else {
618 		*typep = AMD_SYNDTYPE_ECC;
619 		return (AMD_BANK_SYND(status));
620 	}
621 }
622 
623 static nvlist_t *
624 ao_ereport_create_resource_elem(cmi_hdl_t hdl, nv_alloc_t *nva,
625     mc_unum_t *unump, int dimmnum)
626 {
627 	nvlist_t *nvl, *snvl;
628 	nvlist_t *board_list = NULL;
629 
630 	if ((nvl = fm_nvlist_create(nva)) == NULL)	/* freed by caller */
631 		return (NULL);
632 
633 	if ((snvl = fm_nvlist_create(nva)) == NULL) {
634 		fm_nvlist_destroy(nvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
635 		return (NULL);
636 	}
637 
638 	(void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
639 	    unump->unum_offset);
640 
641 	if (!x86gentopo_legacy) {
642 		board_list = cmi_hdl_smb_bboard(hdl);
643 
644 		if (board_list == NULL) {
645 			fm_nvlist_destroy(nvl,
646 			    nva ? FM_NVA_RETAIN : FM_NVA_FREE);
647 			fm_nvlist_destroy(snvl,
648 			    nva ? FM_NVA_RETAIN : FM_NVA_FREE);
649 			return (NULL);
650 		}
651 
652 		fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, snvl,
653 		    board_list, 4,
654 		    "chip", cmi_hdl_smb_chipid(hdl),
655 		    "memory-controller", unump->unum_mc,
656 		    "dimm", unump->unum_dimms[dimmnum],
657 		    "rank", unump->unum_rank);
658 	} else {
659 		fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, snvl, 5,
660 		    "motherboard", unump->unum_board,
661 		    "chip", unump->unum_chip,
662 		    "memory-controller", unump->unum_mc,
663 		    "dimm", unump->unum_dimms[dimmnum],
664 		    "rank", unump->unum_rank);
665 	}
666 
667 	fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
668 
669 	return (nvl);
670 }
671 
672 static void
673 ao_ereport_add_resource(cmi_hdl_t hdl, nvlist_t *payload, nv_alloc_t *nva,
674     mc_unum_t *unump)
675 {
676 
677 	nvlist_t *elems[MC_UNUM_NDIMM];
678 	int nelems = 0;
679 	int i;
680 
681 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
682 		if (unump->unum_dimms[i] == MC_INVALNUM)
683 			break;
684 
685 		if ((elems[nelems] = ao_ereport_create_resource_elem(hdl, nva,
686 		    unump, i)) == NULL)
687 			break;
688 
689 		nelems++;
690 	}
691 
692 	if (nelems == 0)
693 		return;
694 
695 	fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
696 	    DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
697 
698 	for (i = 0; i < nelems; i++)
699 		fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
700 }
701 
702 /*ARGSUSED*/
703 void
704 ao_ms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport,
705     nv_alloc_t *nva, int banknum, uint64_t status, uint64_t addr,
706     uint64_t misc, void *mslogout, cms_cookie_t mscookie)
707 {
708 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
709 	const ao_error_disp_t *aed = mscookie;
710 	uint_t synd, syndtype;
711 	uint64_t members;
712 
713 	if (aed == NULL)
714 		return;
715 
716 	members = aed->aed_ereport_members;
717 
718 	synd = ao_ereport_synd(ao, status, &syndtype,
719 	    banknum == AMD_MCA_BANK_NB);
720 
721 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
722 		fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND,
723 		    DATA_TYPE_UINT16, synd, NULL);
724 	}
725 
726 	if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) {
727 		fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE,
728 		    DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ?
729 		    "C4" : "E"), NULL);
730 	}
731 
732 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) {
733 		mc_unum_t unum;
734 
735 		if (((aed->aed_flags & AO_AED_FLAGS_ADDRTYPE) ==
736 		    AO_AED_F_PHYSICAL) && (status & MSR_MC_STATUS_ADDRV) &&
737 		    cmi_mc_patounum(addr, aed->aed_addrvalid_hi,
738 		    aed->aed_addrvalid_lo, synd, syndtype, &unum) ==
739 		    CMI_SUCCESS)
740 			ao_ereport_add_resource(hdl, ereport, nva, &unum);
741 	}
742 }
743 
744 /*ARGSUSED*/
745 boolean_t
746 ao_ms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
747 {
748 	const ao_error_disp_t *aed = mscookie;
749 
750 	if (aed == NULL)
751 		return (0);
752 
753 	return ((aed->aed_ereport_members &
754 	    FM_EREPORT_PAYLOAD_FLAG_STACK) != 0);
755 }
756 
757 cms_errno_t
758 ao_ms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
759 {
760 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
761 	cms_errno_t rv = CMSERR_BADMSRWRITE;
762 
763 	ao_bankstatus_prewrite(hdl, ao);
764 	if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
765 		rv = CMS_SUCCESS;
766 	ao_bankstatus_postwrite(hdl, ao);
767 
768 	return (rv);
769 }
770 
771 /*ARGSUSED*/
772 uint64_t
773 ao_ms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
774 {
775 	return ((1ULL << nbanks) - 1);
776 }
777 
778 boolean_t
779 ao_ms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
780 {
781 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
782 
783 	if (banknum != AMD_MCA_BANK_NB)
784 		return (B_FALSE);
785 
786 	/*
787 	 * If we are the first to atomically set the "I'll do it" bit
788 	 * then return B_FALSE (do not skip), otherwise skip with B_TRUE.
789 	 */
790 	return (ao_chip_once(ao, AO_CFGONCE_NBMCA) == B_TRUE ?
791 	    B_FALSE : B_TRUE);
792 }
793 
794 uint64_t
795 ao_ms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
796 {
797 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
798 	const struct ao_ctl_init *extrap;
799 	const ao_bank_cfg_t *bankcfg;
800 	uint64_t mcictl;
801 	x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
802 
803 	if (banknum >= sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]))
804 		return (def);
805 
806 	bankcfg = &ao_bank_cfgs[banknum];
807 	extrap = bankcfg->bank_ctl_init_extra;
808 
809 	mcictl = bankcfg->bank_ctl_init_cmn;
810 
811 	while (extrap != NULL && extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) {
812 		if (chiprev_matches(rev, extrap->ctl_revmask))
813 			mcictl |= extrap->ctl_bits;
814 		extrap++;
815 	}
816 
817 	return (mcictl);
818 }
819 
820 /*ARGSUSED*/
821 void
822 ao_bankstatus_prewrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
823 {
824 #ifndef __xpv
825 	uint64_t hwcr;
826 
827 	if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
828 		return;
829 
830 	ao->ao_ms_hwcr_val = hwcr;
831 
832 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
833 		hwcr |= AMD_HWCR_MCI_STATUS_WREN;
834 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
835 	}
836 #endif
837 }
838 
839 /*ARGSUSED*/
840 void
841 ao_bankstatus_postwrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
842 {
843 #ifndef __xpv
844 	uint64_t hwcr = ao->ao_ms_hwcr_val;
845 
846 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
847 		hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
848 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
849 	}
850 #endif
851 }
852 
853 void
854 ao_ms_mca_init(cmi_hdl_t hdl, int nbanks)
855 {
856 	ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
857 	x86_chiprev_t rev = ao->ao_ms_shared->aos_chiprev;
858 	ao_ms_mca_t *mca = &ao->ao_ms_mca;
859 	uint64_t *maskp;
860 	int i;
861 
862 	maskp = mca->ao_mca_bios_cfg.bcfg_bank_mask = kmem_zalloc(nbanks *
863 	    sizeof (uint64_t), KM_SLEEP);
864 
865 	/*
866 	 * Read the bank ctl mask MSRs, but only as many as we know
867 	 * certainly exist - don't calculate the register address.
868 	 * Also initialize the MCi_MISC register where required.
869 	 */
870 	for (i = 0; i < MIN(nbanks, ao_nbanks); i++) {
871 		(void) cmi_hdl_rdmsr(hdl, ao_bank_cfgs[i].bank_ctl_mask,
872 		    maskp++);
873 		if (ao_bank_cfgs[i].bank_misc_initfunc != NULL)
874 			ao_bank_cfgs[i].bank_misc_initfunc(hdl, ao, rev);
875 
876 	}
877 
878 	if (ao_chip_once(ao, AO_CFGONCE_NBCFG) == B_TRUE) {
879 		ao_nb_cfg(ao, rev);
880 
881 		if (chiprev_matches(rev, AO_F_REVS_FG))
882 			ao_sparectl_cfg(ao);
883 	}
884 
885 	if (ao_chip_once(ao, AO_CFGONCE_DRAMCFG) == B_TRUE)
886 		ao_dram_cfg(ao, rev);
887 
888 	ao_procnode_scrubber_enable(hdl, ao);
889 }
890 
891 /*
892  * Note that although this cpu module is loaded before the PSMs are
893  * loaded (and hence before acpica is loaded), this function is
894  * called from post_startup(), after PSMs are initialized and acpica
895  * is loaded.
896  */
897 static int
898 ao_acpi_find_smicmd(int *asd_port)
899 {
900 	ACPI_TABLE_FADT *fadt = NULL;
901 
902 	/*
903 	 * AcpiGetTable works even if ACPI is disabled, so a failure
904 	 * here means we weren't able to retreive a pointer to the FADT.
905 	 */
906 	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
907 	    AE_OK)
908 		return (-1);
909 
910 	ASSERT(fadt != NULL);
911 
912 	*asd_port = fadt->SmiCommand;
913 	return (0);
914 }
915 
916 /*ARGSUSED*/
917 void
918 ao_ms_post_startup(cmi_hdl_t hdl)
919 {
920 	const struct ao_smi_disable *asd;
921 	id_t id;
922 	int rv = -1, asd_port;
923 
924 	smbios_system_t sy;
925 	smbios_bios_t sb;
926 	smbios_info_t si;
927 
928 	/*
929 	 * Fetch the System and BIOS vendor strings from SMBIOS and see if they
930 	 * match a value in our table.  If so, disable SMI error polling.  This
931 	 * is grotesque and should be replaced by self-describing vendor-
932 	 * specific SMBIOS data or a specification enhancement instead.
933 	 */
934 	if (ao_mca_smi_disable && ksmbios != NULL &&
935 	    smbios_info_bios(ksmbios, &sb) != SMB_ERR &&
936 	    (id = smbios_info_system(ksmbios, &sy)) != SMB_ERR &&
937 	    smbios_info_common(ksmbios, id, &si) != SMB_ERR) {
938 
939 		for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) {
940 			if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer,
941 			    strlen(asd->asd_sys_vendor)) != 0 ||
942 			    strncmp(asd->asd_sys_product, si.smbi_product,
943 			    strlen(asd->asd_sys_product)) != 0 ||
944 			    strncmp(asd->asd_bios_vendor, sb.smbb_vendor,
945 			    strlen(asd->asd_bios_vendor)) != 0)
946 				continue;
947 
948 			/*
949 			 * Look for the SMI_CMD port in the ACPI FADT,
950 			 * if the port is 0, this platform doesn't support
951 			 * SMM, so there is no SMI error polling to disable.
952 			 */
953 			if ((rv = ao_acpi_find_smicmd(&asd_port)) == 0 &&
954 			    asd_port != 0) {
955 				cmn_err(CE_CONT, "?SMI polling disabled in "
956 				    "favor of Solaris Fault Management for "
957 				    "AMD Processors\n");
958 
959 				outb(asd_port, asd->asd_code);
960 
961 			} else if (rv < 0) {
962 				cmn_err(CE_CONT, "?Solaris Fault Management "
963 				    "for AMD Processors could not disable SMI "
964 				    "polling because an error occurred while "
965 				    "trying to determine the SMI command port "
966 				    "from the ACPI FADT table\n");
967 			}
968 			break;
969 		}
970 	}
971 }
972