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