17aec1d6eScindi /*
23ad553a7Sgavinm * CDDL HEADER START
33ad553a7Sgavinm *
43ad553a7Sgavinm * The contents of this file are subject to the terms of the
53ad553a7Sgavinm * Common Development and Distribution License (the "License").
63ad553a7Sgavinm * You may not use this file except in compliance with the License.
73ad553a7Sgavinm *
83ad553a7Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93ad553a7Sgavinm * or http://www.opensolaris.org/os/licensing.
103ad553a7Sgavinm * See the License for the specific language governing permissions
113ad553a7Sgavinm * and limitations under the License.
123ad553a7Sgavinm *
133ad553a7Sgavinm * When distributing Covered Code, include this CDDL HEADER in each
143ad553a7Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153ad553a7Sgavinm * If applicable, add the following below this CDDL HEADER, with the
163ad553a7Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying
173ad553a7Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner]
183ad553a7Sgavinm *
193ad553a7Sgavinm * CDDL HEADER END
203ad553a7Sgavinm */
213ad553a7Sgavinm
223ad553a7Sgavinm /*
23*a3114836SGerry Liu * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
247aec1d6eScindi * Use is subject to license terms.
257aec1d6eScindi */
267aec1d6eScindi
277aec1d6eScindi /*
287aec1d6eScindi * Public interface to routines implemented by CPU modules
297aec1d6eScindi */
307aec1d6eScindi
3120c794b3Sgavinm #include <sys/types.h>
3220c794b3Sgavinm #include <sys/atomic.h>
337aec1d6eScindi #include <sys/x86_archext.h>
347aec1d6eScindi #include <sys/cpu_module_impl.h>
3520c794b3Sgavinm #include <sys/cpu_module_ms.h>
367aec1d6eScindi #include <sys/fm/util.h>
377aec1d6eScindi #include <sys/reboot.h>
387aec1d6eScindi #include <sys/modctl.h>
397aec1d6eScindi #include <sys/param.h>
407aec1d6eScindi #include <sys/cmn_err.h>
417aec1d6eScindi #include <sys/systm.h>
4220c794b3Sgavinm #include <sys/fm/protocol.h>
4320c794b3Sgavinm #include <sys/pcb.h>
4420c794b3Sgavinm #include <sys/ontrap.h>
4520c794b3Sgavinm #include <sys/psw.h>
4620c794b3Sgavinm #include <sys/privregs.h>
47e4b86885SCheng Sean Ye #include <sys/machsystm.h>
487aec1d6eScindi
4920c794b3Sgavinm /*
5020c794b3Sgavinm * Set to force cmi_init to fail.
5120c794b3Sgavinm */
5220c794b3Sgavinm int cmi_no_init = 0;
537aec1d6eScindi
5420c794b3Sgavinm /*
5520c794b3Sgavinm * Set to avoid MCA initialization.
5620c794b3Sgavinm */
5720c794b3Sgavinm int cmi_no_mca_init = 0;
587aec1d6eScindi
597aec1d6eScindi /*
608a40a695Sgavinm * If cleared for debugging we will not attempt to load a model-specific
618a40a695Sgavinm * cpu module but will load the generic cpu module instead.
628a40a695Sgavinm */
638a40a695Sgavinm int cmi_force_generic = 0;
648a40a695Sgavinm
658a40a695Sgavinm /*
667aec1d6eScindi * If cleared for debugging, we will suppress panicking on fatal hardware
677aec1d6eScindi * errors. This should *only* be used for debugging; it use can and will
687aec1d6eScindi * cause data corruption if actual hardware errors are detected by the system.
697aec1d6eScindi */
707aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1;
717aec1d6eScindi
72e4b86885SCheng Sean Ye #ifndef __xpv
7320c794b3Sgavinm /*
74e3d60c9bSAdrian Frost * Set to indicate whether we are able to enable cmci interrupt.
75e3d60c9bSAdrian Frost */
76e3d60c9bSAdrian Frost int cmi_enable_cmci = 0;
77e4b86885SCheng Sean Ye #endif
78e3d60c9bSAdrian Frost
79e3d60c9bSAdrian Frost /*
8020c794b3Sgavinm * Subdirectory (relative to the module search path) in which we will
8120c794b3Sgavinm * look for cpu modules.
8220c794b3Sgavinm */
8320c794b3Sgavinm #define CPUMOD_SUBDIR "cpu"
8420c794b3Sgavinm
8520c794b3Sgavinm /*
8620c794b3Sgavinm * CPU modules have a filenames such as "cpu.AuthenticAMD.15" and
8720c794b3Sgavinm * "cpu.generic" - the "cpu" prefix is specified by the following.
8820c794b3Sgavinm */
8920c794b3Sgavinm #define CPUMOD_PREFIX "cpu"
9020c794b3Sgavinm
9120c794b3Sgavinm /*
9220c794b3Sgavinm * Structure used to keep track of cpu modules we have loaded and their ops
9320c794b3Sgavinm */
9420c794b3Sgavinm typedef struct cmi {
9520c794b3Sgavinm struct cmi *cmi_next;
9620c794b3Sgavinm struct cmi *cmi_prev;
9720c794b3Sgavinm const cmi_ops_t *cmi_ops;
9820c794b3Sgavinm struct modctl *cmi_modp;
9920c794b3Sgavinm uint_t cmi_refcnt;
10020c794b3Sgavinm } cmi_t;
10120c794b3Sgavinm
1027aec1d6eScindi static cmi_t *cmi_list;
103*a3114836SGerry Liu static const cmi_mc_ops_t *cmi_mc_global_ops;
104*a3114836SGerry Liu static void *cmi_mc_global_data;
1057aec1d6eScindi static kmutex_t cmi_load_lock;
1067aec1d6eScindi
10720c794b3Sgavinm /*
10820c794b3Sgavinm * Functions we need from cmi_hw.c that are not part of the cpu_module.h
10920c794b3Sgavinm * interface.
11020c794b3Sgavinm */
111e4b86885SCheng Sean Ye extern cmi_hdl_t cmi_hdl_create(enum cmi_hdl_class, uint_t, uint_t, uint_t);
112*a3114836SGerry Liu extern void cmi_hdl_destroy(cmi_hdl_t ophdl);
11320c794b3Sgavinm extern void cmi_hdl_setcmi(cmi_hdl_t, void *, void *);
11420c794b3Sgavinm extern void *cmi_hdl_getcmi(cmi_hdl_t);
11520c794b3Sgavinm extern void cmi_hdl_setmc(cmi_hdl_t, const struct cmi_mc_ops *, void *);
116e4b86885SCheng Sean Ye extern void cmi_hdl_inj_begin(cmi_hdl_t);
117e4b86885SCheng Sean Ye extern void cmi_hdl_inj_end(cmi_hdl_t);
118074bb90dSTom Pothier extern void cmi_read_smbios(cmi_hdl_t);
11920c794b3Sgavinm
12020c794b3Sgavinm #define HDL2CMI(hdl) cmi_hdl_getcmi(hdl)
12120c794b3Sgavinm
12220c794b3Sgavinm #define CMI_OPS(cmi) (cmi)->cmi_ops
12320c794b3Sgavinm #define CMI_OP_PRESENT(cmi, op) ((cmi) && CMI_OPS(cmi)->op != NULL)
12420c794b3Sgavinm
12520c794b3Sgavinm #define CMI_MATCH_VENDOR 0 /* Just match on vendor */
12620c794b3Sgavinm #define CMI_MATCH_FAMILY 1 /* Match down to family */
12720c794b3Sgavinm #define CMI_MATCH_MODEL 2 /* Match down to model */
12820c794b3Sgavinm #define CMI_MATCH_STEPPING 3 /* Match down to stepping */
12920c794b3Sgavinm
13020c794b3Sgavinm static void
cmi_link(cmi_t * cmi)13120c794b3Sgavinm cmi_link(cmi_t *cmi)
1327aec1d6eScindi {
13320c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
13420c794b3Sgavinm
13520c794b3Sgavinm cmi->cmi_prev = NULL;
13620c794b3Sgavinm cmi->cmi_next = cmi_list;
13720c794b3Sgavinm if (cmi_list != NULL)
13820c794b3Sgavinm cmi_list->cmi_prev = cmi;
13920c794b3Sgavinm cmi_list = cmi;
14020c794b3Sgavinm }
14120c794b3Sgavinm
14220c794b3Sgavinm static void
cmi_unlink(cmi_t * cmi)14320c794b3Sgavinm cmi_unlink(cmi_t *cmi)
14420c794b3Sgavinm {
14520c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
14620c794b3Sgavinm ASSERT(cmi->cmi_refcnt == 0);
14720c794b3Sgavinm
14820c794b3Sgavinm if (cmi->cmi_prev != NULL)
14920c794b3Sgavinm cmi->cmi_prev = cmi->cmi_next;
15020c794b3Sgavinm
15120c794b3Sgavinm if (cmi->cmi_next != NULL)
15220c794b3Sgavinm cmi->cmi_next->cmi_prev = cmi->cmi_prev;
15320c794b3Sgavinm
15420c794b3Sgavinm if (cmi_list == cmi)
15520c794b3Sgavinm cmi_list = cmi->cmi_next;
15620c794b3Sgavinm }
15720c794b3Sgavinm
15820c794b3Sgavinm /*
15920c794b3Sgavinm * Hold the module in memory. We call to CPU modules without using the
16020c794b3Sgavinm * stubs mechanism, so these modules must be manually held in memory.
16120c794b3Sgavinm * The mod_ref acts as if another loaded module has a dependency on us.
16220c794b3Sgavinm */
16320c794b3Sgavinm static void
cmi_hold(cmi_t * cmi)16420c794b3Sgavinm cmi_hold(cmi_t *cmi)
16520c794b3Sgavinm {
16620c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
16720c794b3Sgavinm
16820c794b3Sgavinm mutex_enter(&mod_lock);
16920c794b3Sgavinm cmi->cmi_modp->mod_ref++;
17020c794b3Sgavinm mutex_exit(&mod_lock);
17120c794b3Sgavinm cmi->cmi_refcnt++;
17220c794b3Sgavinm }
17320c794b3Sgavinm
17420c794b3Sgavinm static void
cmi_rele(cmi_t * cmi)17520c794b3Sgavinm cmi_rele(cmi_t *cmi)
17620c794b3Sgavinm {
17720c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
17820c794b3Sgavinm
17920c794b3Sgavinm mutex_enter(&mod_lock);
18020c794b3Sgavinm cmi->cmi_modp->mod_ref--;
18120c794b3Sgavinm mutex_exit(&mod_lock);
18220c794b3Sgavinm
18320c794b3Sgavinm if (--cmi->cmi_refcnt == 0) {
18420c794b3Sgavinm cmi_unlink(cmi);
18520c794b3Sgavinm kmem_free(cmi, sizeof (cmi_t));
18620c794b3Sgavinm }
18720c794b3Sgavinm }
18820c794b3Sgavinm
18920c794b3Sgavinm static cmi_ops_t *
cmi_getops(modctl_t * modp)19020c794b3Sgavinm cmi_getops(modctl_t *modp)
19120c794b3Sgavinm {
19220c794b3Sgavinm cmi_ops_t *ops;
19320c794b3Sgavinm
19420c794b3Sgavinm if ((ops = (cmi_ops_t *)modlookup_by_modctl(modp, "_cmi_ops")) ==
19520c794b3Sgavinm NULL) {
19620c794b3Sgavinm cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops "
19720c794b3Sgavinm "found", modp->mod_modname);
19820c794b3Sgavinm return (NULL);
19920c794b3Sgavinm }
20020c794b3Sgavinm
20120c794b3Sgavinm if (ops->cmi_init == NULL) {
20220c794b3Sgavinm cmn_err(CE_WARN, "cpu module '%s' is invalid: no cmi_init "
20320c794b3Sgavinm "entry point", modp->mod_modname);
20420c794b3Sgavinm return (NULL);
20520c794b3Sgavinm }
20620c794b3Sgavinm
20720c794b3Sgavinm return (ops);
2087aec1d6eScindi }
2097aec1d6eScindi
2107aec1d6eScindi static cmi_t *
cmi_load_modctl(modctl_t * modp)2117aec1d6eScindi cmi_load_modctl(modctl_t *modp)
2127aec1d6eScindi {
21320c794b3Sgavinm cmi_ops_t *ops;
21420c794b3Sgavinm uintptr_t ver;
2157aec1d6eScindi cmi_t *cmi;
21620c794b3Sgavinm cmi_api_ver_t apiver;
2177aec1d6eScindi
2187aec1d6eScindi ASSERT(MUTEX_HELD(&cmi_load_lock));
2197aec1d6eScindi
2207aec1d6eScindi for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
2217aec1d6eScindi if (cmi->cmi_modp == modp)
2227aec1d6eScindi return (cmi);
2237aec1d6eScindi }
2247aec1d6eScindi
22520c794b3Sgavinm if ((ver = modlookup_by_modctl(modp, "_cmi_api_version")) == NULL) {
22620c794b3Sgavinm /*
22720c794b3Sgavinm * Apparently a cpu module before versioning was introduced -
22820c794b3Sgavinm * we call this version 0.
22920c794b3Sgavinm */
23020c794b3Sgavinm apiver = CMI_API_VERSION_0;
23120c794b3Sgavinm } else {
23220c794b3Sgavinm apiver = *((cmi_api_ver_t *)ver);
23320c794b3Sgavinm if (!CMI_API_VERSION_CHKMAGIC(apiver)) {
23420c794b3Sgavinm cmn_err(CE_WARN, "cpu module '%s' is invalid: "
23520c794b3Sgavinm "_cmi_api_version 0x%x has bad magic",
23620c794b3Sgavinm modp->mod_modname, apiver);
23720c794b3Sgavinm return (NULL);
23820c794b3Sgavinm }
23920c794b3Sgavinm }
24020c794b3Sgavinm
24120c794b3Sgavinm if (apiver != CMI_API_VERSION) {
24220c794b3Sgavinm cmn_err(CE_WARN, "cpu module '%s' has API version %d, "
24320c794b3Sgavinm "kernel requires API version %d", modp->mod_modname,
24420c794b3Sgavinm CMI_API_VERSION_TOPRINT(apiver),
24520c794b3Sgavinm CMI_API_VERSION_TOPRINT(CMI_API_VERSION));
2467aec1d6eScindi return (NULL);
2477aec1d6eScindi }
2487aec1d6eScindi
24920c794b3Sgavinm if ((ops = cmi_getops(modp)) == NULL)
25020c794b3Sgavinm return (NULL);
2517aec1d6eScindi
25220c794b3Sgavinm cmi = kmem_zalloc(sizeof (*cmi), KM_SLEEP);
25320c794b3Sgavinm cmi->cmi_ops = ops;
2547aec1d6eScindi cmi->cmi_modp = modp;
2557aec1d6eScindi
25620c794b3Sgavinm cmi_link(cmi);
25720c794b3Sgavinm
25820c794b3Sgavinm return (cmi);
25920c794b3Sgavinm }
26020c794b3Sgavinm
26120c794b3Sgavinm static int
cmi_cpu_match(cmi_hdl_t hdl1,cmi_hdl_t hdl2,int match)26220c794b3Sgavinm cmi_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match)
26320c794b3Sgavinm {
26420c794b3Sgavinm if (match >= CMI_MATCH_VENDOR &&
26520c794b3Sgavinm cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2))
26620c794b3Sgavinm return (0);
26720c794b3Sgavinm
26820c794b3Sgavinm if (match >= CMI_MATCH_FAMILY &&
26920c794b3Sgavinm cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2))
27020c794b3Sgavinm return (0);
27120c794b3Sgavinm
27220c794b3Sgavinm if (match >= CMI_MATCH_MODEL &&
27320c794b3Sgavinm cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2))
27420c794b3Sgavinm return (0);
27520c794b3Sgavinm
27620c794b3Sgavinm if (match >= CMI_MATCH_STEPPING &&
27720c794b3Sgavinm cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2))
27820c794b3Sgavinm return (0);
27920c794b3Sgavinm
28020c794b3Sgavinm return (1);
28120c794b3Sgavinm }
28220c794b3Sgavinm
28320c794b3Sgavinm static int
cmi_search_list_cb(cmi_hdl_t whdl,void * arg1,void * arg2,void * arg3)28420c794b3Sgavinm cmi_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
28520c794b3Sgavinm {
28620c794b3Sgavinm cmi_hdl_t thdl = (cmi_hdl_t)arg1;
28720c794b3Sgavinm int match = *((int *)arg2);
28820c794b3Sgavinm cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3;
28920c794b3Sgavinm
29020c794b3Sgavinm if (cmi_cpu_match(thdl, whdl, match)) {
29120c794b3Sgavinm cmi_hdl_hold(whdl); /* short-term hold */
29220c794b3Sgavinm *rsltp = whdl;
29320c794b3Sgavinm return (CMI_HDL_WALK_DONE);
29420c794b3Sgavinm } else {
29520c794b3Sgavinm return (CMI_HDL_WALK_NEXT);
29620c794b3Sgavinm }
29720c794b3Sgavinm }
29820c794b3Sgavinm
29920c794b3Sgavinm static cmi_t *
cmi_search_list(cmi_hdl_t hdl,int match)30020c794b3Sgavinm cmi_search_list(cmi_hdl_t hdl, int match)
30120c794b3Sgavinm {
30220c794b3Sgavinm cmi_hdl_t dhdl = NULL;
30320c794b3Sgavinm cmi_t *cmi = NULL;
30420c794b3Sgavinm
30520c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
30620c794b3Sgavinm
30720c794b3Sgavinm cmi_hdl_walk(cmi_search_list_cb, (void *)hdl, (void *)&match, &dhdl);
30820c794b3Sgavinm if (dhdl) {
30920c794b3Sgavinm cmi = HDL2CMI(dhdl);
31020c794b3Sgavinm cmi_hdl_rele(dhdl); /* held in cmi_search_list_cb */
31120c794b3Sgavinm }
3127aec1d6eScindi
3137aec1d6eScindi return (cmi);
3147aec1d6eScindi }
3157aec1d6eScindi
3167aec1d6eScindi static cmi_t *
cmi_load_module(cmi_hdl_t hdl,int match,int * chosenp)31720c794b3Sgavinm cmi_load_module(cmi_hdl_t hdl, int match, int *chosenp)
3187aec1d6eScindi {
3197aec1d6eScindi modctl_t *modp;
3207aec1d6eScindi cmi_t *cmi;
32120c794b3Sgavinm int modid;
3227aec1d6eScindi uint_t s[3];
3237aec1d6eScindi
3247aec1d6eScindi ASSERT(MUTEX_HELD(&cmi_load_lock));
32520c794b3Sgavinm ASSERT(match == CMI_MATCH_STEPPING || match == CMI_MATCH_MODEL ||
32620c794b3Sgavinm match == CMI_MATCH_FAMILY || match == CMI_MATCH_VENDOR);
3277aec1d6eScindi
3287aec1d6eScindi /*
32920c794b3Sgavinm * Have we already loaded a module for a cpu with the same
33020c794b3Sgavinm * vendor/family/model/stepping?
3317aec1d6eScindi */
33220c794b3Sgavinm if ((cmi = cmi_search_list(hdl, match)) != NULL) {
33320c794b3Sgavinm cmi_hold(cmi);
33420c794b3Sgavinm return (cmi);
33520c794b3Sgavinm }
3367aec1d6eScindi
33720c794b3Sgavinm s[0] = cmi_hdl_family(hdl);
33820c794b3Sgavinm s[1] = cmi_hdl_model(hdl);
33920c794b3Sgavinm s[2] = cmi_hdl_stepping(hdl);
3407aec1d6eScindi modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
34120c794b3Sgavinm cmi_hdl_vendorstr(hdl), ".", s, match, chosenp);
3427aec1d6eScindi
3437aec1d6eScindi if (modid == -1)
3447aec1d6eScindi return (NULL);
3457aec1d6eScindi
3467aec1d6eScindi modp = mod_hold_by_id(modid);
3477aec1d6eScindi cmi = cmi_load_modctl(modp);
34820c794b3Sgavinm if (cmi)
34920c794b3Sgavinm cmi_hold(cmi);
3507aec1d6eScindi mod_release_mod(modp);
3517aec1d6eScindi
3527aec1d6eScindi return (cmi);
3537aec1d6eScindi }
3547aec1d6eScindi
35520c794b3Sgavinm /*
35620c794b3Sgavinm * Try to load a cpu module with specific support for this chip type.
35720c794b3Sgavinm */
3587aec1d6eScindi static cmi_t *
cmi_load_specific(cmi_hdl_t hdl,void ** datap)35920c794b3Sgavinm cmi_load_specific(cmi_hdl_t hdl, void **datap)
36020c794b3Sgavinm {
36120c794b3Sgavinm cmi_t *cmi;
36220c794b3Sgavinm int err;
36320c794b3Sgavinm int i;
36420c794b3Sgavinm
36520c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
36620c794b3Sgavinm
36720c794b3Sgavinm for (i = CMI_MATCH_STEPPING; i >= CMI_MATCH_VENDOR; i--) {
36820c794b3Sgavinm int suffixlevel;
36920c794b3Sgavinm
37020c794b3Sgavinm if ((cmi = cmi_load_module(hdl, i, &suffixlevel)) == NULL)
37120c794b3Sgavinm return (NULL);
37220c794b3Sgavinm
37320c794b3Sgavinm /*
37420c794b3Sgavinm * A module has loaded and has a _cmi_ops structure, and the
37520c794b3Sgavinm * module has been held for this instance. Call its cmi_init
37620c794b3Sgavinm * entry point - we expect success (0) or ENOTSUP.
37720c794b3Sgavinm */
37820c794b3Sgavinm if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) == 0) {
37920c794b3Sgavinm if (boothowto & RB_VERBOSE) {
38020c794b3Sgavinm printf("initialized cpu module '%s' on "
38120c794b3Sgavinm "chip %d core %d strand %d\n",
38220c794b3Sgavinm cmi->cmi_modp->mod_modname,
38320c794b3Sgavinm cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
38420c794b3Sgavinm cmi_hdl_strandid(hdl));
38520c794b3Sgavinm }
38620c794b3Sgavinm return (cmi);
38720c794b3Sgavinm } else if (err != ENOTSUP) {
38820c794b3Sgavinm cmn_err(CE_WARN, "failed to init cpu module '%s' on "
38920c794b3Sgavinm "chip %d core %d strand %d: err=%d\n",
39020c794b3Sgavinm cmi->cmi_modp->mod_modname,
39120c794b3Sgavinm cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
39220c794b3Sgavinm cmi_hdl_strandid(hdl), err);
39320c794b3Sgavinm }
39420c794b3Sgavinm
39520c794b3Sgavinm /*
39620c794b3Sgavinm * The module failed or declined to init, so release
39720c794b3Sgavinm * it and update i to be equal to the number
39820c794b3Sgavinm * of suffices actually used in the last module path.
39920c794b3Sgavinm */
40020c794b3Sgavinm cmi_rele(cmi);
40120c794b3Sgavinm i = suffixlevel;
40220c794b3Sgavinm }
40320c794b3Sgavinm
40420c794b3Sgavinm return (NULL);
40520c794b3Sgavinm }
40620c794b3Sgavinm
40720c794b3Sgavinm /*
40820c794b3Sgavinm * Load the generic IA32 MCA cpu module, which may still supplement
40920c794b3Sgavinm * itself with model-specific support through cpu model-specific modules.
41020c794b3Sgavinm */
41120c794b3Sgavinm static cmi_t *
cmi_load_generic(cmi_hdl_t hdl,void ** datap)41220c794b3Sgavinm cmi_load_generic(cmi_hdl_t hdl, void **datap)
4137aec1d6eScindi {
4147aec1d6eScindi modctl_t *modp;
4157aec1d6eScindi cmi_t *cmi;
4167aec1d6eScindi int modid;
41720c794b3Sgavinm int err;
41820c794b3Sgavinm
41920c794b3Sgavinm ASSERT(MUTEX_HELD(&cmi_load_lock));
4207aec1d6eScindi
4217aec1d6eScindi if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
4227aec1d6eScindi return (NULL);
4237aec1d6eScindi
4247aec1d6eScindi modp = mod_hold_by_id(modid);
4257aec1d6eScindi cmi = cmi_load_modctl(modp);
42620c794b3Sgavinm if (cmi)
42720c794b3Sgavinm cmi_hold(cmi);
4287aec1d6eScindi mod_release_mod(modp);
4297aec1d6eScindi
43020c794b3Sgavinm if (cmi == NULL)
43120c794b3Sgavinm return (NULL);
43220c794b3Sgavinm
43320c794b3Sgavinm if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) != 0) {
43420c794b3Sgavinm if (err != ENOTSUP)
43520c794b3Sgavinm cmn_err(CE_WARN, CPUMOD_PREFIX ".generic failed to "
43620c794b3Sgavinm "init: err=%d", err);
43720c794b3Sgavinm cmi_rele(cmi);
43820c794b3Sgavinm return (NULL);
43920c794b3Sgavinm }
44020c794b3Sgavinm
4417aec1d6eScindi return (cmi);
4427aec1d6eScindi }
4437aec1d6eScindi
44420c794b3Sgavinm cmi_hdl_t
cmi_init(enum cmi_hdl_class class,uint_t chipid,uint_t coreid,uint_t strandid)44520c794b3Sgavinm cmi_init(enum cmi_hdl_class class, uint_t chipid, uint_t coreid,
446e4b86885SCheng Sean Ye uint_t strandid)
4477aec1d6eScindi {
44820c794b3Sgavinm cmi_t *cmi = NULL;
44920c794b3Sgavinm cmi_hdl_t hdl;
4507aec1d6eScindi void *data;
4517aec1d6eScindi
45220c794b3Sgavinm if (cmi_no_init) {
45320c794b3Sgavinm cmi_no_mca_init = 1;
45420c794b3Sgavinm return (NULL);
45520c794b3Sgavinm }
45620c794b3Sgavinm
4577aec1d6eScindi mutex_enter(&cmi_load_lock);
4587aec1d6eScindi
459e4b86885SCheng Sean Ye if ((hdl = cmi_hdl_create(class, chipid, coreid, strandid)) == NULL) {
4607aec1d6eScindi mutex_exit(&cmi_load_lock);
46120c794b3Sgavinm cmn_err(CE_WARN, "There will be no MCA support on chip %d "
46220c794b3Sgavinm "core %d strand %d (cmi_hdl_create returned NULL)\n",
46320c794b3Sgavinm chipid, coreid, strandid);
46420c794b3Sgavinm return (NULL);
4657aec1d6eScindi }
4667aec1d6eScindi
46720c794b3Sgavinm if (!cmi_force_generic)
46820c794b3Sgavinm cmi = cmi_load_specific(hdl, &data);
46920c794b3Sgavinm
47020c794b3Sgavinm if (cmi == NULL && (cmi = cmi_load_generic(hdl, &data)) == NULL) {
47120c794b3Sgavinm cmn_err(CE_WARN, "There will be no MCA support on chip %d "
47220c794b3Sgavinm "core %d strand %d\n", chipid, coreid, strandid);
47320c794b3Sgavinm cmi_hdl_rele(hdl);
4747aec1d6eScindi mutex_exit(&cmi_load_lock);
47520c794b3Sgavinm return (NULL);
4767aec1d6eScindi }
4777aec1d6eScindi
47820c794b3Sgavinm cmi_hdl_setcmi(hdl, cmi, data);
4797aec1d6eScindi
48020c794b3Sgavinm cms_init(hdl);
48120c794b3Sgavinm
482074bb90dSTom Pothier cmi_read_smbios(hdl);
483074bb90dSTom Pothier
4847aec1d6eScindi mutex_exit(&cmi_load_lock);
4857aec1d6eScindi
48620c794b3Sgavinm return (hdl);
4877aec1d6eScindi }
4887aec1d6eScindi
48920c794b3Sgavinm /*
490*a3114836SGerry Liu * cmi_fini is called on DR deconfigure of a cpu resource.
491*a3114836SGerry Liu * It should not be called at simple offline of a cpu.
49220c794b3Sgavinm */
4937aec1d6eScindi void
cmi_fini(cmi_hdl_t hdl)49420c794b3Sgavinm cmi_fini(cmi_hdl_t hdl)
4957aec1d6eScindi {
49620c794b3Sgavinm cmi_t *cmi = HDL2CMI(hdl);
49720c794b3Sgavinm
49820c794b3Sgavinm if (cms_present(hdl))
49920c794b3Sgavinm cms_fini(hdl);
50020c794b3Sgavinm
50120c794b3Sgavinm if (CMI_OP_PRESENT(cmi, cmi_fini))
50220c794b3Sgavinm CMI_OPS(cmi)->cmi_fini(hdl);
50320c794b3Sgavinm
504*a3114836SGerry Liu cmi_hdl_destroy(hdl);
5057aec1d6eScindi }
5067aec1d6eScindi
50720c794b3Sgavinm /*
508e4b86885SCheng Sean Ye * cmi_post_startup is called from post_startup for the boot cpu only (no
509e4b86885SCheng Sean Ye * other cpus are started yet).
51020c794b3Sgavinm */
5117aec1d6eScindi void
cmi_post_startup(void)51220c794b3Sgavinm cmi_post_startup(void)
5137aec1d6eScindi {
51420c794b3Sgavinm cmi_hdl_t hdl;
51520c794b3Sgavinm cmi_t *cmi;
51620c794b3Sgavinm
51720c794b3Sgavinm if (cmi_no_mca_init != 0 ||
51820c794b3Sgavinm (hdl = cmi_hdl_any()) == NULL) /* short-term hold */
51920c794b3Sgavinm return;
52020c794b3Sgavinm
52120c794b3Sgavinm cmi = HDL2CMI(hdl);
52220c794b3Sgavinm
52320c794b3Sgavinm if (CMI_OP_PRESENT(cmi, cmi_post_startup))
52420c794b3Sgavinm CMI_OPS(cmi)->cmi_post_startup(hdl);
52520c794b3Sgavinm
52620c794b3Sgavinm cmi_hdl_rele(hdl);
5277aec1d6eScindi }
5287aec1d6eScindi
5298a40a695Sgavinm /*
5308a40a695Sgavinm * Called just once from start_other_cpus when all processors are started.
5318a40a695Sgavinm * This will not be called for each cpu, so the registered op must not
532e4b86885SCheng Sean Ye * assume it is called as such. We are not necessarily executing on
533e4b86885SCheng Sean Ye * the boot cpu.
5348a40a695Sgavinm */
5357aec1d6eScindi void
cmi_post_mpstartup(void)5363ad553a7Sgavinm cmi_post_mpstartup(void)
5373ad553a7Sgavinm {
53820c794b3Sgavinm cmi_hdl_t hdl;
53920c794b3Sgavinm cmi_t *cmi;
54020c794b3Sgavinm
54120c794b3Sgavinm if (cmi_no_mca_init != 0 ||
54220c794b3Sgavinm (hdl = cmi_hdl_any()) == NULL) /* short-term hold */
54320c794b3Sgavinm return;
54420c794b3Sgavinm
54520c794b3Sgavinm cmi = HDL2CMI(hdl);
54620c794b3Sgavinm
54720c794b3Sgavinm if (CMI_OP_PRESENT(cmi, cmi_post_mpstartup))
54820c794b3Sgavinm CMI_OPS(cmi)->cmi_post_mpstartup(hdl);
54920c794b3Sgavinm
55020c794b3Sgavinm cmi_hdl_rele(hdl);
5513ad553a7Sgavinm }
5523ad553a7Sgavinm
5533ad553a7Sgavinm void
cmi_faulted_enter(cmi_hdl_t hdl)55420c794b3Sgavinm cmi_faulted_enter(cmi_hdl_t hdl)
5557aec1d6eScindi {
55620c794b3Sgavinm cmi_t *cmi = HDL2CMI(hdl);
55720c794b3Sgavinm
55820c794b3Sgavinm if (cmi_no_mca_init != 0)
55920c794b3Sgavinm return;
56020c794b3Sgavinm
56120c794b3Sgavinm if (CMI_OP_PRESENT(cmi, cmi_faulted_enter))
56220c794b3Sgavinm CMI_OPS(cmi)->cmi_faulted_enter(hdl);
5637aec1d6eScindi }
5647aec1d6eScindi
5657aec1d6eScindi void
cmi_faulted_exit(cmi_hdl_t hdl)56620c794b3Sgavinm cmi_faulted_exit(cmi_hdl_t hdl)
5677aec1d6eScindi {
56820c794b3Sgavinm cmi_t *cmi = HDL2CMI(hdl);
56920c794b3Sgavinm
57020c794b3Sgavinm if (cmi_no_mca_init != 0)
57120c794b3Sgavinm return;
57220c794b3Sgavinm
57320c794b3Sgavinm if (CMI_OP_PRESENT(cmi, cmi_faulted_exit))
57420c794b3Sgavinm CMI_OPS(cmi)->cmi_faulted_exit(hdl);
5757aec1d6eScindi }
5767aec1d6eScindi
57720c794b3Sgavinm void
cmi_mca_init(cmi_hdl_t hdl)57820c794b3Sgavinm cmi_mca_init(cmi_hdl_t hdl)
57920c794b3Sgavinm {
58020c794b3Sgavinm cmi_t *cmi;
58120c794b3Sgavinm
58220c794b3Sgavinm if (cmi_no_mca_init != 0)
58320c794b3Sgavinm return;
58420c794b3Sgavinm
58520c794b3Sgavinm cmi = HDL2CMI(hdl);
58620c794b3Sgavinm
58720c794b3Sgavinm if (CMI_OP_PRESENT(cmi, cmi_mca_init))
58820c794b3Sgavinm CMI_OPS(cmi)->cmi_mca_init(hdl);
58920c794b3Sgavinm }
59020c794b3Sgavinm
59120c794b3Sgavinm #define CMI_RESPONSE_PANIC 0x0 /* panic must have value 0 */
59220c794b3Sgavinm #define CMI_RESPONSE_NONE 0x1
59320c794b3Sgavinm #define CMI_RESPONSE_CKILL 0x2
59420c794b3Sgavinm #define CMI_RESPONSE_REBOOT 0x3 /* not implemented */
59520c794b3Sgavinm #define CMI_RESPONSE_ONTRAP_PROT 0x4
59620c794b3Sgavinm #define CMI_RESPONSE_LOFAULT_PROT 0x5
59720c794b3Sgavinm
59820c794b3Sgavinm /*
59920c794b3Sgavinm * Return 0 if we will panic in response to this machine check, otherwise
60020c794b3Sgavinm * non-zero. If the caller is cmi_mca_trap in this file then the nonzero
60120c794b3Sgavinm * return values are to be interpreted from CMI_RESPONSE_* above.
60220c794b3Sgavinm *
60320c794b3Sgavinm * This function must just return what will be done without actually
60420c794b3Sgavinm * doing anything; this includes not changing the regs.
60520c794b3Sgavinm */
6067aec1d6eScindi int
cmi_mce_response(struct regs * rp,uint64_t disp)60720c794b3Sgavinm cmi_mce_response(struct regs *rp, uint64_t disp)
6087aec1d6eScindi {
60920c794b3Sgavinm int panicrsp = cmi_panic_on_uncorrectable_error ? CMI_RESPONSE_PANIC :
61020c794b3Sgavinm CMI_RESPONSE_NONE;
61120c794b3Sgavinm on_trap_data_t *otp;
61220c794b3Sgavinm
61320c794b3Sgavinm ASSERT(rp != NULL); /* don't call for polling, only on #MC */
61420c794b3Sgavinm
61520c794b3Sgavinm /*
61620c794b3Sgavinm * If no bits are set in the disposition then there is nothing to
61720c794b3Sgavinm * worry about and we do not need to trampoline to ontrap or
61820c794b3Sgavinm * lofault handlers.
61920c794b3Sgavinm */
62020c794b3Sgavinm if (disp == 0)
62120c794b3Sgavinm return (CMI_RESPONSE_NONE);
62220c794b3Sgavinm
62320c794b3Sgavinm /*
62420c794b3Sgavinm * Unconstrained errors cannot be forgiven, even by ontrap or
62520c794b3Sgavinm * lofault protection. The data is not poisoned and may not
62620c794b3Sgavinm * even belong to the trapped context - eg a writeback of
62720c794b3Sgavinm * data that is found to be bad.
62820c794b3Sgavinm */
62920c794b3Sgavinm if (disp & CMI_ERRDISP_UC_UNCONSTRAINED)
63020c794b3Sgavinm return (panicrsp);
63120c794b3Sgavinm
63220c794b3Sgavinm /*
63320c794b3Sgavinm * ontrap OT_DATA_EC and lofault protection forgive any disposition
63420c794b3Sgavinm * other than unconstrained, even those normally forced fatal.
63520c794b3Sgavinm */
63620c794b3Sgavinm if ((otp = curthread->t_ontrap) != NULL && otp->ot_prot & OT_DATA_EC)
63720c794b3Sgavinm return (CMI_RESPONSE_ONTRAP_PROT);
63820c794b3Sgavinm else if (curthread->t_lofault)
63920c794b3Sgavinm return (CMI_RESPONSE_LOFAULT_PROT);
64020c794b3Sgavinm
64120c794b3Sgavinm /*
64220c794b3Sgavinm * Forced-fatal errors are terminal even in user mode.
64320c794b3Sgavinm */
64420c794b3Sgavinm if (disp & CMI_ERRDISP_FORCEFATAL)
64520c794b3Sgavinm return (panicrsp);
64620c794b3Sgavinm
64720c794b3Sgavinm /*
64820c794b3Sgavinm * If the trapped context is corrupt or we have no instruction pointer
64920c794b3Sgavinm * to resume at (and aren't trampolining to a fault handler)
65020c794b3Sgavinm * then in the kernel case we must panic and in usermode we
65120c794b3Sgavinm * kill the affected contract.
65220c794b3Sgavinm */
65320c794b3Sgavinm if (disp & (CMI_ERRDISP_CURCTXBAD | CMI_ERRDISP_RIPV_INVALID))
65420c794b3Sgavinm return (USERMODE(rp->r_cs) ? CMI_RESPONSE_CKILL : panicrsp);
65520c794b3Sgavinm
65620c794b3Sgavinm /*
65720c794b3Sgavinm * Anything else is harmless
65820c794b3Sgavinm */
65920c794b3Sgavinm return (CMI_RESPONSE_NONE);
6607aec1d6eScindi }
6617aec1d6eScindi
66220c794b3Sgavinm int cma_mca_trap_panic_suppressed = 0;
66320c794b3Sgavinm
66420c794b3Sgavinm static void
cmi_mca_panic(void)66520c794b3Sgavinm cmi_mca_panic(void)
6667aec1d6eScindi {
66720c794b3Sgavinm if (cmi_panic_on_uncorrectable_error) {
66820c794b3Sgavinm fm_panic("Unrecoverable Machine-Check Exception");
66920c794b3Sgavinm } else {
67020c794b3Sgavinm cmn_err(CE_WARN, "suppressing panic from fatal #mc");
67120c794b3Sgavinm cma_mca_trap_panic_suppressed++;
67220c794b3Sgavinm }
6737aec1d6eScindi }
6747aec1d6eScindi
67520c794b3Sgavinm
67620c794b3Sgavinm int cma_mca_trap_contract_kills = 0;
67720c794b3Sgavinm int cma_mca_trap_ontrap_forgiven = 0;
67820c794b3Sgavinm int cma_mca_trap_lofault_forgiven = 0;
67920c794b3Sgavinm
68020c794b3Sgavinm /*
68120c794b3Sgavinm * Native #MC handler - we branch to here from mcetrap
68220c794b3Sgavinm */
68320c794b3Sgavinm /*ARGSUSED*/
6847aec1d6eScindi void
cmi_mca_trap(struct regs * rp)6857aec1d6eScindi cmi_mca_trap(struct regs *rp)
6867aec1d6eScindi {
68720c794b3Sgavinm #ifndef __xpv
68820c794b3Sgavinm cmi_hdl_t hdl = NULL;
68920c794b3Sgavinm uint64_t disp;
69020c794b3Sgavinm cmi_t *cmi;
69120c794b3Sgavinm int s;
69220c794b3Sgavinm
69320c794b3Sgavinm if (cmi_no_mca_init != 0)
69420c794b3Sgavinm return;
69520c794b3Sgavinm
69620c794b3Sgavinm /*
69720c794b3Sgavinm * This function can call cmn_err, and the cpu module cmi_mca_trap
69820c794b3Sgavinm * entry point may also elect to call cmn_err (e.g., if it can't
69920c794b3Sgavinm * log the error onto an errorq, say very early in boot).
70020c794b3Sgavinm * We need to let cprintf know that we must not block.
70120c794b3Sgavinm */
70220c794b3Sgavinm s = spl8();
70320c794b3Sgavinm
70420c794b3Sgavinm if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU),
70520c794b3Sgavinm cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL ||
70620c794b3Sgavinm (cmi = HDL2CMI(hdl)) == NULL ||
70720c794b3Sgavinm !CMI_OP_PRESENT(cmi, cmi_mca_trap)) {
70820c794b3Sgavinm
70920c794b3Sgavinm cmn_err(CE_WARN, "#MC exception on cpuid %d: %s",
71020c794b3Sgavinm CPU->cpu_id,
71120c794b3Sgavinm hdl ? "handle lookup ok but no #MC handler found" :
71220c794b3Sgavinm "handle lookup failed");
71320c794b3Sgavinm
71420c794b3Sgavinm if (hdl != NULL)
71520c794b3Sgavinm cmi_hdl_rele(hdl);
71620c794b3Sgavinm
71720c794b3Sgavinm splx(s);
71820c794b3Sgavinm return;
7197aec1d6eScindi }
7207aec1d6eScindi
72120c794b3Sgavinm disp = CMI_OPS(cmi)->cmi_mca_trap(hdl, rp);
7227aec1d6eScindi
72320c794b3Sgavinm switch (cmi_mce_response(rp, disp)) {
72420c794b3Sgavinm default:
72520c794b3Sgavinm cmn_err(CE_WARN, "Invalid response from cmi_mce_response");
72620c794b3Sgavinm /*FALLTHRU*/
7277aec1d6eScindi
72820c794b3Sgavinm case CMI_RESPONSE_PANIC:
72920c794b3Sgavinm cmi_mca_panic();
73020c794b3Sgavinm break;
73120c794b3Sgavinm
73220c794b3Sgavinm case CMI_RESPONSE_NONE:
73320c794b3Sgavinm break;
73420c794b3Sgavinm
73520c794b3Sgavinm case CMI_RESPONSE_CKILL:
73620c794b3Sgavinm ttolwp(curthread)->lwp_pcb.pcb_flags |= ASYNC_HWERR;
73720c794b3Sgavinm aston(curthread);
73820c794b3Sgavinm cma_mca_trap_contract_kills++;
73920c794b3Sgavinm break;
74020c794b3Sgavinm
74120c794b3Sgavinm case CMI_RESPONSE_ONTRAP_PROT: {
74220c794b3Sgavinm on_trap_data_t *otp = curthread->t_ontrap;
74320c794b3Sgavinm otp->ot_trap = OT_DATA_EC;
74420c794b3Sgavinm rp->r_pc = otp->ot_trampoline;
74520c794b3Sgavinm cma_mca_trap_ontrap_forgiven++;
74620c794b3Sgavinm break;
74720c794b3Sgavinm }
74820c794b3Sgavinm
74920c794b3Sgavinm case CMI_RESPONSE_LOFAULT_PROT:
75020c794b3Sgavinm rp->r_r0 = EFAULT;
75120c794b3Sgavinm rp->r_pc = curthread->t_lofault;
75220c794b3Sgavinm cma_mca_trap_lofault_forgiven++;
75320c794b3Sgavinm break;
75420c794b3Sgavinm }
75520c794b3Sgavinm
75620c794b3Sgavinm cmi_hdl_rele(hdl);
75720c794b3Sgavinm splx(s);
75820c794b3Sgavinm #endif /* __xpv */
7597aec1d6eScindi }
7607aec1d6eScindi
7617aec1d6eScindi void
cmi_hdl_poke(cmi_hdl_t hdl)76220c794b3Sgavinm cmi_hdl_poke(cmi_hdl_t hdl)
7637aec1d6eScindi {
76420c794b3Sgavinm cmi_t *cmi = HDL2CMI(hdl);
76520c794b3Sgavinm
76620c794b3Sgavinm if (!CMI_OP_PRESENT(cmi, cmi_hdl_poke))
76720c794b3Sgavinm return;
76820c794b3Sgavinm
76920c794b3Sgavinm CMI_OPS(cmi)->cmi_hdl_poke(hdl);
7707aec1d6eScindi }
7717aec1d6eScindi
772e4b86885SCheng Sean Ye #ifndef __xpv
7737aec1d6eScindi void
cmi_cmci_trap()774e3d60c9bSAdrian Frost cmi_cmci_trap()
775e3d60c9bSAdrian Frost {
776e3d60c9bSAdrian Frost cmi_hdl_t hdl = NULL;
777e3d60c9bSAdrian Frost cmi_t *cmi;
778e3d60c9bSAdrian Frost
779e3d60c9bSAdrian Frost if (cmi_no_mca_init != 0)
780e3d60c9bSAdrian Frost return;
781e3d60c9bSAdrian Frost
782e3d60c9bSAdrian Frost if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU),
783e3d60c9bSAdrian Frost cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL ||
784e3d60c9bSAdrian Frost (cmi = HDL2CMI(hdl)) == NULL ||
785e3d60c9bSAdrian Frost !CMI_OP_PRESENT(cmi, cmi_cmci_trap)) {
786e3d60c9bSAdrian Frost
787e3d60c9bSAdrian Frost cmn_err(CE_WARN, "CMCI interrupt on cpuid %d: %s",
788e3d60c9bSAdrian Frost CPU->cpu_id,
789e3d60c9bSAdrian Frost hdl ? "handle lookup ok but no CMCI handler found" :
790e3d60c9bSAdrian Frost "handle lookup failed");
791e3d60c9bSAdrian Frost
792e3d60c9bSAdrian Frost if (hdl != NULL)
793e3d60c9bSAdrian Frost cmi_hdl_rele(hdl);
794e3d60c9bSAdrian Frost
795e3d60c9bSAdrian Frost return;
796e3d60c9bSAdrian Frost }
797e3d60c9bSAdrian Frost
798e3d60c9bSAdrian Frost CMI_OPS(cmi)->cmi_cmci_trap(hdl);
799e3d60c9bSAdrian Frost
800e3d60c9bSAdrian Frost cmi_hdl_rele(hdl);
801e3d60c9bSAdrian Frost }
802e4b86885SCheng Sean Ye #endif /* __xpv */
803e3d60c9bSAdrian Frost
804e3d60c9bSAdrian Frost void
cmi_mc_register(cmi_hdl_t hdl,const cmi_mc_ops_t * mcops,void * mcdata)80520c794b3Sgavinm cmi_mc_register(cmi_hdl_t hdl, const cmi_mc_ops_t *mcops, void *mcdata)
8067aec1d6eScindi {
80720c794b3Sgavinm if (!cmi_no_mca_init)
80820c794b3Sgavinm cmi_hdl_setmc(hdl, mcops, mcdata);
8097aec1d6eScindi }
8107aec1d6eScindi
811*a3114836SGerry Liu cmi_errno_t
cmi_mc_register_global(const cmi_mc_ops_t * mcops,void * mcdata)812*a3114836SGerry Liu cmi_mc_register_global(const cmi_mc_ops_t *mcops, void *mcdata)
813*a3114836SGerry Liu {
814*a3114836SGerry Liu if (!cmi_no_mca_init) {
815*a3114836SGerry Liu if (cmi_mc_global_ops != NULL || cmi_mc_global_data != NULL ||
816*a3114836SGerry Liu mcops == NULL || mcops->cmi_mc_patounum == NULL ||
817*a3114836SGerry Liu mcops->cmi_mc_unumtopa == NULL) {
818*a3114836SGerry Liu return (CMIERR_UNKNOWN);
819*a3114836SGerry Liu }
820*a3114836SGerry Liu cmi_mc_global_data = mcdata;
821*a3114836SGerry Liu cmi_mc_global_ops = mcops;
822*a3114836SGerry Liu }
823*a3114836SGerry Liu return (CMI_SUCCESS);
824*a3114836SGerry Liu }
825*a3114836SGerry Liu
826e4b86885SCheng Sean Ye void
cmi_mc_sw_memscrub_disable(void)827e4b86885SCheng Sean Ye cmi_mc_sw_memscrub_disable(void)
828e4b86885SCheng Sean Ye {
829e4b86885SCheng Sean Ye memscrub_disable();
830e4b86885SCheng Sean Ye }
831e4b86885SCheng Sean Ye
83220c794b3Sgavinm cmi_errno_t
cmi_mc_patounum(uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * up)8334156fc34Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd,
8344156fc34Sgavinm int syndtype, mc_unum_t *up)
8357aec1d6eScindi {
8367aec1d6eScindi const struct cmi_mc_ops *mcops;
83720c794b3Sgavinm cmi_hdl_t hdl;
83820c794b3Sgavinm cmi_errno_t rv;
8397aec1d6eScindi
840*a3114836SGerry Liu if (cmi_no_mca_init)
841*a3114836SGerry Liu return (CMIERR_MC_ABSENT);
842*a3114836SGerry Liu
843*a3114836SGerry Liu if (cmi_mc_global_ops != NULL) {
844*a3114836SGerry Liu if (cmi_mc_global_ops->cmi_mc_patounum == NULL)
845*a3114836SGerry Liu return (CMIERR_MC_NOTSUP);
846*a3114836SGerry Liu return (cmi_mc_global_ops->cmi_mc_patounum(cmi_mc_global_data,
847*a3114836SGerry Liu pa, valid_hi, valid_lo, synd, syndtype, up));
848*a3114836SGerry Liu }
849*a3114836SGerry Liu
850*a3114836SGerry Liu if ((hdl = cmi_hdl_any()) == NULL) /* short-term hold */
85120c794b3Sgavinm return (CMIERR_MC_ABSENT);
8527aec1d6eScindi
85320c794b3Sgavinm if ((mcops = cmi_hdl_getmcops(hdl)) == NULL ||
85420c794b3Sgavinm mcops->cmi_mc_patounum == NULL) {
85520c794b3Sgavinm cmi_hdl_rele(hdl);
85620c794b3Sgavinm return (CMIERR_MC_NOTSUP);
8577aec1d6eScindi }
8587aec1d6eScindi
85920c794b3Sgavinm rv = mcops->cmi_mc_patounum(cmi_hdl_getmcdata(hdl), pa, valid_hi,
86020c794b3Sgavinm valid_lo, synd, syndtype, up);
86120c794b3Sgavinm
86220c794b3Sgavinm cmi_hdl_rele(hdl);
86320c794b3Sgavinm
86420c794b3Sgavinm return (rv);
86520c794b3Sgavinm }
86620c794b3Sgavinm
86720c794b3Sgavinm cmi_errno_t
cmi_mc_unumtopa(mc_unum_t * up,nvlist_t * nvl,uint64_t * pap)8687aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
8697aec1d6eScindi {
8707aec1d6eScindi const struct cmi_mc_ops *mcops;
87120c794b3Sgavinm cmi_hdl_t hdl;
87220c794b3Sgavinm cmi_errno_t rv;
873e4b86885SCheng Sean Ye nvlist_t *hcsp;
8747aec1d6eScindi
8757aec1d6eScindi if (up != NULL && nvl != NULL)
87620c794b3Sgavinm return (CMIERR_API); /* convert from just one form */
8777aec1d6eScindi
878*a3114836SGerry Liu if (cmi_no_mca_init)
879*a3114836SGerry Liu return (CMIERR_MC_ABSENT);
880*a3114836SGerry Liu
881*a3114836SGerry Liu if (cmi_mc_global_ops != NULL) {
882*a3114836SGerry Liu if (cmi_mc_global_ops->cmi_mc_unumtopa == NULL)
883*a3114836SGerry Liu return (CMIERR_MC_NOTSUP);
884*a3114836SGerry Liu return (cmi_mc_global_ops->cmi_mc_unumtopa(cmi_mc_global_data,
885*a3114836SGerry Liu up, nvl, pap));
886*a3114836SGerry Liu }
887*a3114836SGerry Liu
888*a3114836SGerry Liu if ((hdl = cmi_hdl_any()) == NULL) /* short-term hold */
88920c794b3Sgavinm return (CMIERR_MC_ABSENT);
8907aec1d6eScindi
89120c794b3Sgavinm if ((mcops = cmi_hdl_getmcops(hdl)) == NULL ||
89220c794b3Sgavinm mcops->cmi_mc_unumtopa == NULL) {
89320c794b3Sgavinm cmi_hdl_rele(hdl);
89420c794b3Sgavinm
895e4b86885SCheng Sean Ye if (nvl != NULL && nvlist_lookup_nvlist(nvl,
896e4b86885SCheng Sean Ye FM_FMRI_HC_SPECIFIC, &hcsp) == 0 &&
897e4b86885SCheng Sean Ye (nvlist_lookup_uint64(hcsp,
898e4b86885SCheng Sean Ye "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, pap) == 0 ||
899e4b86885SCheng Sean Ye nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR,
900e4b86885SCheng Sean Ye pap) == 0)) {
90120c794b3Sgavinm return (CMIERR_MC_PARTIALUNUMTOPA);
90220c794b3Sgavinm } else {
903*a3114836SGerry Liu return (mcops && mcops->cmi_mc_unumtopa == NULL ?
90420c794b3Sgavinm CMIERR_MC_NOTSUP : CMIERR_MC_ABSENT);
90520c794b3Sgavinm }
90620c794b3Sgavinm }
90720c794b3Sgavinm
90820c794b3Sgavinm rv = mcops->cmi_mc_unumtopa(cmi_hdl_getmcdata(hdl), up, nvl, pap);
90920c794b3Sgavinm
91020c794b3Sgavinm cmi_hdl_rele(hdl);
91120c794b3Sgavinm
91220c794b3Sgavinm return (rv);
91320c794b3Sgavinm }
91420c794b3Sgavinm
91520c794b3Sgavinm void
cmi_mc_logout(cmi_hdl_t hdl,boolean_t ismc,boolean_t sync)91620c794b3Sgavinm cmi_mc_logout(cmi_hdl_t hdl, boolean_t ismc, boolean_t sync)
91720c794b3Sgavinm {
91820c794b3Sgavinm const struct cmi_mc_ops *mcops;
91920c794b3Sgavinm
920*a3114836SGerry Liu if (cmi_no_mca_init)
92120c794b3Sgavinm return;
92220c794b3Sgavinm
923*a3114836SGerry Liu if (cmi_mc_global_ops != NULL)
924*a3114836SGerry Liu mcops = cmi_mc_global_ops;
925*a3114836SGerry Liu else
926*a3114836SGerry Liu mcops = cmi_hdl_getmcops(hdl);
927*a3114836SGerry Liu
928*a3114836SGerry Liu if (mcops != NULL && mcops->cmi_mc_logout != NULL)
92920c794b3Sgavinm mcops->cmi_mc_logout(hdl, ismc, sync);
93020c794b3Sgavinm }
93120c794b3Sgavinm
93220c794b3Sgavinm cmi_errno_t
cmi_hdl_msrinject(cmi_hdl_t hdl,cmi_mca_regs_t * regs,uint_t nregs,int force)93320c794b3Sgavinm cmi_hdl_msrinject(cmi_hdl_t hdl, cmi_mca_regs_t *regs, uint_t nregs,
93420c794b3Sgavinm int force)
93520c794b3Sgavinm {
93620c794b3Sgavinm cmi_t *cmi = cmi_hdl_getcmi(hdl);
937e4b86885SCheng Sean Ye cmi_errno_t rc;
93820c794b3Sgavinm
93920c794b3Sgavinm if (!CMI_OP_PRESENT(cmi, cmi_msrinject))
94020c794b3Sgavinm return (CMIERR_NOTSUP);
94120c794b3Sgavinm
942e4b86885SCheng Sean Ye cmi_hdl_inj_begin(hdl);
943e4b86885SCheng Sean Ye rc = CMI_OPS(cmi)->cmi_msrinject(hdl, regs, nregs, force);
944e4b86885SCheng Sean Ye cmi_hdl_inj_end(hdl);
945e4b86885SCheng Sean Ye
946e4b86885SCheng Sean Ye return (rc);
94720c794b3Sgavinm }
94820c794b3Sgavinm
94920c794b3Sgavinm boolean_t
cmi_panic_on_ue(void)95020c794b3Sgavinm cmi_panic_on_ue(void)
95120c794b3Sgavinm {
95220c794b3Sgavinm return (cmi_panic_on_uncorrectable_error ? B_TRUE : B_FALSE);
9537aec1d6eScindi }
954e4b86885SCheng Sean Ye
955e4b86885SCheng Sean Ye void
cmi_panic_callback(void)956e4b86885SCheng Sean Ye cmi_panic_callback(void)
957e4b86885SCheng Sean Ye {
958e4b86885SCheng Sean Ye cmi_hdl_t hdl;
959e4b86885SCheng Sean Ye cmi_t *cmi;
960e4b86885SCheng Sean Ye
961e4b86885SCheng Sean Ye if (cmi_no_mca_init || (hdl = cmi_hdl_any()) == NULL)
962e4b86885SCheng Sean Ye return;
963e4b86885SCheng Sean Ye
964e4b86885SCheng Sean Ye cmi = cmi_hdl_getcmi(hdl);
965e4b86885SCheng Sean Ye if (CMI_OP_PRESENT(cmi, cmi_panic_callback))
966e4b86885SCheng Sean Ye CMI_OPS(cmi)->cmi_panic_callback();
967e4b86885SCheng Sean Ye
968e4b86885SCheng Sean Ye cmi_hdl_rele(hdl);
969e4b86885SCheng Sean Ye }
970