15254Sgavinm /* 25254Sgavinm * CDDL HEADER START 35254Sgavinm * 45254Sgavinm * The contents of this file are subject to the terms of the 55254Sgavinm * Common Development and Distribution License (the "License"). 65254Sgavinm * You may not use this file except in compliance with the License. 75254Sgavinm * 85254Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95254Sgavinm * or http://www.opensolaris.org/os/licensing. 105254Sgavinm * See the License for the specific language governing permissions 115254Sgavinm * and limitations under the License. 125254Sgavinm * 135254Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 145254Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155254Sgavinm * If applicable, add the following below this CDDL HEADER, with the 165254Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 175254Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 185254Sgavinm * 195254Sgavinm * CDDL HEADER END 205254Sgavinm */ 215254Sgavinm 225254Sgavinm /* 23*6000Sgavinm * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 245254Sgavinm * Use is subject to license terms. 255254Sgavinm */ 265254Sgavinm 275254Sgavinm #pragma ident "%Z%%M% %I% %E% SMI" 285254Sgavinm 295254Sgavinm /* 305254Sgavinm * "Generic AMD" model-specific support. If no more-specific support can 315254Sgavinm * be found, or such modules declines to initialize, then for AuthenticAMD 325254Sgavinm * cpus this module can have a crack at providing some AMD model-specific 335254Sgavinm * support that at least goes beyond common MCA architectural features 345254Sgavinm * if not down to the nitty-gritty level for a particular model. We 355254Sgavinm * are layered on top of a cpu module, likely cpu.generic, so there is no 365254Sgavinm * need for us to perform common architecturally-accessible functions. 375254Sgavinm */ 385254Sgavinm 395254Sgavinm #include <sys/types.h> 405254Sgavinm #include <sys/cmn_err.h> 415254Sgavinm #include <sys/modctl.h> 425254Sgavinm #include <sys/cpu_module.h> 435254Sgavinm #include <sys/mca_x86.h> 445254Sgavinm #include <sys/pci_cfgspace.h> 455254Sgavinm #include <sys/x86_archext.h> 465254Sgavinm #include <sys/mc_amd.h> 475254Sgavinm #include <sys/fm/protocol.h> 485254Sgavinm #include <sys/fm/cpu/GENAMD.h> 495254Sgavinm #include <sys/nvpair.h> 505254Sgavinm #include <sys/controlregs.h> 515254Sgavinm #include <sys/pghw.h> 525254Sgavinm #include <sys/sunddi.h> 535327Sgavinm #include <sys/sysmacros.h> 545254Sgavinm #include <sys/cpu_module_ms_impl.h> 555254Sgavinm 565254Sgavinm #include "authamd.h" 575254Sgavinm 585254Sgavinm int authamd_ms_support_disable = 0; 595254Sgavinm 605254Sgavinm #define AUTHAMD_F_REVS_BCDE \ 615254Sgavinm (X86_CHIPREV_AMD_F_REV_B | X86_CHIPREV_AMD_F_REV_C0 | \ 625254Sgavinm X86_CHIPREV_AMD_F_REV_CG | X86_CHIPREV_AMD_F_REV_D | \ 635254Sgavinm X86_CHIPREV_AMD_F_REV_E) 645254Sgavinm 655254Sgavinm #define AUTHAMD_F_REVS_FG \ 665254Sgavinm (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G) 675254Sgavinm 685254Sgavinm #define AUTHAMD_10_REVS_AB \ 695254Sgavinm (X86_CHIPREV_AMD_10_REV_A | X86_CHIPREV_AMD_10_REV_B) 705254Sgavinm 715254Sgavinm /* 725254Sgavinm * Bitmasks of support for various features. Try to enable features 735254Sgavinm * via inclusion in one of these bitmasks and check that at the 745254Sgavinm * feature imlementation - that way new family support may often simply 755254Sgavinm * simply need to update these bitmasks. 765254Sgavinm */ 775254Sgavinm 785254Sgavinm /* 795254Sgavinm * Families that this module will provide some model-specific 805254Sgavinm * support for (if no more-specific module claims it first). 815254Sgavinm * We try to support whole families rather than differentiate down 825254Sgavinm * to revision. 835254Sgavinm */ 845254Sgavinm #define AUTHAMD_SUPPORTED(fam) \ 855254Sgavinm ((fam) == AUTHAMD_FAMILY_6 || (fam) == AUTHAMD_FAMILY_F || \ 865254Sgavinm (fam) == AUTHAMD_FAMILY_10) 875254Sgavinm 885254Sgavinm /* 895639Sgavinm * Models that include an on-chip NorthBridge. 905639Sgavinm */ 915639Sgavinm #define AUTHAMD_NBONCHIP(rev) \ 925639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 935639Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 945639Sgavinm 955639Sgavinm /* 965254Sgavinm * Families/revisions for which we can recognise main memory ECC errors. 975254Sgavinm */ 985254Sgavinm #define AUTHAMD_MEMECC_RECOGNISED(rev) \ 995254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1005254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1015254Sgavinm 1025254Sgavinm /* 1035254Sgavinm * Families/revisions that have an Online Spare Control Register 1045254Sgavinm */ 1055254Sgavinm #define AUTHAMD_HAS_ONLINESPARECTL(rev) \ 1065254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) || \ 1075254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1085254Sgavinm 1095254Sgavinm /* 1105327Sgavinm * Families/revisions for which we will perform NB MCA Config changes 1115327Sgavinm */ 1125327Sgavinm #define AUTHAMD_DO_NBMCACFG(rev) \ 1135639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1145327Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1155327Sgavinm 1165327Sgavinm /* 1175327Sgavinm * Families/revisions that have chip cache scrubbers. 1185327Sgavinm */ 1195327Sgavinm #define AUTHAMD_HAS_CHIPSCRUB(rev) \ 1205639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1215327Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1225327Sgavinm 1235327Sgavinm /* 1245254Sgavinm * Families/revisions that have a NB misc register or registers - 1255254Sgavinm * evaluates to 0 if no support, otherwise the number of MC4_MISCj. 1265254Sgavinm */ 1275254Sgavinm #define AUTHAMD_NBMISC_NUM(rev) \ 1285254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F)? 1 : \ 1295254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A) ? 3 : 0)) 1305254Sgavinm 1315254Sgavinm /* 1325254Sgavinm * Families/revision for which we wish not to machine check for GART 1335254Sgavinm * table walk errors - bit 10 of NB CTL. 1345254Sgavinm */ 1355254Sgavinm #define AUTHAMD_NOGARTTBLWLK_MC(rev) \ 1365254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1375254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1385254Sgavinm 1395254Sgavinm /* 1405327Sgavinm * Families/revisions that are potentially L3 capable 1415327Sgavinm */ 1425327Sgavinm #define AUTHAMD_L3CAPABLE(rev) \ 1435327Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1445327Sgavinm 1455327Sgavinm /* 1465254Sgavinm * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED 1475254Sgavinm * revisions as: 1485254Sgavinm * 1495254Sgavinm * - being reported by the NB 1505254Sgavinm * - being a compound bus/interconnect error (external to chip) 1515254Sgavinm * - having LL of LG 1525254Sgavinm * - having II of MEM (but could still be a master/target abort) 1535254Sgavinm * - having CECC or UECC set 1545254Sgavinm * 1555254Sgavinm * We do not check the extended error code (first nibble of the 1565254Sgavinm * model-specific error code on AMD) since this has changed from 1575254Sgavinm * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10). 1585254Sgavinm * Instead we use CECC/UECC to separate off the master/target 1595254Sgavinm * abort cases. 1605254Sgavinm * 1615254Sgavinm * We insist that the detector be the NorthBridge bank; although 1625254Sgavinm * IC/DC can report some main memory errors, they do not capture 1635254Sgavinm * an address at sufficient resolution to be useful and the NB will 1645254Sgavinm * report most errors. 1655254Sgavinm */ 1665254Sgavinm #define AUTHAMD_IS_MEMECCERR(bank, status) \ 1675254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 1685254Sgavinm MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \ 1695254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 1705254Sgavinm MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \ 1715254Sgavinm ((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC))) 1725254Sgavinm 1735254Sgavinm static authamd_error_disp_t authamd_memce_disp = { 1745254Sgavinm FM_EREPORT_CPU_GENAMD, 1755254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_CE, 1765254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE 1775254Sgavinm }; 1785254Sgavinm 1795254Sgavinm static authamd_error_disp_t authamd_memue_disp = { 1805254Sgavinm FM_EREPORT_CPU_GENAMD, 1815254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_UE, 1825254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE 1835254Sgavinm }; 1845254Sgavinm 1855254Sgavinm static authamd_error_disp_t authamd_ckmemce_disp = { 1865254Sgavinm FM_EREPORT_CPU_GENAMD, 1875254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_CE, 1885254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE 1895254Sgavinm }; 1905254Sgavinm 1915254Sgavinm static authamd_error_disp_t authamd_ckmemue_disp = { 1925254Sgavinm FM_EREPORT_CPU_GENAMD, 1935254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_UE, 1945254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE 1955254Sgavinm }; 1965254Sgavinm 1975254Sgavinm /* 1985254Sgavinm * We recognise GART walk errors as: 1995254Sgavinm * 2005254Sgavinm * - being reported by the NB 2015254Sgavinm * - being a compound TLB error 2025254Sgavinm * - having LL of LG and TT of GEN 2035254Sgavinm * - having UC set 2045254Sgavinm * - possibly having PCC set (if source CPU) 2055254Sgavinm */ 2065254Sgavinm #define AUTHAMD_IS_GARTERR(bank, status) \ 2075254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 2085254Sgavinm MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \ 2095254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 2105254Sgavinm MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \ 2115254Sgavinm (status) & MSR_MC_STATUS_UC) 2125254Sgavinm 2135254Sgavinm static authamd_error_disp_t authamd_gart_disp = { 2145254Sgavinm FM_EREPORT_CPU_GENAMD, /* use generic subclass */ 2155254Sgavinm FM_EREPORT_CPU_GENADM_GARTTBLWLK, /* use generic leafclass */ 2165254Sgavinm 0 /* no additional payload */ 2175254Sgavinm }; 2185254Sgavinm 2195254Sgavinm 2205254Sgavinm static struct authamd_chipshared *authamd_shared[AUTHAMD_MAX_CHIPS]; 2215254Sgavinm 2225254Sgavinm static int 2235254Sgavinm authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what) 2245254Sgavinm { 2255254Sgavinm return (atomic_set_long_excl(&authamd->amd_shared->acs_cfgonce, 2265254Sgavinm what) == 0 ? B_TRUE : B_FALSE); 2275254Sgavinm } 2285254Sgavinm 2295254Sgavinm static void 2305254Sgavinm authamd_pcicfg_write(uint_t chipid, uint_t func, uint_t reg, uint32_t val) 2315254Sgavinm { 2325254Sgavinm ASSERT(chipid + 24 <= 31); 2335254Sgavinm ASSERT((func & 7) == func); 2345254Sgavinm ASSERT((reg & 3) == 0 && reg < 256); 2355254Sgavinm 2365254Sgavinm cmi_pci_putl(0, chipid + 24, func, reg, 0, val); 2375254Sgavinm } 2385254Sgavinm 2395254Sgavinm static uint32_t 2405254Sgavinm authamd_pcicfg_read(uint_t chipid, uint_t func, uint_t reg) 2415254Sgavinm { 2425254Sgavinm ASSERT(chipid + 24 <= 31); 2435254Sgavinm ASSERT((func & 7) == func); 2445254Sgavinm ASSERT((reg & 3) == 0 && reg < 256); 2455254Sgavinm 2465254Sgavinm return (cmi_pci_getl(0, chipid + 24, func, reg, 0, 0)); 2475254Sgavinm } 2485254Sgavinm 2495254Sgavinm void 2505254Sgavinm authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2515254Sgavinm { 2525254Sgavinm uint64_t hwcr; 2535254Sgavinm 2545254Sgavinm if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS) 2555254Sgavinm return; 2565254Sgavinm 2575254Sgavinm authamd->amd_hwcr = hwcr; 2585254Sgavinm 2595254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2605254Sgavinm hwcr |= AMD_HWCR_MCI_STATUS_WREN; 2615254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2625254Sgavinm } 2635254Sgavinm } 2645254Sgavinm 2655254Sgavinm void 2665254Sgavinm authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2675254Sgavinm { 2685254Sgavinm uint64_t hwcr = authamd->amd_hwcr; 2695254Sgavinm 2705254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2715254Sgavinm hwcr &= ~AMD_HWCR_MCI_STATUS_WREN; 2725254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2735254Sgavinm } 2745254Sgavinm } 2755254Sgavinm 2765254Sgavinm /* 2775254Sgavinm * Read EccCnt repeatedly for all possible channel/chip-select combos: 2785254Sgavinm * 2795254Sgavinm * - read sparectl register 2805254Sgavinm * - if EccErrCntWrEn is set, clear that bit in the just-read value 2815254Sgavinm * and write it back to sparectl; this *may* clobber the EccCnt 2825254Sgavinm * for the channel/chip-select combination currently selected, so 2835254Sgavinm * we leave this bit clear if we had to clear it 2845254Sgavinm * - cycle through all channel/chip-select combinations writing each 2855254Sgavinm * combination to sparectl before reading the register back for 2865254Sgavinm * EccCnt for that combination; since EccErrCntWrEn is clear 2875254Sgavinm * the writes to select what count to read will not themselves 2885254Sgavinm * zero any counts 2895254Sgavinm */ 2905254Sgavinm static int 2915254Sgavinm authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl) 2925254Sgavinm { 2935254Sgavinm union mcreg_sparectl sparectl; 2945254Sgavinm uint_t chipid = authamd->amd_shared->acs_chipid; 2955254Sgavinm uint_t family = authamd->amd_shared->acs_family; 2965254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 2975254Sgavinm int chan, cs; 2985254Sgavinm 2995254Sgavinm /* 3005254Sgavinm * Check for feature support; this macro will test down to the 3015254Sgavinm * family revision number, whereafter we'll switch on family 3025254Sgavinm * assuming that future revisions will use the same register 3035254Sgavinm * format. 3045254Sgavinm */ 3055254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) { 3065254Sgavinm bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt)); 3075254Sgavinm return (0); 3085254Sgavinm } 3095254Sgavinm 3105254Sgavinm MCREG_VAL32(&sparectl) = 3115254Sgavinm authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 3125254Sgavinm 3135254Sgavinm switch (family) { 3145254Sgavinm case AUTHAMD_FAMILY_F: 3155254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0; 3165254Sgavinm break; 3175254Sgavinm 3185254Sgavinm case AUTHAMD_FAMILY_10: 3195254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0; 3205254Sgavinm break; 3215254Sgavinm } 3225254Sgavinm 3235254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 3245254Sgavinm switch (family) { 3255254Sgavinm case AUTHAMD_FAMILY_F: 3265254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 3275254Sgavinm chan; 3285254Sgavinm break; 3295254Sgavinm 3305254Sgavinm case AUTHAMD_FAMILY_10: 3315254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 3325254Sgavinm chan; 3335254Sgavinm break; 3345254Sgavinm } 3355254Sgavinm 3365254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 3375254Sgavinm switch (family) { 3385254Sgavinm case AUTHAMD_FAMILY_F: 3395254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 3405254Sgavinm EccErrCntDramCs) = cs; 3415254Sgavinm break; 3425254Sgavinm 3435254Sgavinm case AUTHAMD_FAMILY_10: 3445254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 3455254Sgavinm EccErrCntDramCs) = cs; 3465254Sgavinm break; 3475254Sgavinm } 3485254Sgavinm 3495254Sgavinm authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, 3505254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 3515254Sgavinm 3525254Sgavinm MCREG_VAL32(&sparectl) = authamd_pcicfg_read(chipid, 3535254Sgavinm MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 3545254Sgavinm 3555254Sgavinm switch (family) { 3565254Sgavinm case AUTHAMD_FAMILY_F: 3575254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3585254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCnt); 3595254Sgavinm break; 3605254Sgavinm case AUTHAMD_FAMILY_10: 3615254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3625254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCnt); 3635254Sgavinm break; 3645254Sgavinm } 3655254Sgavinm } 3665254Sgavinm } 3675254Sgavinm 3685254Sgavinm return (1); 3695254Sgavinm } 3705254Sgavinm 3715254Sgavinm /* 3725254Sgavinm * Clear EccCnt for all possible channel/chip-select combos: 3735254Sgavinm * 3745254Sgavinm * - set EccErrCntWrEn in sparectl, if necessary 3755254Sgavinm * - write 0 to EccCnt for all channel/chip-select combinations 3765254Sgavinm * - clear EccErrCntWrEn 3775254Sgavinm * 3785254Sgavinm * If requested also disable the interrupts taken on counter overflow 3795254Sgavinm * and on swap done. 3805254Sgavinm */ 3815254Sgavinm static void 3825254Sgavinm authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint) 3835254Sgavinm { 3845254Sgavinm union mcreg_sparectl sparectl; 3855254Sgavinm uint_t chipid = authamd->amd_shared->acs_chipid; 3865254Sgavinm uint_t family = authamd->amd_shared->acs_family; 3875254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 3885254Sgavinm int chan, cs; 3895254Sgavinm 3905254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) 3915254Sgavinm return; 3925254Sgavinm 3935254Sgavinm MCREG_VAL32(&sparectl) = 3945254Sgavinm authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 3955254Sgavinm 3965254Sgavinm switch (family) { 3975254Sgavinm case AUTHAMD_FAMILY_F: 3985254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1; 3995254Sgavinm if (clrint) { 4005254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0; 4015254Sgavinm MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0; 4025254Sgavinm } 4035254Sgavinm break; 4045254Sgavinm 4055254Sgavinm case AUTHAMD_FAMILY_10: 4065254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 1; 4075254Sgavinm if (clrint) { 4085254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrInt) = 0; 4095254Sgavinm MCREG_FIELD_10_revAB(&sparectl, SwapDoneInt) = 0; 4105254Sgavinm } 4115254Sgavinm break; 4125254Sgavinm } 4135254Sgavinm 4145254Sgavinm authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, 4155254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4165254Sgavinm 4175254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 4185254Sgavinm switch (family) { 4195254Sgavinm case AUTHAMD_FAMILY_F: 4205254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 4215254Sgavinm chan; 4225254Sgavinm break; 4235254Sgavinm 4245254Sgavinm case AUTHAMD_FAMILY_10: 4255254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 4265254Sgavinm chan; 4275254Sgavinm break; 4285254Sgavinm } 4295254Sgavinm 4305254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 4315254Sgavinm switch (family) { 4325254Sgavinm case AUTHAMD_FAMILY_F: 4335254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4345254Sgavinm EccErrCntDramCs) = cs; 4355254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4365254Sgavinm EccErrCnt) = 0; 4375254Sgavinm break; 4385254Sgavinm 4395254Sgavinm case AUTHAMD_FAMILY_10: 4405254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4415254Sgavinm EccErrCntDramCs) = cs; 4425254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4435254Sgavinm EccErrCnt) = 0; 4445254Sgavinm break; 4455254Sgavinm } 4465254Sgavinm 4475254Sgavinm authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, 4485254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4495254Sgavinm } 4505254Sgavinm } 4515254Sgavinm } 4525254Sgavinm 4535254Sgavinm /* 4545254Sgavinm * cms_init entry point. 4555254Sgavinm * 4565254Sgavinm * This module provides broad model-specific support for AMD families 4575254Sgavinm * 0x6, 0xf and 0x10. Future families will have to be evaluated once their 4585254Sgavinm * documentation is available. 4595254Sgavinm */ 4605254Sgavinm int 4615254Sgavinm authamd_init(cmi_hdl_t hdl, void **datap) 4625254Sgavinm { 4635254Sgavinm uint_t chipid = cmi_hdl_chipid(hdl); 4645254Sgavinm struct authamd_chipshared *sp, *osp; 4655254Sgavinm uint_t family = cmi_hdl_family(hdl); 4665254Sgavinm authamd_data_t *authamd; 4675254Sgavinm uint64_t cap; 4685254Sgavinm 4695254Sgavinm if (authamd_ms_support_disable || !AUTHAMD_SUPPORTED(family)) 4705254Sgavinm return (ENOTSUP); 4715254Sgavinm 4725254Sgavinm if (!(x86_feature & X86_MCA)) 4735254Sgavinm return (ENOTSUP); 4745254Sgavinm 4755254Sgavinm if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) 4765254Sgavinm return (ENOTSUP); 4775254Sgavinm 4785254Sgavinm if (!(cap & MCG_CAP_CTL_P)) 4795254Sgavinm return (ENOTSUP); 4805254Sgavinm 4815254Sgavinm authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP); 4825254Sgavinm cmi_hdl_hold(hdl); /* release in fini */ 4835254Sgavinm authamd->amd_hdl = hdl; 4845254Sgavinm 4855254Sgavinm if ((sp = authamd_shared[chipid]) == NULL) { 4865254Sgavinm sp = kmem_zalloc(sizeof (struct authamd_chipshared), KM_SLEEP); 487*6000Sgavinm sp->acs_chipid = chipid; 488*6000Sgavinm sp->acs_family = family; 489*6000Sgavinm sp->acs_rev = cmi_hdl_chiprev(hdl); 490*6000Sgavinm membar_producer(); 491*6000Sgavinm 4925254Sgavinm osp = atomic_cas_ptr(&authamd_shared[chipid], NULL, sp); 4935254Sgavinm if (osp != NULL) { 4945254Sgavinm kmem_free(sp, sizeof (struct authamd_chipshared)); 4955254Sgavinm sp = osp; 4965254Sgavinm } 4975254Sgavinm } 4985254Sgavinm authamd->amd_shared = sp; 4995254Sgavinm 5005254Sgavinm return (0); 5015254Sgavinm } 5025254Sgavinm 5035254Sgavinm /* 5045254Sgavinm * cms_logout_size entry point. 5055254Sgavinm */ 5065254Sgavinm /*ARGSUSED*/ 5075254Sgavinm size_t 5085254Sgavinm authamd_logout_size(cmi_hdl_t hdl) 5095254Sgavinm { 5105254Sgavinm return (sizeof (struct authamd_logout)); 5115254Sgavinm } 5125254Sgavinm 5135254Sgavinm /* 5145254Sgavinm * cms_mcgctl_val entry point 5155254Sgavinm * 5165254Sgavinm * Instead of setting all bits to 1 we can set just those for the 5175254Sgavinm * error detector banks known to exist. 5185254Sgavinm */ 5195254Sgavinm /*ARGSUSED*/ 5205254Sgavinm uint64_t 5215254Sgavinm authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed) 5225254Sgavinm { 5235254Sgavinm return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed); 5245254Sgavinm } 5255254Sgavinm 5265254Sgavinm /* 5275254Sgavinm * cms_bankctl_skipinit entry point 5285254Sgavinm * 5295254Sgavinm * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC) 5305254Sgavinm * may produce spurious machine checks. 5315639Sgavinm * 5325639Sgavinm * Only allow a single core to setup the NorthBridge MCi_CTL register. 5335254Sgavinm */ 5345254Sgavinm /*ARGSUSED*/ 5355254Sgavinm boolean_t 5365254Sgavinm authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank) 5375254Sgavinm { 5385254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 5395639Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 5405254Sgavinm 5415639Sgavinm if (authamd->amd_shared->acs_family == AUTHAMD_FAMILY_6) 5425639Sgavinm return (bank == 0 ? B_TRUE : B_FALSE); 5435639Sgavinm 5445639Sgavinm if (AUTHAMD_NBONCHIP(rev) && bank == AMD_MCA_BANK_NB) { 5455639Sgavinm return (authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCA) == 5465639Sgavinm B_TRUE ? B_FALSE : B_TRUE); 5475639Sgavinm } 5485639Sgavinm 5495639Sgavinm return (B_FALSE); 5505254Sgavinm } 5515254Sgavinm 5525254Sgavinm /* 5535254Sgavinm * cms_bankctl_val entry point 5545254Sgavinm */ 5555254Sgavinm uint64_t 5565254Sgavinm authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed) 5575254Sgavinm { 5585254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 5595254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 5605254Sgavinm uint64_t val = proposed; 5615254Sgavinm 5625254Sgavinm /* 5635254Sgavinm * The Intel MCA says we can write all 1's to enable #MC for 5645254Sgavinm * all errors, and AMD docs say much the same. But, depending 5655254Sgavinm * perhaps on other config registers, taking machine checks 5665254Sgavinm * for some errors such as GART TLB errors and master/target 5675254Sgavinm * aborts may be bad - they set UC and sometime also PCC, but 5685254Sgavinm * we should not always panic for these error types. 5695254Sgavinm * 5705254Sgavinm * Our cms_error_action entry point can suppress such panics, 5715254Sgavinm * however we can also use the cms_bankctl_val entry point to 5725254Sgavinm * veto enabling of some of the known villains in the first place. 5735254Sgavinm */ 5745254Sgavinm if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev)) 5755254Sgavinm val &= ~AMD_NB_EN_GARTTBLWK; 5765254Sgavinm 5775254Sgavinm return (val); 5785254Sgavinm } 5795254Sgavinm 5805254Sgavinm /* 5815327Sgavinm * Bits to add to NB MCA config (after watchdog config). 5825327Sgavinm */ 5835327Sgavinm uint32_t authamd_nb_mcacfg_add = AMD_NB_CFG_ADD_CMN; 5845327Sgavinm 5855327Sgavinm /* 5865327Sgavinm * Bits to remove from NB MCA config (after watchdog config) 5875327Sgavinm */ 5885327Sgavinm uint32_t authamd_nb_mcacfg_remove = AMD_NB_CFG_REMOVE_CMN; 5895327Sgavinm 5905327Sgavinm /* 5915327Sgavinm * NB Watchdog policy, and rate we use if enabling. 5925327Sgavinm */ 5935327Sgavinm enum { 5945327Sgavinm AUTHAMD_NB_WDOG_LEAVEALONE, 5955327Sgavinm AUTHAMD_NB_WDOG_DISABLE, 5965327Sgavinm AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED, 5975327Sgavinm AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE 5985327Sgavinm } authamd_nb_watchdog_policy = AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED; 5995327Sgavinm 6005327Sgavinm uint32_t authamd_nb_mcacfg_wdog = AMD_NB_CFG_WDOGTMRCNTSEL_4095 | 6015327Sgavinm AMD_NB_CFG_WDOGTMRBASESEL_1MS; 6025327Sgavinm 6035327Sgavinm /* 6045327Sgavinm * Per-core cache scrubbing policy and rates. 6055327Sgavinm */ 6065327Sgavinm enum { 6075327Sgavinm AUTHAMD_SCRUB_BIOSDEFAULT, /* leave as BIOS configured */ 6085327Sgavinm AUTHAMD_SCRUB_FIXED, /* assign our chosen rate */ 6095327Sgavinm AUTHAMD_SCRUB_MAX /* use higher of ours and BIOS rate */ 6105327Sgavinm } authamd_scrub_policy = AUTHAMD_SCRUB_MAX; 6115327Sgavinm 6125327Sgavinm uint32_t authamd_scrub_rate_dcache = 0xf; /* 64K per 0.67 seconds */ 6135327Sgavinm uint32_t authamd_scrub_rate_l2cache = 0xe; /* 1MB per 5.3 seconds */ 6145327Sgavinm uint32_t authamd_scrub_rate_l3cache = 0xd; /* 1MB per 2.7 seconds */ 6155327Sgavinm 6165327Sgavinm static uint32_t 6175327Sgavinm authamd_scrubrate(uint32_t osrate, uint32_t biosrate, const char *varnm) 6185327Sgavinm { 6195327Sgavinm uint32_t rate; 6205327Sgavinm 6215327Sgavinm if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) { 6225327Sgavinm cmn_err(CE_WARN, "%s is too large, resetting to 0x%x\n", 6235327Sgavinm varnm, AMD_NB_SCRUBCTL_RATE_MAX); 6245327Sgavinm osrate = AMD_NB_SCRUBCTL_RATE_MAX; 6255327Sgavinm } 6265327Sgavinm 6275327Sgavinm switch (authamd_scrub_policy) { 6285327Sgavinm case AUTHAMD_SCRUB_FIXED: 6295327Sgavinm rate = osrate; 6305327Sgavinm break; 6315327Sgavinm 6325327Sgavinm default: 6335327Sgavinm cmn_err(CE_WARN, "Unknown authamd_scrub_policy %d - " 6345327Sgavinm "using default policy of AUTHAMD_SCRUB_MAX", 6355327Sgavinm authamd_scrub_policy); 6365327Sgavinm /*FALLTHRU*/ 6375327Sgavinm 6385327Sgavinm case AUTHAMD_SCRUB_MAX: 6395327Sgavinm if (osrate != 0 && biosrate != 0) 6405327Sgavinm rate = MIN(osrate, biosrate); /* small is fast */ 6415327Sgavinm else 6425327Sgavinm rate = osrate ? osrate : biosrate; 6435327Sgavinm } 6445327Sgavinm 6455327Sgavinm return (rate); 6465327Sgavinm } 6475327Sgavinm 6485327Sgavinm /* 6495254Sgavinm * cms_mca_init entry point. 6505254Sgavinm */ 6515254Sgavinm /*ARGSUSED*/ 6525254Sgavinm void 6535254Sgavinm authamd_mca_init(cmi_hdl_t hdl, int nbanks) 6545254Sgavinm { 6555254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 6565254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 6575327Sgavinm uint_t chipid = authamd->amd_shared->acs_chipid; 6585254Sgavinm 6595254Sgavinm /* 6605254Sgavinm * On chips with a NB online spare control register take control 6615254Sgavinm * and clear ECC counts. 6625254Sgavinm */ 6635254Sgavinm if (AUTHAMD_HAS_ONLINESPARECTL(rev) && 6645254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) { 6655254Sgavinm authamd_clear_ecccnt(authamd, B_TRUE); 6665254Sgavinm } 6675254Sgavinm 6685254Sgavinm /* 6695254Sgavinm * And since we are claiming the telemetry stop the BIOS receiving 6705254Sgavinm * an SMI on NB threshold overflow. 6715254Sgavinm */ 6725254Sgavinm if (AUTHAMD_NBMISC_NUM(rev) && 6735254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) { 6745254Sgavinm union mcmsr_nbmisc nbm; 6755254Sgavinm int i; 6765254Sgavinm 6775254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 6785254Sgavinm 6795254Sgavinm for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) { 6805254Sgavinm if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i), 6815254Sgavinm (uint64_t *)&nbm) != CMI_SUCCESS) 6825254Sgavinm continue; 6835254Sgavinm 6845254Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && 6855254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) && 6865254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) { 6875254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0; 6885254Sgavinm } else if (X86_CHIPREV_ATLEAST(rev, 6895254Sgavinm X86_CHIPREV_AMD_10_REV_A) && 6905254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) && 6915254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) { 6925254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0; 6935254Sgavinm } 6945254Sgavinm 6955254Sgavinm (void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i), 6965254Sgavinm MCMSR_VAL(&nbm)); 6975254Sgavinm } 6985254Sgavinm 6995254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 7005254Sgavinm } 7015327Sgavinm 7025327Sgavinm /* 7035327Sgavinm * NB MCA Configuration Register. 7045327Sgavinm */ 7055327Sgavinm if (AUTHAMD_DO_NBMCACFG(rev) && 7065327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCACFG)) { 7075327Sgavinm uint32_t val = authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, 7085327Sgavinm MC_CTL_REG_NBCFG); 7095327Sgavinm 7105327Sgavinm switch (authamd_nb_watchdog_policy) { 7115327Sgavinm case AUTHAMD_NB_WDOG_LEAVEALONE: 7125327Sgavinm break; 7135327Sgavinm 7145327Sgavinm case AUTHAMD_NB_WDOG_DISABLE: 7155327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7165327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK); 7175327Sgavinm val |= AMD_NB_CFG_WDOGTMRDIS; 7185327Sgavinm break; 7195327Sgavinm 7205327Sgavinm default: 7215327Sgavinm cmn_err(CE_NOTE, "authamd_nb_watchdog_policy=%d " 7225327Sgavinm "unrecognised, using default policy", 7235327Sgavinm authamd_nb_watchdog_policy); 7245327Sgavinm /*FALLTHRU*/ 7255327Sgavinm 7265327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED: 7275327Sgavinm if (!(val & AMD_NB_CFG_WDOGTMRDIS)) 7285327Sgavinm break; /* if enabled leave rate intact */ 7295327Sgavinm /*FALLTHRU*/ 7305327Sgavinm 7315327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE: 7325327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7335327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK | 7345327Sgavinm AMD_NB_CFG_WDOGTMRDIS); 7355327Sgavinm val |= authamd_nb_mcacfg_wdog; 7365327Sgavinm break; 7375327Sgavinm } 7385327Sgavinm 7395327Sgavinm /* 7405327Sgavinm * Bit 0 of the NB MCA Config register is reserved on family 7415327Sgavinm * 0x10. 7425327Sgavinm */ 7435327Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 7445327Sgavinm authamd_nb_mcacfg_add &= ~AMD_NB_CFG_CPUECCERREN; 7455327Sgavinm 7465327Sgavinm val &= ~authamd_nb_mcacfg_remove; 7475327Sgavinm val |= authamd_nb_mcacfg_add; 7485327Sgavinm 7495327Sgavinm authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG, 7505327Sgavinm val); 7515327Sgavinm } 7525327Sgavinm 7535327Sgavinm /* 7545327Sgavinm * Cache scrubbing. We can't enable DRAM scrubbing since 7555327Sgavinm * we don't know the DRAM base for this node. 7565327Sgavinm */ 7575327Sgavinm if (AUTHAMD_HAS_CHIPSCRUB(rev) && 7585327Sgavinm authamd_scrub_policy != AUTHAMD_SCRUB_BIOSDEFAULT && 7595327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_CACHESCRUB)) { 7605327Sgavinm uint32_t val = authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, 7615327Sgavinm MC_CTL_REG_SCRUBCTL); 7625327Sgavinm int l3cap = 0; 7635327Sgavinm 7645327Sgavinm if (AUTHAMD_L3CAPABLE(rev)) { 7655327Sgavinm l3cap = (authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, 7665327Sgavinm MC_CTL_REG_NBCAP) & MC_NBCAP_L3CAPABLE) != 0; 7675327Sgavinm } 7685327Sgavinm 7695327Sgavinm authamd_scrub_rate_dcache = 7705327Sgavinm authamd_scrubrate(authamd_scrub_rate_dcache, 7715327Sgavinm (val & AMD_NB_SCRUBCTL_DC_MASK) >> AMD_NB_SCRUBCTL_DC_SHIFT, 7725327Sgavinm "authamd_scrub_rate_dcache"); 7735327Sgavinm 7745327Sgavinm authamd_scrub_rate_l2cache = 7755327Sgavinm authamd_scrubrate(authamd_scrub_rate_l2cache, 7765327Sgavinm (val & AMD_NB_SCRUBCTL_L2_MASK) >> AMD_NB_SCRUBCTL_L2_SHIFT, 7775327Sgavinm "authamd_scrub_rate_l2cache"); 7785327Sgavinm 7795327Sgavinm authamd_scrub_rate_l3cache = l3cap ? 7805327Sgavinm authamd_scrubrate(authamd_scrub_rate_l3cache, 7815327Sgavinm (val & AMD_NB_SCRUBCTL_L3_MASK) >> AMD_NB_SCRUBCTL_L3_SHIFT, 7825327Sgavinm "authamd_scrub_rate_l3cache") : 0; 7835327Sgavinm 7845327Sgavinm val = AMD_NB_MKSCRUBCTL(authamd_scrub_rate_l3cache, 7855327Sgavinm authamd_scrub_rate_dcache, authamd_scrub_rate_l2cache, 7865327Sgavinm val & AMD_NB_SCRUBCTL_DRAM_MASK); 7875327Sgavinm 7885327Sgavinm authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, 7895327Sgavinm MC_CTL_REG_SCRUBCTL, val); 7905327Sgavinm } 7915327Sgavinm 7925327Sgavinm } 7935327Sgavinm 7945327Sgavinm /* 7955327Sgavinm * cms_poll_ownermask entry point. 7965327Sgavinm */ 7975327Sgavinm uint64_t 7985327Sgavinm authamd_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl) 7995327Sgavinm { 8005327Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 8015327Sgavinm struct authamd_chipshared *acsp = authamd->amd_shared; 8025327Sgavinm hrtime_t now = gethrtime_waitfree(); 8035327Sgavinm hrtime_t last = acsp->acs_poll_timestamp; 8045327Sgavinm int dopoll = 0; 8055327Sgavinm 8065327Sgavinm if (now - last > 2 * pintvl || last == 0) { 8075327Sgavinm acsp->acs_pollowner = hdl; 8085327Sgavinm dopoll = 1; 8095327Sgavinm } else if (acsp->acs_pollowner == hdl) { 8105327Sgavinm dopoll = 1; 8115327Sgavinm } 8125327Sgavinm 8135327Sgavinm if (dopoll) 8145327Sgavinm acsp->acs_poll_timestamp = now; 8155327Sgavinm 8165327Sgavinm return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB)); 8175327Sgavinm 8185254Sgavinm } 8195254Sgavinm 8205254Sgavinm /* 8215254Sgavinm * cms_bank_logout entry point. 8225254Sgavinm */ 8235254Sgavinm /*ARGSUSED*/ 8245254Sgavinm void 8255254Sgavinm authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status, 8265254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 8275254Sgavinm { 8285254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 8295254Sgavinm struct authamd_logout *msl = mslogout; 8305254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 8315254Sgavinm 8325254Sgavinm if (msl == NULL) 8335254Sgavinm return; 8345254Sgavinm 8355254Sgavinm /* 8365254Sgavinm * For main memory ECC errors on revisions with an Online Spare 8375254Sgavinm * Control Register grab the ECC counts by channel and chip-select 8385254Sgavinm * and reset them to 0. 8395254Sgavinm */ 8405254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 8415254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status) && 8425254Sgavinm AUTHAMD_HAS_ONLINESPARECTL(rev)) { 8435254Sgavinm if (authamd_read_ecccnt(authamd, msl)) 8445254Sgavinm authamd_clear_ecccnt(authamd, B_FALSE); 8455254Sgavinm } 8465254Sgavinm } 8475254Sgavinm 8485254Sgavinm /* 8495254Sgavinm * cms_error_action entry point 8505254Sgavinm */ 8515254Sgavinm 8525254Sgavinm int authamd_forgive_uc = 0; /* For test/debug only */ 8535254Sgavinm int authamd_forgive_pcc = 0; /* For test/debug only */ 8545254Sgavinm int authamd_fake_poison = 0; /* For test/debug only */ 8555254Sgavinm 8565254Sgavinm /*ARGSUSED*/ 8575254Sgavinm uint32_t 8585254Sgavinm authamd_error_action(cmi_hdl_t hdl, int ismc, int bank, 8595254Sgavinm uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) 8605254Sgavinm { 8615254Sgavinm authamd_error_disp_t *disp; 8625254Sgavinm uint32_t rv = 0; 8635254Sgavinm 8645254Sgavinm if (authamd_forgive_uc) 8655254Sgavinm rv |= CMS_ERRSCOPE_CLEARED_UC; 8665254Sgavinm 8675254Sgavinm if (authamd_forgive_pcc) 8685254Sgavinm rv |= CMS_ERRSCOPE_CURCONTEXT_OK; 8695254Sgavinm 8705254Sgavinm if (authamd_fake_poison && status & MSR_MC_STATUS_UC) 8715254Sgavinm rv |= CMS_ERRSCOPE_POISONED; 8725254Sgavinm 8735254Sgavinm if (rv) 8745254Sgavinm return (rv); 8755254Sgavinm 8765254Sgavinm disp = authamd_disp_match(hdl, bank, status, addr, misc, mslogout); 8775254Sgavinm 8785254Sgavinm if (disp == &authamd_gart_disp) { 8795254Sgavinm /* 8805254Sgavinm * GART walk errors set UC and possibly PCC (if source CPU) 8815254Sgavinm * but should not be regarded as terminal. 8825254Sgavinm */ 8835254Sgavinm return (CMS_ERRSCOPE_IGNORE_ERR); 8845254Sgavinm } 8855254Sgavinm 8865254Sgavinm /* 8875254Sgavinm * May also want to consider master abort and target abort. These 8885254Sgavinm * also set UC and PCC (if src CPU) but the requester gets -1 8895254Sgavinm * and I believe the IO stuff in Solaris will handle that. 8905254Sgavinm */ 8915254Sgavinm 8925254Sgavinm return (rv); 8935254Sgavinm } 8945254Sgavinm 8955254Sgavinm /* 8965254Sgavinm * cms_disp_match entry point 8975254Sgavinm */ 8985254Sgavinm /*ARGSUSED*/ 8995254Sgavinm cms_cookie_t 9005254Sgavinm authamd_disp_match(cmi_hdl_t hdl, int bank, uint64_t status, 9015254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 9025254Sgavinm { 9035254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 9045254Sgavinm /* uint16_t errcode = MCAX86_ERRCODE(status); */ 9055254Sgavinm uint16_t exterrcode = AMD_EXT_ERRCODE(status); 9065254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 9075254Sgavinm 9085254Sgavinm /* 9095254Sgavinm * Recognise main memory ECC errors 9105254Sgavinm */ 9115254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 9125254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status)) { 9135254Sgavinm if (status & AMD_BANK_STAT_CECC) { 9145254Sgavinm return (exterrcode == 0 ? &authamd_memce_disp : 9155254Sgavinm &authamd_ckmemce_disp); 9165254Sgavinm } else if (status & AMD_BANK_STAT_UECC) { 9175254Sgavinm return (exterrcode == 0 ? &authamd_memue_disp : 9185254Sgavinm &authamd_ckmemue_disp); 9195254Sgavinm } 9205254Sgavinm } 9215254Sgavinm 9225254Sgavinm /* 9235254Sgavinm * Recognise GART walk errors 9245254Sgavinm */ 9255254Sgavinm if (AUTHAMD_NOGARTTBLWLK_MC(rev) && AUTHAMD_IS_GARTERR(bank, status)) 9265254Sgavinm return (&authamd_gart_disp); 9275254Sgavinm 9285254Sgavinm return (NULL); 9295254Sgavinm } 9305254Sgavinm 9315254Sgavinm /* 9325254Sgavinm * cms_ereport_class entry point 9335254Sgavinm */ 9345254Sgavinm /*ARGSUSED*/ 9355254Sgavinm void 9365254Sgavinm authamd_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, 9375254Sgavinm const char **cpuclsp, const char **leafclsp) 9385254Sgavinm { 9395254Sgavinm const authamd_error_disp_t *aed = mscookie; 9405254Sgavinm 9415254Sgavinm if (aed == NULL) 9425254Sgavinm return; 9435254Sgavinm 9445254Sgavinm if (aed->aad_subclass != NULL) 9455254Sgavinm *cpuclsp = aed->aad_subclass; 9465254Sgavinm if (aed->aad_leafclass != NULL) 9475254Sgavinm *leafclsp = aed->aad_leafclass; 9485254Sgavinm } 9495254Sgavinm 9505254Sgavinm /*ARGSUSED*/ 9515254Sgavinm static void 9525254Sgavinm authamd_ereport_add_resource(cmi_hdl_t hdl, authamd_data_t *authamd, 9535254Sgavinm nvlist_t *ereport, nv_alloc_t *nva, void *mslogout) 9545254Sgavinm { 9555254Sgavinm nvlist_t *elems[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; 9565254Sgavinm uint8_t counts[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; 9575254Sgavinm authamd_logout_t *msl; 9585254Sgavinm nvlist_t *nvl; 9595254Sgavinm int nelems = 0; 9605254Sgavinm int i, chan, cs; 9615254Sgavinm 9625254Sgavinm if ((msl = mslogout) == NULL) 9635254Sgavinm return; 9645254Sgavinm 9655254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 9665254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 9675254Sgavinm if (msl->aal_eccerrcnt[chan][cs] == 0) 9685254Sgavinm continue; 9695254Sgavinm 9705254Sgavinm if ((nvl = fm_nvlist_create(nva)) == NULL) 9715254Sgavinm continue; 9725254Sgavinm 9735254Sgavinm elems[nelems] = nvl; 9745254Sgavinm counts[nelems++] = msl->aal_eccerrcnt[chan][cs]; 9755254Sgavinm 9765254Sgavinm fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 5, 9775254Sgavinm "motherboard", 0, 9785254Sgavinm "chip", authamd->amd_shared->acs_chipid, 9795254Sgavinm "memory-controller", 0, 9805254Sgavinm "dram-channel", chan, 9815254Sgavinm "chip-select", cs); 9825254Sgavinm } 9835254Sgavinm } 9845254Sgavinm 9855254Sgavinm if (nelems == 0) 9865254Sgavinm return; 9875254Sgavinm 9885254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE, 9895254Sgavinm DATA_TYPE_NVLIST_ARRAY, nelems, elems, 9905254Sgavinm NULL); 9915254Sgavinm 9925254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT, 9935254Sgavinm DATA_TYPE_UINT8_ARRAY, nelems, &counts[0], 9945254Sgavinm NULL); 9955254Sgavinm 9965254Sgavinm for (i = 0; i < nelems; i++) 9975254Sgavinm fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE); 9985254Sgavinm } 9995254Sgavinm 10005254Sgavinm /* 10015254Sgavinm * cms_ereport_add_logout entry point 10025254Sgavinm */ 10035254Sgavinm /*ARGSUSED*/ 10045254Sgavinm void 10055254Sgavinm authamd_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, nv_alloc_t *nva, 10065254Sgavinm int bank, uint64_t status, uint64_t addr, uint64_t misc, 10075254Sgavinm void *mslogout, cms_cookie_t mscookie) 10085254Sgavinm { 10095254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 10105254Sgavinm const authamd_error_disp_t *aed = mscookie; 10115254Sgavinm uint64_t members; 10125254Sgavinm 10135254Sgavinm if (aed == NULL) 10145254Sgavinm return; 10155254Sgavinm 10165254Sgavinm members = aed->aad_ereport_members; 10175254Sgavinm 10185254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND) { 10195254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND, 10205254Sgavinm DATA_TYPE_UINT16, (uint16_t)AMD_BANK_SYND(status), 10215254Sgavinm NULL); 10225254Sgavinm 10235254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { 10245254Sgavinm fm_payload_set(ereport, 10255254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, 10265254Sgavinm DATA_TYPE_STRING, "E", 10275254Sgavinm NULL); 10285254Sgavinm } 10295254Sgavinm } 10305254Sgavinm 10315254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND) { 10325254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND, 10335254Sgavinm DATA_TYPE_UINT16, (uint16_t)AMD_NB_STAT_CKSYND(status), 10345254Sgavinm NULL); 10355254Sgavinm 10365254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { 10375254Sgavinm fm_payload_set(ereport, 10385254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, 10395254Sgavinm DATA_TYPE_STRING, "C", 10405254Sgavinm NULL); 10415254Sgavinm } 10425254Sgavinm } 10435254Sgavinm 10445254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE && 10455254Sgavinm status & MSR_MC_STATUS_ADDRV) { 10465254Sgavinm authamd_ereport_add_resource(hdl, authamd, ereport, nva, 10475254Sgavinm mslogout); 10485254Sgavinm } 10495254Sgavinm } 10505254Sgavinm 10515254Sgavinm /* 10525254Sgavinm * cms_msrinject entry point 10535254Sgavinm */ 10545254Sgavinm cms_errno_t 10555254Sgavinm authamd_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val) 10565254Sgavinm { 10575254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 10585254Sgavinm cms_errno_t rv = CMSERR_BADMSRWRITE; 10595254Sgavinm 10605254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 10615254Sgavinm if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS) 10625254Sgavinm rv = CMS_SUCCESS; 10635254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 10645254Sgavinm 10655254Sgavinm return (rv); 10665254Sgavinm } 10675254Sgavinm 10685254Sgavinm cms_api_ver_t _cms_api_version = CMS_API_VERSION_0; 10695254Sgavinm 10705254Sgavinm const cms_ops_t _cms_ops = { 10715254Sgavinm authamd_init, /* cms_init */ 10725254Sgavinm NULL, /* cms_post_startup */ 10735254Sgavinm NULL, /* cms_post_mpstartup */ 10745254Sgavinm authamd_logout_size, /* cms_logout_size */ 10755254Sgavinm authamd_mcgctl_val, /* cms_mcgctl_val */ 10765254Sgavinm authamd_bankctl_skipinit, /* cms_bankctl_skipinit */ 10775254Sgavinm authamd_bankctl_val, /* cms_bankctl_val */ 10785254Sgavinm NULL, /* cms_bankstatus_skipinit */ 10795254Sgavinm NULL, /* cms_bankstatus_val */ 10805254Sgavinm authamd_mca_init, /* cms_mca_init */ 10815327Sgavinm authamd_poll_ownermask, /* cms_poll_ownermask */ 10825254Sgavinm authamd_bank_logout, /* cms_bank_logout */ 10835254Sgavinm authamd_error_action, /* cms_error_action */ 10845254Sgavinm authamd_disp_match, /* cms_disp_match */ 10855254Sgavinm authamd_ereport_class, /* cms_ereport_class */ 10865254Sgavinm NULL, /* cms_ereport_detector */ 10875254Sgavinm NULL, /* cms_ereport_includestack */ 10885254Sgavinm authamd_ereport_add_logout, /* cms_ereport_add_logout */ 10895254Sgavinm authamd_msrinject, /* cms_msrinject */ 10905254Sgavinm NULL, /* cms_fini */ 10915254Sgavinm }; 10925254Sgavinm 10935254Sgavinm static struct modlcpu modlcpu = { 10945254Sgavinm &mod_cpuops, 10955254Sgavinm "Generic AMD model-specific MCA" 10965254Sgavinm }; 10975254Sgavinm 10985254Sgavinm static struct modlinkage modlinkage = { 10995254Sgavinm MODREV_1, 11005254Sgavinm (void *)&modlcpu, 11015254Sgavinm NULL 11025254Sgavinm }; 11035254Sgavinm 11045254Sgavinm int 11055254Sgavinm _init(void) 11065254Sgavinm { 11075254Sgavinm return (mod_install(&modlinkage)); 11085254Sgavinm } 11095254Sgavinm 11105254Sgavinm int 11115254Sgavinm _info(struct modinfo *modinfop) 11125254Sgavinm { 11135254Sgavinm return (mod_info(&modlinkage, modinfop)); 11145254Sgavinm } 11155254Sgavinm 11165254Sgavinm int 11175254Sgavinm _fini(void) 11185254Sgavinm { 11195254Sgavinm return (mod_remove(&modlinkage)); 11205254Sgavinm } 1121