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