11642Sgavinm /* 21642Sgavinm * CDDL HEADER START 31642Sgavinm * 41642Sgavinm * The contents of this file are subject to the terms of the 51642Sgavinm * Common Development and Distribution License (the "License"). 61642Sgavinm * You may not use this file except in compliance with the License. 71642Sgavinm * 81642Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91642Sgavinm * or http://www.opensolaris.org/os/licensing. 101642Sgavinm * See the License for the specific language governing permissions 111642Sgavinm * and limitations under the License. 121642Sgavinm * 131642Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 141642Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151642Sgavinm * If applicable, add the following below this CDDL HEADER, with the 161642Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 171642Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 181642Sgavinm * 191642Sgavinm * CDDL HEADER END 201642Sgavinm */ 211642Sgavinm 221414Scindi /* 23*10942STom.Pothier@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 241414Scindi * Use is subject to license terms. 251414Scindi */ 261414Scindi 271414Scindi /* 281414Scindi * Public interface to routines implemented by CPU modules 291414Scindi */ 301414Scindi 315254Sgavinm #include <sys/types.h> 325254Sgavinm #include <sys/atomic.h> 331414Scindi #include <sys/x86_archext.h> 341414Scindi #include <sys/cpu_module_impl.h> 355254Sgavinm #include <sys/cpu_module_ms.h> 361414Scindi #include <sys/fm/util.h> 371414Scindi #include <sys/reboot.h> 381414Scindi #include <sys/modctl.h> 391414Scindi #include <sys/param.h> 401414Scindi #include <sys/cmn_err.h> 411414Scindi #include <sys/systm.h> 425254Sgavinm #include <sys/fm/protocol.h> 435254Sgavinm #include <sys/pcb.h> 445254Sgavinm #include <sys/ontrap.h> 455254Sgavinm #include <sys/psw.h> 465254Sgavinm #include <sys/privregs.h> 477532SSean.Ye@Sun.COM #include <sys/machsystm.h> 481414Scindi 495254Sgavinm /* 505254Sgavinm * Set to force cmi_init to fail. 515254Sgavinm */ 525254Sgavinm int cmi_no_init = 0; 535254Sgavinm 545254Sgavinm /* 555254Sgavinm * Set to avoid MCA initialization. 565254Sgavinm */ 575254Sgavinm int cmi_no_mca_init = 0; 581414Scindi 591414Scindi /* 602869Sgavinm * If cleared for debugging we will not attempt to load a model-specific 612869Sgavinm * cpu module but will load the generic cpu module instead. 622869Sgavinm */ 632869Sgavinm int cmi_force_generic = 0; 642869Sgavinm 652869Sgavinm /* 661414Scindi * If cleared for debugging, we will suppress panicking on fatal hardware 671414Scindi * errors. This should *only* be used for debugging; it use can and will 681414Scindi * cause data corruption if actual hardware errors are detected by the system. 691414Scindi */ 701414Scindi int cmi_panic_on_uncorrectable_error = 1; 711414Scindi 727532SSean.Ye@Sun.COM #ifndef __xpv 735254Sgavinm /* 747349SAdrian.Frost@Sun.COM * Set to indicate whether we are able to enable cmci interrupt. 757349SAdrian.Frost@Sun.COM */ 767349SAdrian.Frost@Sun.COM int cmi_enable_cmci = 0; 777532SSean.Ye@Sun.COM #endif 787349SAdrian.Frost@Sun.COM 797349SAdrian.Frost@Sun.COM /* 805254Sgavinm * Subdirectory (relative to the module search path) in which we will 815254Sgavinm * look for cpu modules. 825254Sgavinm */ 835254Sgavinm #define CPUMOD_SUBDIR "cpu" 845254Sgavinm 855254Sgavinm /* 865254Sgavinm * CPU modules have a filenames such as "cpu.AuthenticAMD.15" and 875254Sgavinm * "cpu.generic" - the "cpu" prefix is specified by the following. 885254Sgavinm */ 895254Sgavinm #define CPUMOD_PREFIX "cpu" 905254Sgavinm 915254Sgavinm /* 925254Sgavinm * Structure used to keep track of cpu modules we have loaded and their ops 935254Sgavinm */ 945254Sgavinm typedef struct cmi { 955254Sgavinm struct cmi *cmi_next; 965254Sgavinm struct cmi *cmi_prev; 975254Sgavinm const cmi_ops_t *cmi_ops; 985254Sgavinm struct modctl *cmi_modp; 995254Sgavinm uint_t cmi_refcnt; 1005254Sgavinm } cmi_t; 1015254Sgavinm 1021414Scindi static cmi_t *cmi_list; 1031414Scindi static kmutex_t cmi_load_lock; 1041414Scindi 1055254Sgavinm /* 1065254Sgavinm * Functions we need from cmi_hw.c that are not part of the cpu_module.h 1075254Sgavinm * interface. 1085254Sgavinm */ 1097532SSean.Ye@Sun.COM extern cmi_hdl_t cmi_hdl_create(enum cmi_hdl_class, uint_t, uint_t, uint_t); 1105254Sgavinm extern void cmi_hdl_setcmi(cmi_hdl_t, void *, void *); 1115254Sgavinm extern void *cmi_hdl_getcmi(cmi_hdl_t); 1125254Sgavinm extern void cmi_hdl_setmc(cmi_hdl_t, const struct cmi_mc_ops *, void *); 1137532SSean.Ye@Sun.COM extern void cmi_hdl_inj_begin(cmi_hdl_t); 1147532SSean.Ye@Sun.COM extern void cmi_hdl_inj_end(cmi_hdl_t); 115*10942STom.Pothier@Sun.COM extern void cmi_read_smbios(cmi_hdl_t); 1165254Sgavinm 1175254Sgavinm #define HDL2CMI(hdl) cmi_hdl_getcmi(hdl) 1185254Sgavinm 1195254Sgavinm #define CMI_OPS(cmi) (cmi)->cmi_ops 1205254Sgavinm #define CMI_OP_PRESENT(cmi, op) ((cmi) && CMI_OPS(cmi)->op != NULL) 1215254Sgavinm 1225254Sgavinm #define CMI_MATCH_VENDOR 0 /* Just match on vendor */ 1235254Sgavinm #define CMI_MATCH_FAMILY 1 /* Match down to family */ 1245254Sgavinm #define CMI_MATCH_MODEL 2 /* Match down to model */ 1255254Sgavinm #define CMI_MATCH_STEPPING 3 /* Match down to stepping */ 1265254Sgavinm 1275254Sgavinm static void 1285254Sgavinm cmi_link(cmi_t *cmi) 1295254Sgavinm { 1305254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 1315254Sgavinm 1325254Sgavinm cmi->cmi_prev = NULL; 1335254Sgavinm cmi->cmi_next = cmi_list; 1345254Sgavinm if (cmi_list != NULL) 1355254Sgavinm cmi_list->cmi_prev = cmi; 1365254Sgavinm cmi_list = cmi; 1375254Sgavinm } 1385254Sgavinm 1395254Sgavinm static void 1405254Sgavinm cmi_unlink(cmi_t *cmi) 1411414Scindi { 1425254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 1435254Sgavinm ASSERT(cmi->cmi_refcnt == 0); 1445254Sgavinm 1455254Sgavinm if (cmi->cmi_prev != NULL) 1465254Sgavinm cmi->cmi_prev = cmi->cmi_next; 1475254Sgavinm 1485254Sgavinm if (cmi->cmi_next != NULL) 1495254Sgavinm cmi->cmi_next->cmi_prev = cmi->cmi_prev; 1505254Sgavinm 1515254Sgavinm if (cmi_list == cmi) 1525254Sgavinm cmi_list = cmi->cmi_next; 1535254Sgavinm } 1545254Sgavinm 1555254Sgavinm /* 1565254Sgavinm * Hold the module in memory. We call to CPU modules without using the 1575254Sgavinm * stubs mechanism, so these modules must be manually held in memory. 1585254Sgavinm * The mod_ref acts as if another loaded module has a dependency on us. 1595254Sgavinm */ 1605254Sgavinm static void 1615254Sgavinm cmi_hold(cmi_t *cmi) 1625254Sgavinm { 1635254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 1645254Sgavinm 1655254Sgavinm mutex_enter(&mod_lock); 1665254Sgavinm cmi->cmi_modp->mod_ref++; 1675254Sgavinm mutex_exit(&mod_lock); 1685254Sgavinm cmi->cmi_refcnt++; 1695254Sgavinm } 1705254Sgavinm 1715254Sgavinm static void 1725254Sgavinm cmi_rele(cmi_t *cmi) 1735254Sgavinm { 1745254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 1755254Sgavinm 1765254Sgavinm mutex_enter(&mod_lock); 1775254Sgavinm cmi->cmi_modp->mod_ref--; 1785254Sgavinm mutex_exit(&mod_lock); 1795254Sgavinm 1805254Sgavinm if (--cmi->cmi_refcnt == 0) { 1815254Sgavinm cmi_unlink(cmi); 1825254Sgavinm kmem_free(cmi, sizeof (cmi_t)); 1835254Sgavinm } 1845254Sgavinm } 1855254Sgavinm 1865254Sgavinm static cmi_ops_t * 1875254Sgavinm cmi_getops(modctl_t *modp) 1885254Sgavinm { 1895254Sgavinm cmi_ops_t *ops; 1905254Sgavinm 1915254Sgavinm if ((ops = (cmi_ops_t *)modlookup_by_modctl(modp, "_cmi_ops")) == 1925254Sgavinm NULL) { 1935254Sgavinm cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops " 1945254Sgavinm "found", modp->mod_modname); 1955254Sgavinm return (NULL); 1965254Sgavinm } 1975254Sgavinm 1985254Sgavinm if (ops->cmi_init == NULL) { 1995254Sgavinm cmn_err(CE_WARN, "cpu module '%s' is invalid: no cmi_init " 2005254Sgavinm "entry point", modp->mod_modname); 2015254Sgavinm return (NULL); 2025254Sgavinm } 2035254Sgavinm 2045254Sgavinm return (ops); 2051414Scindi } 2061414Scindi 2071414Scindi static cmi_t * 2081414Scindi cmi_load_modctl(modctl_t *modp) 2091414Scindi { 2105254Sgavinm cmi_ops_t *ops; 2115254Sgavinm uintptr_t ver; 2121414Scindi cmi_t *cmi; 2135254Sgavinm cmi_api_ver_t apiver; 2141414Scindi 2151414Scindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 2161414Scindi 2171414Scindi for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) { 2181414Scindi if (cmi->cmi_modp == modp) 2191414Scindi return (cmi); 2201414Scindi } 2211414Scindi 2225254Sgavinm if ((ver = modlookup_by_modctl(modp, "_cmi_api_version")) == NULL) { 2235254Sgavinm /* 2245254Sgavinm * Apparently a cpu module before versioning was introduced - 2255254Sgavinm * we call this version 0. 2265254Sgavinm */ 2275254Sgavinm apiver = CMI_API_VERSION_0; 2285254Sgavinm } else { 2295254Sgavinm apiver = *((cmi_api_ver_t *)ver); 2305254Sgavinm if (!CMI_API_VERSION_CHKMAGIC(apiver)) { 2315254Sgavinm cmn_err(CE_WARN, "cpu module '%s' is invalid: " 2325254Sgavinm "_cmi_api_version 0x%x has bad magic", 2335254Sgavinm modp->mod_modname, apiver); 2345254Sgavinm return (NULL); 2355254Sgavinm } 2365254Sgavinm } 2375254Sgavinm 2385254Sgavinm if (apiver != CMI_API_VERSION) { 2395254Sgavinm cmn_err(CE_WARN, "cpu module '%s' has API version %d, " 2405254Sgavinm "kernel requires API version %d", modp->mod_modname, 2415254Sgavinm CMI_API_VERSION_TOPRINT(apiver), 2425254Sgavinm CMI_API_VERSION_TOPRINT(CMI_API_VERSION)); 2431414Scindi return (NULL); 2441414Scindi } 2451414Scindi 2465254Sgavinm if ((ops = cmi_getops(modp)) == NULL) 2475254Sgavinm return (NULL); 2481414Scindi 2495254Sgavinm cmi = kmem_zalloc(sizeof (*cmi), KM_SLEEP); 2505254Sgavinm cmi->cmi_ops = ops; 2511414Scindi cmi->cmi_modp = modp; 2521414Scindi 2535254Sgavinm cmi_link(cmi); 2545254Sgavinm 2555254Sgavinm return (cmi); 2565254Sgavinm } 2575254Sgavinm 2585254Sgavinm static int 2595254Sgavinm cmi_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match) 2605254Sgavinm { 2615254Sgavinm if (match >= CMI_MATCH_VENDOR && 2625254Sgavinm cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2)) 2635254Sgavinm return (0); 2645254Sgavinm 2655254Sgavinm if (match >= CMI_MATCH_FAMILY && 2665254Sgavinm cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2)) 2675254Sgavinm return (0); 2685254Sgavinm 2695254Sgavinm if (match >= CMI_MATCH_MODEL && 2705254Sgavinm cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2)) 2715254Sgavinm return (0); 2725254Sgavinm 2735254Sgavinm if (match >= CMI_MATCH_STEPPING && 2745254Sgavinm cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2)) 2755254Sgavinm return (0); 2765254Sgavinm 2775254Sgavinm return (1); 2785254Sgavinm } 2795254Sgavinm 2805254Sgavinm static int 2815254Sgavinm cmi_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) 2825254Sgavinm { 2835254Sgavinm cmi_hdl_t thdl = (cmi_hdl_t)arg1; 2845254Sgavinm int match = *((int *)arg2); 2855254Sgavinm cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3; 2865254Sgavinm 2875254Sgavinm if (cmi_cpu_match(thdl, whdl, match)) { 2885254Sgavinm cmi_hdl_hold(whdl); /* short-term hold */ 2895254Sgavinm *rsltp = whdl; 2905254Sgavinm return (CMI_HDL_WALK_DONE); 2915254Sgavinm } else { 2925254Sgavinm return (CMI_HDL_WALK_NEXT); 2935254Sgavinm } 2945254Sgavinm } 2955254Sgavinm 2965254Sgavinm static cmi_t * 2975254Sgavinm cmi_search_list(cmi_hdl_t hdl, int match) 2985254Sgavinm { 2995254Sgavinm cmi_hdl_t dhdl = NULL; 3005254Sgavinm cmi_t *cmi = NULL; 3015254Sgavinm 3025254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 3035254Sgavinm 3045254Sgavinm cmi_hdl_walk(cmi_search_list_cb, (void *)hdl, (void *)&match, &dhdl); 3055254Sgavinm if (dhdl) { 3065254Sgavinm cmi = HDL2CMI(dhdl); 3075254Sgavinm cmi_hdl_rele(dhdl); /* held in cmi_search_list_cb */ 3085254Sgavinm } 3091414Scindi 3101414Scindi return (cmi); 3111414Scindi } 3121414Scindi 3131414Scindi static cmi_t * 3145254Sgavinm cmi_load_module(cmi_hdl_t hdl, int match, int *chosenp) 3151414Scindi { 3161414Scindi modctl_t *modp; 3171414Scindi cmi_t *cmi; 3185254Sgavinm int modid; 3191414Scindi uint_t s[3]; 3201414Scindi 3215254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 3225254Sgavinm ASSERT(match == CMI_MATCH_STEPPING || match == CMI_MATCH_MODEL || 3235254Sgavinm match == CMI_MATCH_FAMILY || match == CMI_MATCH_VENDOR); 3245254Sgavinm 3251414Scindi /* 3265254Sgavinm * Have we already loaded a module for a cpu with the same 3275254Sgavinm * vendor/family/model/stepping? 3281414Scindi */ 3295254Sgavinm if ((cmi = cmi_search_list(hdl, match)) != NULL) { 3305254Sgavinm cmi_hold(cmi); 3315254Sgavinm return (cmi); 3321414Scindi } 3331414Scindi 3345254Sgavinm s[0] = cmi_hdl_family(hdl); 3355254Sgavinm s[1] = cmi_hdl_model(hdl); 3365254Sgavinm s[2] = cmi_hdl_stepping(hdl); 3371414Scindi modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX, 3385254Sgavinm cmi_hdl_vendorstr(hdl), ".", s, match, chosenp); 3391414Scindi 3401414Scindi if (modid == -1) 3411414Scindi return (NULL); 3421414Scindi 3431414Scindi modp = mod_hold_by_id(modid); 3441414Scindi cmi = cmi_load_modctl(modp); 3455254Sgavinm if (cmi) 3465254Sgavinm cmi_hold(cmi); 3471414Scindi mod_release_mod(modp); 3481414Scindi 3491414Scindi return (cmi); 3501414Scindi } 3511414Scindi 3521414Scindi /* 3535254Sgavinm * Try to load a cpu module with specific support for this chip type. 3541414Scindi */ 3555254Sgavinm static cmi_t * 3565254Sgavinm cmi_load_specific(cmi_hdl_t hdl, void **datap) 3571414Scindi { 3581414Scindi cmi_t *cmi; 3595254Sgavinm int err; 3605254Sgavinm int i; 3615254Sgavinm 3625254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 3635254Sgavinm 3645254Sgavinm for (i = CMI_MATCH_STEPPING; i >= CMI_MATCH_VENDOR; i--) { 3655254Sgavinm int suffixlevel; 3665254Sgavinm 3675254Sgavinm if ((cmi = cmi_load_module(hdl, i, &suffixlevel)) == NULL) 3685254Sgavinm return (NULL); 3695254Sgavinm 3705254Sgavinm /* 3715254Sgavinm * A module has loaded and has a _cmi_ops structure, and the 3725254Sgavinm * module has been held for this instance. Call its cmi_init 3735254Sgavinm * entry point - we expect success (0) or ENOTSUP. 3745254Sgavinm */ 3755254Sgavinm if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) == 0) { 3765254Sgavinm if (boothowto & RB_VERBOSE) { 3775254Sgavinm printf("initialized cpu module '%s' on " 3785254Sgavinm "chip %d core %d strand %d\n", 3795254Sgavinm cmi->cmi_modp->mod_modname, 3805254Sgavinm cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl), 3815254Sgavinm cmi_hdl_strandid(hdl)); 3825254Sgavinm } 3835254Sgavinm return (cmi); 3845254Sgavinm } else if (err != ENOTSUP) { 3855254Sgavinm cmn_err(CE_WARN, "failed to init cpu module '%s' on " 3865254Sgavinm "chip %d core %d strand %d: err=%d\n", 3875254Sgavinm cmi->cmi_modp->mod_modname, 3885254Sgavinm cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl), 3895254Sgavinm cmi_hdl_strandid(hdl), err); 3905254Sgavinm } 3915254Sgavinm 3925254Sgavinm /* 3935254Sgavinm * The module failed or declined to init, so release 3945254Sgavinm * it and update i to be equal to the number 3955254Sgavinm * of suffices actually used in the last module path. 3965254Sgavinm */ 3975254Sgavinm cmi_rele(cmi); 3985254Sgavinm i = suffixlevel; 3995254Sgavinm } 4005254Sgavinm 4015254Sgavinm return (NULL); 4025254Sgavinm } 4035254Sgavinm 4045254Sgavinm /* 4055254Sgavinm * Load the generic IA32 MCA cpu module, which may still supplement 4065254Sgavinm * itself with model-specific support through cpu model-specific modules. 4075254Sgavinm */ 4085254Sgavinm static cmi_t * 4095254Sgavinm cmi_load_generic(cmi_hdl_t hdl, void **datap) 4105254Sgavinm { 4115254Sgavinm modctl_t *modp; 4125254Sgavinm cmi_t *cmi; 4135254Sgavinm int modid; 4145254Sgavinm int err; 4155254Sgavinm 4165254Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock)); 4175254Sgavinm 4185254Sgavinm if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1) 4195254Sgavinm return (NULL); 4205254Sgavinm 4215254Sgavinm modp = mod_hold_by_id(modid); 4225254Sgavinm cmi = cmi_load_modctl(modp); 4235254Sgavinm if (cmi) 4245254Sgavinm cmi_hold(cmi); 4255254Sgavinm mod_release_mod(modp); 4265254Sgavinm 4275254Sgavinm if (cmi == NULL) 4285254Sgavinm return (NULL); 4295254Sgavinm 4305254Sgavinm if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) != 0) { 4315254Sgavinm if (err != ENOTSUP) 4325254Sgavinm cmn_err(CE_WARN, CPUMOD_PREFIX ".generic failed to " 4335254Sgavinm "init: err=%d", err); 4345254Sgavinm cmi_rele(cmi); 4355254Sgavinm return (NULL); 4365254Sgavinm } 4375254Sgavinm 4385254Sgavinm return (cmi); 4395254Sgavinm } 4405254Sgavinm 4415254Sgavinm cmi_hdl_t 4425254Sgavinm cmi_init(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, 4437532SSean.Ye@Sun.COM uint_t strandid) 4445254Sgavinm { 4455254Sgavinm cmi_t *cmi = NULL; 4465254Sgavinm cmi_hdl_t hdl; 4471414Scindi void *data; 4481414Scindi 4495254Sgavinm if (cmi_no_init) { 4505254Sgavinm cmi_no_mca_init = 1; 4515254Sgavinm return (NULL); 4525254Sgavinm } 4535254Sgavinm 4541414Scindi mutex_enter(&cmi_load_lock); 4551414Scindi 4567532SSean.Ye@Sun.COM if ((hdl = cmi_hdl_create(class, chipid, coreid, strandid)) == NULL) { 4571414Scindi mutex_exit(&cmi_load_lock); 4585254Sgavinm cmn_err(CE_WARN, "There will be no MCA support on chip %d " 4595254Sgavinm "core %d strand %d (cmi_hdl_create returned NULL)\n", 4605254Sgavinm chipid, coreid, strandid); 4615254Sgavinm return (NULL); 4621414Scindi } 4631414Scindi 4645254Sgavinm if (!cmi_force_generic) 4655254Sgavinm cmi = cmi_load_specific(hdl, &data); 4665254Sgavinm 4675254Sgavinm if (cmi == NULL && (cmi = cmi_load_generic(hdl, &data)) == NULL) { 4685254Sgavinm cmn_err(CE_WARN, "There will be no MCA support on chip %d " 4695254Sgavinm "core %d strand %d\n", chipid, coreid, strandid); 4705254Sgavinm cmi_hdl_rele(hdl); 4711414Scindi mutex_exit(&cmi_load_lock); 4725254Sgavinm return (NULL); 4731414Scindi } 4741414Scindi 4755254Sgavinm cmi_hdl_setcmi(hdl, cmi, data); 4761414Scindi 4775254Sgavinm cms_init(hdl); 4785254Sgavinm 479*10942STom.Pothier@Sun.COM cmi_read_smbios(hdl); 480*10942STom.Pothier@Sun.COM 4811414Scindi mutex_exit(&cmi_load_lock); 4821414Scindi 4835254Sgavinm return (hdl); 4841414Scindi } 4851414Scindi 4865254Sgavinm /* 4875254Sgavinm * cmi_fini is not called at the moment. It is intended to be called 4885254Sgavinm * on DR deconfigure of a cpu resource. It should not be called at 4895254Sgavinm * simple offline of a cpu. 4905254Sgavinm */ 4911414Scindi void 4925254Sgavinm cmi_fini(cmi_hdl_t hdl) 4931414Scindi { 4945254Sgavinm cmi_t *cmi = HDL2CMI(hdl); 4955254Sgavinm 4965254Sgavinm if (cms_present(hdl)) 4975254Sgavinm cms_fini(hdl); 4985254Sgavinm 4995254Sgavinm if (CMI_OP_PRESENT(cmi, cmi_fini)) 5005254Sgavinm CMI_OPS(cmi)->cmi_fini(hdl); 5015254Sgavinm 5025254Sgavinm cmi_hdl_rele(hdl); /* release hold obtained in cmi_hdl_create */ 5031414Scindi } 5041414Scindi 5055254Sgavinm /* 5067532SSean.Ye@Sun.COM * cmi_post_startup is called from post_startup for the boot cpu only (no 5077532SSean.Ye@Sun.COM * other cpus are started yet). 5085254Sgavinm */ 5091414Scindi void 5105254Sgavinm cmi_post_startup(void) 5111414Scindi { 5125254Sgavinm cmi_hdl_t hdl; 5135254Sgavinm cmi_t *cmi; 5145254Sgavinm 5155254Sgavinm if (cmi_no_mca_init != 0 || 5165254Sgavinm (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ 5175254Sgavinm return; 5185254Sgavinm 5195254Sgavinm cmi = HDL2CMI(hdl); 5205254Sgavinm 5215254Sgavinm if (CMI_OP_PRESENT(cmi, cmi_post_startup)) 5225254Sgavinm CMI_OPS(cmi)->cmi_post_startup(hdl); 5235254Sgavinm 5245254Sgavinm cmi_hdl_rele(hdl); 5251414Scindi } 5261414Scindi 5272869Sgavinm /* 5282869Sgavinm * Called just once from start_other_cpus when all processors are started. 5292869Sgavinm * This will not be called for each cpu, so the registered op must not 5307532SSean.Ye@Sun.COM * assume it is called as such. We are not necessarily executing on 5317532SSean.Ye@Sun.COM * the boot cpu. 5322869Sgavinm */ 5331414Scindi void 5341642Sgavinm cmi_post_mpstartup(void) 5351642Sgavinm { 5365254Sgavinm cmi_hdl_t hdl; 5375254Sgavinm cmi_t *cmi; 5385254Sgavinm 5395254Sgavinm if (cmi_no_mca_init != 0 || 5405254Sgavinm (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ 5415254Sgavinm return; 5425254Sgavinm 5435254Sgavinm cmi = HDL2CMI(hdl); 5445254Sgavinm 5455254Sgavinm if (CMI_OP_PRESENT(cmi, cmi_post_mpstartup)) 5465254Sgavinm CMI_OPS(cmi)->cmi_post_mpstartup(hdl); 5475254Sgavinm 5485254Sgavinm cmi_hdl_rele(hdl); 5491642Sgavinm } 5501642Sgavinm 5511642Sgavinm void 5525254Sgavinm cmi_faulted_enter(cmi_hdl_t hdl) 5531414Scindi { 5545254Sgavinm cmi_t *cmi = HDL2CMI(hdl); 5555254Sgavinm 5565254Sgavinm if (cmi_no_mca_init != 0) 5575254Sgavinm return; 5585254Sgavinm 5595254Sgavinm if (CMI_OP_PRESENT(cmi, cmi_faulted_enter)) 5605254Sgavinm CMI_OPS(cmi)->cmi_faulted_enter(hdl); 5615254Sgavinm } 5625254Sgavinm 5635254Sgavinm void 5645254Sgavinm cmi_faulted_exit(cmi_hdl_t hdl) 5655254Sgavinm { 5665254Sgavinm cmi_t *cmi = HDL2CMI(hdl); 5675254Sgavinm 5685254Sgavinm if (cmi_no_mca_init != 0) 5695254Sgavinm return; 5705254Sgavinm 5715254Sgavinm if (CMI_OP_PRESENT(cmi, cmi_faulted_exit)) 5725254Sgavinm CMI_OPS(cmi)->cmi_faulted_exit(hdl); 5731414Scindi } 5741414Scindi 5751414Scindi void 5765254Sgavinm cmi_mca_init(cmi_hdl_t hdl) 5771414Scindi { 5785254Sgavinm cmi_t *cmi; 5795254Sgavinm 5805254Sgavinm if (cmi_no_mca_init != 0) 5815254Sgavinm return; 5825254Sgavinm 5835254Sgavinm cmi = HDL2CMI(hdl); 5845254Sgavinm 5855254Sgavinm if (CMI_OP_PRESENT(cmi, cmi_mca_init)) 5865254Sgavinm CMI_OPS(cmi)->cmi_mca_init(hdl); 5871414Scindi } 5881414Scindi 5895254Sgavinm #define CMI_RESPONSE_PANIC 0x0 /* panic must have value 0 */ 5905254Sgavinm #define CMI_RESPONSE_NONE 0x1 5915254Sgavinm #define CMI_RESPONSE_CKILL 0x2 5925254Sgavinm #define CMI_RESPONSE_REBOOT 0x3 /* not implemented */ 5935254Sgavinm #define CMI_RESPONSE_ONTRAP_PROT 0x4 5945254Sgavinm #define CMI_RESPONSE_LOFAULT_PROT 0x5 5955254Sgavinm 5965254Sgavinm /* 5975254Sgavinm * Return 0 if we will panic in response to this machine check, otherwise 5985254Sgavinm * non-zero. If the caller is cmi_mca_trap in this file then the nonzero 5995254Sgavinm * return values are to be interpreted from CMI_RESPONSE_* above. 6005254Sgavinm * 6015254Sgavinm * This function must just return what will be done without actually 6025254Sgavinm * doing anything; this includes not changing the regs. 6035254Sgavinm */ 6041414Scindi int 6055254Sgavinm cmi_mce_response(struct regs *rp, uint64_t disp) 6061414Scindi { 6075254Sgavinm int panicrsp = cmi_panic_on_uncorrectable_error ? CMI_RESPONSE_PANIC : 6085254Sgavinm CMI_RESPONSE_NONE; 6095254Sgavinm on_trap_data_t *otp; 6105254Sgavinm 6115254Sgavinm ASSERT(rp != NULL); /* don't call for polling, only on #MC */ 6125254Sgavinm 6135254Sgavinm /* 6145254Sgavinm * If no bits are set in the disposition then there is nothing to 6155254Sgavinm * worry about and we do not need to trampoline to ontrap or 6165254Sgavinm * lofault handlers. 6175254Sgavinm */ 6185254Sgavinm if (disp == 0) 6195254Sgavinm return (CMI_RESPONSE_NONE); 6205254Sgavinm 6215254Sgavinm /* 6225254Sgavinm * Unconstrained errors cannot be forgiven, even by ontrap or 6235254Sgavinm * lofault protection. The data is not poisoned and may not 6245254Sgavinm * even belong to the trapped context - eg a writeback of 6255254Sgavinm * data that is found to be bad. 6265254Sgavinm */ 6275254Sgavinm if (disp & CMI_ERRDISP_UC_UNCONSTRAINED) 6285254Sgavinm return (panicrsp); 6295254Sgavinm 6305254Sgavinm /* 6315254Sgavinm * ontrap OT_DATA_EC and lofault protection forgive any disposition 6325254Sgavinm * other than unconstrained, even those normally forced fatal. 6335254Sgavinm */ 6345254Sgavinm if ((otp = curthread->t_ontrap) != NULL && otp->ot_prot & OT_DATA_EC) 6355254Sgavinm return (CMI_RESPONSE_ONTRAP_PROT); 6365254Sgavinm else if (curthread->t_lofault) 6375254Sgavinm return (CMI_RESPONSE_LOFAULT_PROT); 6385254Sgavinm 6395254Sgavinm /* 6405254Sgavinm * Forced-fatal errors are terminal even in user mode. 6415254Sgavinm */ 6425254Sgavinm if (disp & CMI_ERRDISP_FORCEFATAL) 6435254Sgavinm return (panicrsp); 6445254Sgavinm 6455254Sgavinm /* 6465254Sgavinm * If the trapped context is corrupt or we have no instruction pointer 6475254Sgavinm * to resume at (and aren't trampolining to a fault handler) 6485254Sgavinm * then in the kernel case we must panic and in usermode we 6495254Sgavinm * kill the affected contract. 6505254Sgavinm */ 6515254Sgavinm if (disp & (CMI_ERRDISP_CURCTXBAD | CMI_ERRDISP_RIPV_INVALID)) 6525254Sgavinm return (USERMODE(rp->r_cs) ? CMI_RESPONSE_CKILL : panicrsp); 6535254Sgavinm 6545254Sgavinm /* 6555254Sgavinm * Anything else is harmless 6565254Sgavinm */ 6575254Sgavinm return (CMI_RESPONSE_NONE); 6581414Scindi } 6591414Scindi 6605254Sgavinm int cma_mca_trap_panic_suppressed = 0; 6615254Sgavinm 6625254Sgavinm static void 6635254Sgavinm cmi_mca_panic(void) 6641414Scindi { 6655254Sgavinm if (cmi_panic_on_uncorrectable_error) { 6665254Sgavinm fm_panic("Unrecoverable Machine-Check Exception"); 6675254Sgavinm } else { 6685254Sgavinm cmn_err(CE_WARN, "suppressing panic from fatal #mc"); 6695254Sgavinm cma_mca_trap_panic_suppressed++; 6705254Sgavinm } 6711414Scindi } 6721414Scindi 6735254Sgavinm 6745254Sgavinm int cma_mca_trap_contract_kills = 0; 6755254Sgavinm int cma_mca_trap_ontrap_forgiven = 0; 6765254Sgavinm int cma_mca_trap_lofault_forgiven = 0; 6775254Sgavinm 6785254Sgavinm /* 6795254Sgavinm * Native #MC handler - we branch to here from mcetrap 6805254Sgavinm */ 6815254Sgavinm /*ARGSUSED*/ 6821414Scindi void 6831414Scindi cmi_mca_trap(struct regs *rp) 6841414Scindi { 6855254Sgavinm #ifndef __xpv 6865254Sgavinm cmi_hdl_t hdl = NULL; 6875254Sgavinm uint64_t disp; 6885254Sgavinm cmi_t *cmi; 6895254Sgavinm int s; 6905254Sgavinm 6915254Sgavinm if (cmi_no_mca_init != 0) 6925254Sgavinm return; 6935254Sgavinm 6945254Sgavinm /* 6955254Sgavinm * This function can call cmn_err, and the cpu module cmi_mca_trap 6965254Sgavinm * entry point may also elect to call cmn_err (e.g., if it can't 6975254Sgavinm * log the error onto an errorq, say very early in boot). 6985254Sgavinm * We need to let cprintf know that we must not block. 6995254Sgavinm */ 7005254Sgavinm s = spl8(); 7015254Sgavinm 7025254Sgavinm if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU), 7035254Sgavinm cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL || 7045254Sgavinm (cmi = HDL2CMI(hdl)) == NULL || 7055254Sgavinm !CMI_OP_PRESENT(cmi, cmi_mca_trap)) { 7065254Sgavinm 7075254Sgavinm cmn_err(CE_WARN, "#MC exception on cpuid %d: %s", 7085254Sgavinm CPU->cpu_id, 7095254Sgavinm hdl ? "handle lookup ok but no #MC handler found" : 7105254Sgavinm "handle lookup failed"); 7115254Sgavinm 7125254Sgavinm if (hdl != NULL) 7135254Sgavinm cmi_hdl_rele(hdl); 7145254Sgavinm 7155254Sgavinm splx(s); 7165254Sgavinm return; 7171414Scindi } 7185254Sgavinm 7195254Sgavinm disp = CMI_OPS(cmi)->cmi_mca_trap(hdl, rp); 7201414Scindi 7215254Sgavinm switch (cmi_mce_response(rp, disp)) { 7225254Sgavinm default: 7235254Sgavinm cmn_err(CE_WARN, "Invalid response from cmi_mce_response"); 7245254Sgavinm /*FALLTHRU*/ 7255254Sgavinm 7265254Sgavinm case CMI_RESPONSE_PANIC: 7275254Sgavinm cmi_mca_panic(); 7285254Sgavinm break; 7295254Sgavinm 7305254Sgavinm case CMI_RESPONSE_NONE: 7315254Sgavinm break; 7325254Sgavinm 7335254Sgavinm case CMI_RESPONSE_CKILL: 7345254Sgavinm ttolwp(curthread)->lwp_pcb.pcb_flags |= ASYNC_HWERR; 7355254Sgavinm aston(curthread); 7365254Sgavinm cma_mca_trap_contract_kills++; 7375254Sgavinm break; 7381414Scindi 7395254Sgavinm case CMI_RESPONSE_ONTRAP_PROT: { 7405254Sgavinm on_trap_data_t *otp = curthread->t_ontrap; 7415254Sgavinm otp->ot_trap = OT_DATA_EC; 7425254Sgavinm rp->r_pc = otp->ot_trampoline; 7435254Sgavinm cma_mca_trap_ontrap_forgiven++; 7445254Sgavinm break; 7455254Sgavinm } 7461414Scindi 7475254Sgavinm case CMI_RESPONSE_LOFAULT_PROT: 7485254Sgavinm rp->r_r0 = EFAULT; 7495254Sgavinm rp->r_pc = curthread->t_lofault; 7505254Sgavinm cma_mca_trap_lofault_forgiven++; 7515254Sgavinm break; 7525254Sgavinm } 7535254Sgavinm 7545254Sgavinm cmi_hdl_rele(hdl); 7555254Sgavinm splx(s); 7565254Sgavinm #endif /* __xpv */ 7571414Scindi } 7581414Scindi 7591414Scindi void 7605254Sgavinm cmi_hdl_poke(cmi_hdl_t hdl) 7611414Scindi { 7625254Sgavinm cmi_t *cmi = HDL2CMI(hdl); 7635254Sgavinm 7645254Sgavinm if (!CMI_OP_PRESENT(cmi, cmi_hdl_poke)) 7655254Sgavinm return; 7665254Sgavinm 7675254Sgavinm CMI_OPS(cmi)->cmi_hdl_poke(hdl); 7681414Scindi } 7691414Scindi 7707532SSean.Ye@Sun.COM #ifndef __xpv 7711414Scindi void 7727349SAdrian.Frost@Sun.COM cmi_cmci_trap() 7737349SAdrian.Frost@Sun.COM { 7747349SAdrian.Frost@Sun.COM cmi_hdl_t hdl = NULL; 7757349SAdrian.Frost@Sun.COM cmi_t *cmi; 7767349SAdrian.Frost@Sun.COM 7777349SAdrian.Frost@Sun.COM if (cmi_no_mca_init != 0) 7787349SAdrian.Frost@Sun.COM return; 7797349SAdrian.Frost@Sun.COM 7807349SAdrian.Frost@Sun.COM if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU), 7817349SAdrian.Frost@Sun.COM cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL || 7827349SAdrian.Frost@Sun.COM (cmi = HDL2CMI(hdl)) == NULL || 7837349SAdrian.Frost@Sun.COM !CMI_OP_PRESENT(cmi, cmi_cmci_trap)) { 7847349SAdrian.Frost@Sun.COM 7857349SAdrian.Frost@Sun.COM cmn_err(CE_WARN, "CMCI interrupt on cpuid %d: %s", 7867349SAdrian.Frost@Sun.COM CPU->cpu_id, 7877349SAdrian.Frost@Sun.COM hdl ? "handle lookup ok but no CMCI handler found" : 7887349SAdrian.Frost@Sun.COM "handle lookup failed"); 7897349SAdrian.Frost@Sun.COM 7907349SAdrian.Frost@Sun.COM if (hdl != NULL) 7917349SAdrian.Frost@Sun.COM cmi_hdl_rele(hdl); 7927349SAdrian.Frost@Sun.COM 7937349SAdrian.Frost@Sun.COM return; 7947349SAdrian.Frost@Sun.COM } 7957349SAdrian.Frost@Sun.COM 7967349SAdrian.Frost@Sun.COM CMI_OPS(cmi)->cmi_cmci_trap(hdl); 7977349SAdrian.Frost@Sun.COM 7987349SAdrian.Frost@Sun.COM cmi_hdl_rele(hdl); 7997532SSean.Ye@Sun.COM } 8007349SAdrian.Frost@Sun.COM #endif /* __xpv */ 8017349SAdrian.Frost@Sun.COM 8027349SAdrian.Frost@Sun.COM void 8035254Sgavinm cmi_mc_register(cmi_hdl_t hdl, const cmi_mc_ops_t *mcops, void *mcdata) 8041414Scindi { 8055254Sgavinm if (!cmi_no_mca_init) 8065254Sgavinm cmi_hdl_setmc(hdl, mcops, mcdata); 8071414Scindi } 8081414Scindi 8097532SSean.Ye@Sun.COM void 8107532SSean.Ye@Sun.COM cmi_mc_sw_memscrub_disable(void) 8117532SSean.Ye@Sun.COM { 8127532SSean.Ye@Sun.COM memscrub_disable(); 8137532SSean.Ye@Sun.COM } 8147532SSean.Ye@Sun.COM 8155254Sgavinm cmi_errno_t 8163164Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd, 8173164Sgavinm int syndtype, mc_unum_t *up) 8181414Scindi { 8191414Scindi const struct cmi_mc_ops *mcops; 8205254Sgavinm cmi_hdl_t hdl; 8215254Sgavinm cmi_errno_t rv; 8225254Sgavinm 8235254Sgavinm if (cmi_no_mca_init || 8245254Sgavinm (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ 8255254Sgavinm return (CMIERR_MC_ABSENT); 8261414Scindi 8275254Sgavinm if ((mcops = cmi_hdl_getmcops(hdl)) == NULL || 8285254Sgavinm mcops->cmi_mc_patounum == NULL) { 8295254Sgavinm cmi_hdl_rele(hdl); 8305254Sgavinm return (CMIERR_MC_NOTSUP); 8315254Sgavinm } 8321414Scindi 8335254Sgavinm rv = mcops->cmi_mc_patounum(cmi_hdl_getmcdata(hdl), pa, valid_hi, 8345254Sgavinm valid_lo, synd, syndtype, up); 8355254Sgavinm 8365254Sgavinm cmi_hdl_rele(hdl); 8375254Sgavinm 8385254Sgavinm return (rv); 8391414Scindi } 8401414Scindi 8415254Sgavinm cmi_errno_t 8421414Scindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap) 8431414Scindi { 8441414Scindi const struct cmi_mc_ops *mcops; 8455254Sgavinm cmi_hdl_t hdl; 8465254Sgavinm cmi_errno_t rv; 8477532SSean.Ye@Sun.COM nvlist_t *hcsp; 8481414Scindi 8491414Scindi if (up != NULL && nvl != NULL) 8505254Sgavinm return (CMIERR_API); /* convert from just one form */ 8515254Sgavinm 8525254Sgavinm if (cmi_no_mca_init || 8535254Sgavinm (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ 8545254Sgavinm return (CMIERR_MC_ABSENT); 8555254Sgavinm 8565254Sgavinm if ((mcops = cmi_hdl_getmcops(hdl)) == NULL || 8575254Sgavinm mcops->cmi_mc_unumtopa == NULL) { 8585254Sgavinm cmi_hdl_rele(hdl); 8595254Sgavinm 8607532SSean.Ye@Sun.COM if (nvl != NULL && nvlist_lookup_nvlist(nvl, 8617532SSean.Ye@Sun.COM FM_FMRI_HC_SPECIFIC, &hcsp) == 0 && 8627532SSean.Ye@Sun.COM (nvlist_lookup_uint64(hcsp, 8637532SSean.Ye@Sun.COM "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, pap) == 0 || 8647532SSean.Ye@Sun.COM nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR, 8657532SSean.Ye@Sun.COM pap) == 0)) { 8665254Sgavinm return (CMIERR_MC_PARTIALUNUMTOPA); 8675254Sgavinm } else { 8685254Sgavinm return (mcops && mcops->cmi_mc_unumtopa ? 8695254Sgavinm CMIERR_MC_NOTSUP : CMIERR_MC_ABSENT); 8705254Sgavinm } 8715254Sgavinm } 8725254Sgavinm 8735254Sgavinm rv = mcops->cmi_mc_unumtopa(cmi_hdl_getmcdata(hdl), up, nvl, pap); 8745254Sgavinm 8755254Sgavinm cmi_hdl_rele(hdl); 8765254Sgavinm 8775254Sgavinm return (rv); 8785254Sgavinm } 8791414Scindi 8805254Sgavinm void 8815254Sgavinm cmi_mc_logout(cmi_hdl_t hdl, boolean_t ismc, boolean_t sync) 8825254Sgavinm { 8835254Sgavinm const struct cmi_mc_ops *mcops; 8845254Sgavinm 8855254Sgavinm if (cmi_no_mca_init || (mcops = cmi_hdl_getmcops(hdl)) == NULL) 8865254Sgavinm return; 8875254Sgavinm 8885254Sgavinm if (mcops->cmi_mc_logout != NULL) 8895254Sgavinm mcops->cmi_mc_logout(hdl, ismc, sync); 8905254Sgavinm } 8911414Scindi 8925254Sgavinm cmi_errno_t 8935254Sgavinm cmi_hdl_msrinject(cmi_hdl_t hdl, cmi_mca_regs_t *regs, uint_t nregs, 8945254Sgavinm int force) 8955254Sgavinm { 8965254Sgavinm cmi_t *cmi = cmi_hdl_getcmi(hdl); 8977532SSean.Ye@Sun.COM cmi_errno_t rc; 8985254Sgavinm 8995254Sgavinm if (!CMI_OP_PRESENT(cmi, cmi_msrinject)) 9005254Sgavinm return (CMIERR_NOTSUP); 9015254Sgavinm 9027532SSean.Ye@Sun.COM cmi_hdl_inj_begin(hdl); 9037532SSean.Ye@Sun.COM rc = CMI_OPS(cmi)->cmi_msrinject(hdl, regs, nregs, force); 9047532SSean.Ye@Sun.COM cmi_hdl_inj_end(hdl); 9057532SSean.Ye@Sun.COM 9067532SSean.Ye@Sun.COM return (rc); 9071414Scindi } 9085254Sgavinm 9095254Sgavinm boolean_t 9105254Sgavinm cmi_panic_on_ue(void) 9115254Sgavinm { 9125254Sgavinm return (cmi_panic_on_uncorrectable_error ? B_TRUE : B_FALSE); 9135254Sgavinm } 9147532SSean.Ye@Sun.COM 9157532SSean.Ye@Sun.COM void 9167532SSean.Ye@Sun.COM cmi_panic_callback(void) 9177532SSean.Ye@Sun.COM { 9187532SSean.Ye@Sun.COM cmi_hdl_t hdl; 9197532SSean.Ye@Sun.COM cmi_t *cmi; 9207532SSean.Ye@Sun.COM 9217532SSean.Ye@Sun.COM if (cmi_no_mca_init || (hdl = cmi_hdl_any()) == NULL) 9227532SSean.Ye@Sun.COM return; 9237532SSean.Ye@Sun.COM 9247532SSean.Ye@Sun.COM cmi = cmi_hdl_getcmi(hdl); 9257532SSean.Ye@Sun.COM if (CMI_OP_PRESENT(cmi, cmi_panic_callback)) 9267532SSean.Ye@Sun.COM CMI_OPS(cmi)->cmi_panic_callback(); 9277532SSean.Ye@Sun.COM 9287532SSean.Ye@Sun.COM cmi_hdl_rele(hdl); 9297532SSean.Ye@Sun.COM } 930