xref: /illumos-gate/usr/src/uts/i86pc/os/cmi.c (revision 4da99751)
13ad553a7Sgavinm /*
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 
227aec1d6eScindi /*
23a3114836SGerry Liu  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
247aec1d6eScindi  * Use is subject to license terms.
252a613b59SRobert Mustacchi  * Copyright (c) 2018, Joyent, Inc.
267aec1d6eScindi  */
277aec1d6eScindi 
287aec1d6eScindi /*
297aec1d6eScindi  * Public interface to routines implemented by CPU modules
307aec1d6eScindi  */
317aec1d6eScindi 
3220c794b3Sgavinm #include <sys/types.h>
3320c794b3Sgavinm #include <sys/atomic.h>
347aec1d6eScindi #include <sys/x86_archext.h>
357aec1d6eScindi #include <sys/cpu_module_impl.h>
3620c794b3Sgavinm #include <sys/cpu_module_ms.h>
377aec1d6eScindi #include <sys/fm/util.h>
387aec1d6eScindi #include <sys/reboot.h>
397aec1d6eScindi #include <sys/modctl.h>
407aec1d6eScindi #include <sys/param.h>
417aec1d6eScindi #include <sys/cmn_err.h>
427aec1d6eScindi #include <sys/systm.h>
4320c794b3Sgavinm #include <sys/fm/protocol.h>
4420c794b3Sgavinm #include <sys/pcb.h>
4520c794b3Sgavinm #include <sys/ontrap.h>
4620c794b3Sgavinm #include <sys/psw.h>
4720c794b3Sgavinm #include <sys/privregs.h>
48e4b86885SCheng Sean Ye #include <sys/machsystm.h>
497aec1d6eScindi 
5020c794b3Sgavinm /*
5120c794b3Sgavinm  * Set to force cmi_init to fail.
5220c794b3Sgavinm  */
5320c794b3Sgavinm int cmi_no_init = 0;
547aec1d6eScindi 
5520c794b3Sgavinm /*
5620c794b3Sgavinm  * Set to avoid MCA initialization.
5720c794b3Sgavinm  */
5820c794b3Sgavinm int cmi_no_mca_init = 0;
597aec1d6eScindi 
608a40a695Sgavinm /*
618a40a695Sgavinm  * If cleared for debugging we will not attempt to load a model-specific
628a40a695Sgavinm  * cpu module but will load the generic cpu module instead.
638a40a695Sgavinm  */
648a40a695Sgavinm int cmi_force_generic = 0;
658a40a695Sgavinm 
667aec1d6eScindi /*
677aec1d6eScindi  * If cleared for debugging, we will suppress panicking on fatal hardware
687aec1d6eScindi  * errors.  This should *only* be used for debugging; it use can and will
697aec1d6eScindi  * cause data corruption if actual hardware errors are detected by the system.
707aec1d6eScindi  */
717aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1;
727aec1d6eScindi 
7320c794b3Sgavinm /*
7420c794b3Sgavinm  * Subdirectory (relative to the module search path) in which we will
7520c794b3Sgavinm  * look for cpu modules.
7620c794b3Sgavinm  */
7720c794b3Sgavinm #define	CPUMOD_SUBDIR	"cpu"
7820c794b3Sgavinm 
7920c794b3Sgavinm /*
8020c794b3Sgavinm  * CPU modules have a filenames such as "cpu.AuthenticAMD.15" and
8120c794b3Sgavinm  * "cpu.generic" - the "cpu" prefix is specified by the following.
8220c794b3Sgavinm  */
8320c794b3Sgavinm #define	CPUMOD_PREFIX	"cpu"
8420c794b3Sgavinm 
8520c794b3Sgavinm /*
8620c794b3Sgavinm  * Structure used to keep track of cpu modules we have loaded and their ops
8720c794b3Sgavinm  */
8820c794b3Sgavinm typedef struct cmi {
8920c794b3Sgavinm 	struct cmi *cmi_next;
9020c794b3Sgavinm 	struct cmi *cmi_prev;
9120c794b3Sgavinm 	const cmi_ops_t *cmi_ops;
9220c794b3Sgavinm 	struct modctl *cmi_modp;
9320c794b3Sgavinm 	uint_t cmi_refcnt;
9420c794b3Sgavinm } cmi_t;
9520c794b3Sgavinm 
967aec1d6eScindi static cmi_t *cmi_list;
97a3114836SGerry Liu static const cmi_mc_ops_t *cmi_mc_global_ops;
98a3114836SGerry Liu static void *cmi_mc_global_data;
997aec1d6eScindi static kmutex_t cmi_load_lock;
1007aec1d6eScindi 
10120c794b3Sgavinm /*
10220c794b3Sgavinm  * Functions we need from cmi_hw.c that are not part of the cpu_module.h
10320c794b3Sgavinm  * interface.
10420c794b3Sgavinm  */
105e4b86885SCheng Sean Ye extern cmi_hdl_t cmi_hdl_create(enum cmi_hdl_class, uint_t, uint_t, uint_t);
106a3114836SGerry Liu extern void cmi_hdl_destroy(cmi_hdl_t ophdl);
10720c794b3Sgavinm extern void cmi_hdl_setcmi(cmi_hdl_t, void *, void *);
10820c794b3Sgavinm extern void *cmi_hdl_getcmi(cmi_hdl_t);
10920c794b3Sgavinm extern void cmi_hdl_setmc(cmi_hdl_t, const struct cmi_mc_ops *, void *);
110e4b86885SCheng Sean Ye extern void cmi_hdl_inj_begin(cmi_hdl_t);
111e4b86885SCheng Sean Ye extern void cmi_hdl_inj_end(cmi_hdl_t);
112074bb90dSTom Pothier extern void cmi_read_smbios(cmi_hdl_t);
11320c794b3Sgavinm 
11420c794b3Sgavinm #define	HDL2CMI(hdl)		cmi_hdl_getcmi(hdl)
11520c794b3Sgavinm 
11620c794b3Sgavinm #define	CMI_OPS(cmi)		(cmi)->cmi_ops
11720c794b3Sgavinm #define	CMI_OP_PRESENT(cmi, op)	((cmi) && CMI_OPS(cmi)->op != NULL)
11820c794b3Sgavinm 
11920c794b3Sgavinm #define	CMI_MATCH_VENDOR	0	/* Just match on vendor */
12020c794b3Sgavinm #define	CMI_MATCH_FAMILY	1	/* Match down to family */
12120c794b3Sgavinm #define	CMI_MATCH_MODEL		2	/* Match down to model */
12220c794b3Sgavinm #define	CMI_MATCH_STEPPING	3	/* Match down to stepping */
12320c794b3Sgavinm 
12420c794b3Sgavinm static void
cmi_link(cmi_t * cmi)12520c794b3Sgavinm cmi_link(cmi_t *cmi)
12620c794b3Sgavinm {
12720c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
12820c794b3Sgavinm 
12920c794b3Sgavinm 	cmi->cmi_prev = NULL;
13020c794b3Sgavinm 	cmi->cmi_next = cmi_list;
13120c794b3Sgavinm 	if (cmi_list != NULL)
13220c794b3Sgavinm 		cmi_list->cmi_prev = cmi;
13320c794b3Sgavinm 	cmi_list = cmi;
13420c794b3Sgavinm }
13520c794b3Sgavinm 
13620c794b3Sgavinm static void
cmi_unlink(cmi_t * cmi)13720c794b3Sgavinm cmi_unlink(cmi_t *cmi)
1387aec1d6eScindi {
13920c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
14020c794b3Sgavinm 	ASSERT(cmi->cmi_refcnt == 0);
14120c794b3Sgavinm 
14220c794b3Sgavinm 	if (cmi->cmi_prev != NULL)
14320c794b3Sgavinm 		cmi->cmi_prev = cmi->cmi_next;
14420c794b3Sgavinm 
14520c794b3Sgavinm 	if (cmi->cmi_next != NULL)
14620c794b3Sgavinm 		cmi->cmi_next->cmi_prev = cmi->cmi_prev;
14720c794b3Sgavinm 
14820c794b3Sgavinm 	if (cmi_list == cmi)
14920c794b3Sgavinm 		cmi_list = cmi->cmi_next;
15020c794b3Sgavinm }
15120c794b3Sgavinm 
15220c794b3Sgavinm /*
15320c794b3Sgavinm  * Hold the module in memory.  We call to CPU modules without using the
15420c794b3Sgavinm  * stubs mechanism, so these modules must be manually held in memory.
15520c794b3Sgavinm  * The mod_ref acts as if another loaded module has a dependency on us.
15620c794b3Sgavinm  */
15720c794b3Sgavinm static void
cmi_hold(cmi_t * cmi)15820c794b3Sgavinm cmi_hold(cmi_t *cmi)
15920c794b3Sgavinm {
16020c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
16120c794b3Sgavinm 
16220c794b3Sgavinm 	mutex_enter(&mod_lock);
16320c794b3Sgavinm 	cmi->cmi_modp->mod_ref++;
16420c794b3Sgavinm 	mutex_exit(&mod_lock);
16520c794b3Sgavinm 	cmi->cmi_refcnt++;
16620c794b3Sgavinm }
16720c794b3Sgavinm 
16820c794b3Sgavinm static void
cmi_rele(cmi_t * cmi)16920c794b3Sgavinm cmi_rele(cmi_t *cmi)
17020c794b3Sgavinm {
17120c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
17220c794b3Sgavinm 
17320c794b3Sgavinm 	mutex_enter(&mod_lock);
17420c794b3Sgavinm 	cmi->cmi_modp->mod_ref--;
17520c794b3Sgavinm 	mutex_exit(&mod_lock);
17620c794b3Sgavinm 
17720c794b3Sgavinm 	if (--cmi->cmi_refcnt == 0) {
17820c794b3Sgavinm 		cmi_unlink(cmi);
17920c794b3Sgavinm 		kmem_free(cmi, sizeof (cmi_t));
18020c794b3Sgavinm 	}
18120c794b3Sgavinm }
18220c794b3Sgavinm 
18320c794b3Sgavinm static cmi_ops_t *
cmi_getops(modctl_t * modp)18420c794b3Sgavinm cmi_getops(modctl_t *modp)
18520c794b3Sgavinm {
18620c794b3Sgavinm 	cmi_ops_t *ops;
18720c794b3Sgavinm 
18820c794b3Sgavinm 	if ((ops = (cmi_ops_t *)modlookup_by_modctl(modp, "_cmi_ops")) ==
18920c794b3Sgavinm 	    NULL) {
19020c794b3Sgavinm 		cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops "
19120c794b3Sgavinm 		    "found", modp->mod_modname);
19220c794b3Sgavinm 		return (NULL);
19320c794b3Sgavinm 	}
19420c794b3Sgavinm 
19520c794b3Sgavinm 	if (ops->cmi_init == NULL) {
19620c794b3Sgavinm 		cmn_err(CE_WARN, "cpu module '%s' is invalid: no cmi_init "
19720c794b3Sgavinm 		    "entry point", modp->mod_modname);
19820c794b3Sgavinm 		return (NULL);
19920c794b3Sgavinm 	}
20020c794b3Sgavinm 
20120c794b3Sgavinm 	return (ops);
2027aec1d6eScindi }
2037aec1d6eScindi 
2047aec1d6eScindi static cmi_t *
cmi_load_modctl(modctl_t * modp)2057aec1d6eScindi cmi_load_modctl(modctl_t *modp)
2067aec1d6eScindi {
20720c794b3Sgavinm 	cmi_ops_t *ops;
20820c794b3Sgavinm 	uintptr_t ver;
2097aec1d6eScindi 	cmi_t *cmi;
21020c794b3Sgavinm 	cmi_api_ver_t apiver;
2117aec1d6eScindi 
2127aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
2137aec1d6eScindi 
2147aec1d6eScindi 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
2157aec1d6eScindi 		if (cmi->cmi_modp == modp)
2167aec1d6eScindi 			return (cmi);
2177aec1d6eScindi 	}
2187aec1d6eScindi 
219*4da99751SToomas Soome 	if ((ver = modlookup_by_modctl(modp, "_cmi_api_version")) == 0) {
22020c794b3Sgavinm 		/*
22120c794b3Sgavinm 		 * Apparently a cpu module before versioning was introduced -
22220c794b3Sgavinm 		 * we call this version 0.
22320c794b3Sgavinm 		 */
22420c794b3Sgavinm 		apiver = CMI_API_VERSION_0;
22520c794b3Sgavinm 	} else {
22620c794b3Sgavinm 		apiver = *((cmi_api_ver_t *)ver);
22720c794b3Sgavinm 		if (!CMI_API_VERSION_CHKMAGIC(apiver)) {
22820c794b3Sgavinm 			cmn_err(CE_WARN, "cpu module '%s' is invalid: "
22920c794b3Sgavinm 			    "_cmi_api_version 0x%x has bad magic",
23020c794b3Sgavinm 			    modp->mod_modname, apiver);
23120c794b3Sgavinm 			return (NULL);
23220c794b3Sgavinm 		}
23320c794b3Sgavinm 	}
23420c794b3Sgavinm 
23520c794b3Sgavinm 	if (apiver != CMI_API_VERSION) {
23620c794b3Sgavinm 		cmn_err(CE_WARN, "cpu module '%s' has API version %d, "
23720c794b3Sgavinm 		    "kernel requires API version %d", modp->mod_modname,
23820c794b3Sgavinm 		    CMI_API_VERSION_TOPRINT(apiver),
23920c794b3Sgavinm 		    CMI_API_VERSION_TOPRINT(CMI_API_VERSION));
2407aec1d6eScindi 		return (NULL);
2417aec1d6eScindi 	}
2427aec1d6eScindi 
24320c794b3Sgavinm 	if ((ops = cmi_getops(modp)) == NULL)
24420c794b3Sgavinm 		return (NULL);
2457aec1d6eScindi 
24620c794b3Sgavinm 	cmi = kmem_zalloc(sizeof (*cmi), KM_SLEEP);
24720c794b3Sgavinm 	cmi->cmi_ops = ops;
2487aec1d6eScindi 	cmi->cmi_modp = modp;
2497aec1d6eScindi 
25020c794b3Sgavinm 	cmi_link(cmi);
2517aec1d6eScindi 
2527aec1d6eScindi 	return (cmi);
2537aec1d6eScindi }
2547aec1d6eScindi 
25520c794b3Sgavinm static int
cmi_cpu_match(cmi_hdl_t hdl1,cmi_hdl_t hdl2,int match)25620c794b3Sgavinm cmi_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match)
25720c794b3Sgavinm {
25820c794b3Sgavinm 	if (match >= CMI_MATCH_VENDOR &&
25920c794b3Sgavinm 	    cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2))
26020c794b3Sgavinm 		return (0);
26120c794b3Sgavinm 
26220c794b3Sgavinm 	if (match >= CMI_MATCH_FAMILY &&
26320c794b3Sgavinm 	    cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2))
26420c794b3Sgavinm 		return (0);
26520c794b3Sgavinm 
26620c794b3Sgavinm 	if (match >= CMI_MATCH_MODEL &&
26720c794b3Sgavinm 	    cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2))
26820c794b3Sgavinm 		return (0);
26920c794b3Sgavinm 
27020c794b3Sgavinm 	if (match >= CMI_MATCH_STEPPING &&
27120c794b3Sgavinm 	    cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2))
27220c794b3Sgavinm 		return (0);
27320c794b3Sgavinm 
27420c794b3Sgavinm 	return (1);
27520c794b3Sgavinm }
27620c794b3Sgavinm 
27720c794b3Sgavinm static int
cmi_search_list_cb(cmi_hdl_t whdl,void * arg1,void * arg2,void * arg3)27820c794b3Sgavinm cmi_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
27920c794b3Sgavinm {
28020c794b3Sgavinm 	cmi_hdl_t thdl = (cmi_hdl_t)arg1;
28120c794b3Sgavinm 	int match = *((int *)arg2);
28220c794b3Sgavinm 	cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3;
28320c794b3Sgavinm 
28420c794b3Sgavinm 	if (cmi_cpu_match(thdl, whdl, match)) {
28520c794b3Sgavinm 		cmi_hdl_hold(whdl);	/* short-term hold */
28620c794b3Sgavinm 		*rsltp = whdl;
28720c794b3Sgavinm 		return (CMI_HDL_WALK_DONE);
28820c794b3Sgavinm 	} else {
28920c794b3Sgavinm 		return (CMI_HDL_WALK_NEXT);
29020c794b3Sgavinm 	}
29120c794b3Sgavinm }
29220c794b3Sgavinm 
2937aec1d6eScindi static cmi_t *
cmi_search_list(cmi_hdl_t hdl,int match)29420c794b3Sgavinm cmi_search_list(cmi_hdl_t hdl, int match)
2957aec1d6eScindi {
29620c794b3Sgavinm 	cmi_hdl_t dhdl = NULL;
29720c794b3Sgavinm 	cmi_t *cmi = NULL;
2987aec1d6eScindi 
2997aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
3007aec1d6eScindi 
30120c794b3Sgavinm 	cmi_hdl_walk(cmi_search_list_cb, (void *)hdl, (void *)&match, &dhdl);
30220c794b3Sgavinm 	if (dhdl) {
30320c794b3Sgavinm 		cmi = HDL2CMI(dhdl);
30420c794b3Sgavinm 		cmi_hdl_rele(dhdl);	/* held in cmi_search_list_cb */
3057aec1d6eScindi 	}
3067aec1d6eScindi 
30720c794b3Sgavinm 	return (cmi);
30820c794b3Sgavinm }
30920c794b3Sgavinm 
31020c794b3Sgavinm static cmi_t *
cmi_load_module(cmi_hdl_t hdl,int match,int * chosenp)31120c794b3Sgavinm cmi_load_module(cmi_hdl_t hdl, int match, int *chosenp)
31220c794b3Sgavinm {
31320c794b3Sgavinm 	modctl_t *modp;
31420c794b3Sgavinm 	cmi_t *cmi;
31520c794b3Sgavinm 	int modid;
31620c794b3Sgavinm 	uint_t s[3];
31720c794b3Sgavinm 
31820c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
31920c794b3Sgavinm 	ASSERT(match == CMI_MATCH_STEPPING || match == CMI_MATCH_MODEL ||
32020c794b3Sgavinm 	    match == CMI_MATCH_FAMILY || match == CMI_MATCH_VENDOR);
3217aec1d6eScindi 
3227aec1d6eScindi 	/*
32320c794b3Sgavinm 	 * Have we already loaded a module for a cpu with the same
32420c794b3Sgavinm 	 * vendor/family/model/stepping?
3257aec1d6eScindi 	 */
32620c794b3Sgavinm 	if ((cmi = cmi_search_list(hdl, match)) != NULL) {
32720c794b3Sgavinm 		cmi_hold(cmi);
32820c794b3Sgavinm 		return (cmi);
32920c794b3Sgavinm 	}
3307aec1d6eScindi 
33120c794b3Sgavinm 	s[0] = cmi_hdl_family(hdl);
33220c794b3Sgavinm 	s[1] = cmi_hdl_model(hdl);
33320c794b3Sgavinm 	s[2] = cmi_hdl_stepping(hdl);
3347aec1d6eScindi 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
33520c794b3Sgavinm 	    cmi_hdl_vendorstr(hdl), ".", s, match, chosenp);
3367aec1d6eScindi 
3377aec1d6eScindi 	if (modid == -1)
3387aec1d6eScindi 		return (NULL);
3397aec1d6eScindi 
3407aec1d6eScindi 	modp = mod_hold_by_id(modid);
3417aec1d6eScindi 	cmi = cmi_load_modctl(modp);
34220c794b3Sgavinm 	if (cmi)
34320c794b3Sgavinm 		cmi_hold(cmi);
3447aec1d6eScindi 	mod_release_mod(modp);
3457aec1d6eScindi 
3467aec1d6eScindi 	return (cmi);
3477aec1d6eScindi }
3487aec1d6eScindi 
34920c794b3Sgavinm /*
35020c794b3Sgavinm  * Try to load a cpu module with specific support for this chip type.
35120c794b3Sgavinm  */
3527aec1d6eScindi static cmi_t *
cmi_load_specific(cmi_hdl_t hdl,void ** datap)35320c794b3Sgavinm cmi_load_specific(cmi_hdl_t hdl, void **datap)
35420c794b3Sgavinm {
35520c794b3Sgavinm 	cmi_t *cmi;
35620c794b3Sgavinm 	int err;
35720c794b3Sgavinm 	int i;
35820c794b3Sgavinm 
35920c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
36020c794b3Sgavinm 
36120c794b3Sgavinm 	for (i = CMI_MATCH_STEPPING; i >= CMI_MATCH_VENDOR; i--) {
36220c794b3Sgavinm 		int suffixlevel;
36320c794b3Sgavinm 
36420c794b3Sgavinm 		if ((cmi = cmi_load_module(hdl, i, &suffixlevel)) == NULL)
36520c794b3Sgavinm 			return (NULL);
36620c794b3Sgavinm 
36720c794b3Sgavinm 		/*
36820c794b3Sgavinm 		 * A module has loaded and has a _cmi_ops structure, and the
36920c794b3Sgavinm 		 * module has been held for this instance.  Call its cmi_init
37020c794b3Sgavinm 		 * entry point - we expect success (0) or ENOTSUP.
37120c794b3Sgavinm 		 */
37220c794b3Sgavinm 		if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) == 0) {
37320c794b3Sgavinm 			if (boothowto & RB_VERBOSE) {
37420c794b3Sgavinm 				printf("initialized cpu module '%s' on "
37520c794b3Sgavinm 				    "chip %d core %d strand %d\n",
37620c794b3Sgavinm 				    cmi->cmi_modp->mod_modname,
37720c794b3Sgavinm 				    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
37820c794b3Sgavinm 				    cmi_hdl_strandid(hdl));
37920c794b3Sgavinm 			}
38020c794b3Sgavinm 			return (cmi);
38120c794b3Sgavinm 		} else if (err != ENOTSUP) {
38220c794b3Sgavinm 			cmn_err(CE_WARN, "failed to init cpu module '%s' on "
38320c794b3Sgavinm 			    "chip %d core %d strand %d: err=%d\n",
38420c794b3Sgavinm 			    cmi->cmi_modp->mod_modname,
38520c794b3Sgavinm 			    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
38620c794b3Sgavinm 			    cmi_hdl_strandid(hdl), err);
38720c794b3Sgavinm 		}
38820c794b3Sgavinm 
38920c794b3Sgavinm 		/*
39020c794b3Sgavinm 		 * The module failed or declined to init, so release
39120c794b3Sgavinm 		 * it and update i to be equal to the number
39220c794b3Sgavinm 		 * of suffices actually used in the last module path.
39320c794b3Sgavinm 		 */
39420c794b3Sgavinm 		cmi_rele(cmi);
39520c794b3Sgavinm 		i = suffixlevel;
39620c794b3Sgavinm 	}
39720c794b3Sgavinm 
39820c794b3Sgavinm 	return (NULL);
39920c794b3Sgavinm }
40020c794b3Sgavinm 
40120c794b3Sgavinm /*
40220c794b3Sgavinm  * Load the generic IA32 MCA cpu module, which may still supplement
40320c794b3Sgavinm  * itself with model-specific support through cpu model-specific modules.
40420c794b3Sgavinm  */
40520c794b3Sgavinm static cmi_t *
cmi_load_generic(cmi_hdl_t hdl,void ** datap)40620c794b3Sgavinm cmi_load_generic(cmi_hdl_t hdl, void **datap)
4077aec1d6eScindi {
4087aec1d6eScindi 	modctl_t *modp;
4097aec1d6eScindi 	cmi_t *cmi;
4107aec1d6eScindi 	int modid;
41120c794b3Sgavinm 	int err;
41220c794b3Sgavinm 
41320c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
4147aec1d6eScindi 
4157aec1d6eScindi 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
4167aec1d6eScindi 		return (NULL);
4177aec1d6eScindi 
4187aec1d6eScindi 	modp = mod_hold_by_id(modid);
4197aec1d6eScindi 	cmi = cmi_load_modctl(modp);
42020c794b3Sgavinm 	if (cmi)
42120c794b3Sgavinm 		cmi_hold(cmi);
4227aec1d6eScindi 	mod_release_mod(modp);
4237aec1d6eScindi 
42420c794b3Sgavinm 	if (cmi == NULL)
42520c794b3Sgavinm 		return (NULL);
42620c794b3Sgavinm 
42720c794b3Sgavinm 	if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) != 0) {
42820c794b3Sgavinm 		if (err != ENOTSUP)
42920c794b3Sgavinm 			cmn_err(CE_WARN, CPUMOD_PREFIX ".generic failed to "
43020c794b3Sgavinm 			    "init: err=%d", err);
43120c794b3Sgavinm 		cmi_rele(cmi);
43220c794b3Sgavinm 		return (NULL);
43320c794b3Sgavinm 	}
43420c794b3Sgavinm 
4357aec1d6eScindi 	return (cmi);
4367aec1d6eScindi }
4377aec1d6eScindi 
43820c794b3Sgavinm cmi_hdl_t
cmi_init(enum cmi_hdl_class class,uint_t chipid,uint_t coreid,uint_t strandid)43920c794b3Sgavinm cmi_init(enum cmi_hdl_class class, uint_t chipid, uint_t coreid,
440e4b86885SCheng Sean Ye     uint_t strandid)
4417aec1d6eScindi {
44220c794b3Sgavinm 	cmi_t *cmi = NULL;
44320c794b3Sgavinm 	cmi_hdl_t hdl;
4447aec1d6eScindi 	void *data;
4457aec1d6eScindi 
44620c794b3Sgavinm 	if (cmi_no_init) {
44720c794b3Sgavinm 		cmi_no_mca_init = 1;
44820c794b3Sgavinm 		return (NULL);
44920c794b3Sgavinm 	}
45020c794b3Sgavinm 
4517aec1d6eScindi 	mutex_enter(&cmi_load_lock);
4527aec1d6eScindi 
453e4b86885SCheng Sean Ye 	if ((hdl = cmi_hdl_create(class, chipid, coreid, strandid)) == NULL) {
4547aec1d6eScindi 		mutex_exit(&cmi_load_lock);
45520c794b3Sgavinm 		cmn_err(CE_WARN, "There will be no MCA support on chip %d "
45620c794b3Sgavinm 		    "core %d strand %d (cmi_hdl_create returned NULL)\n",
45720c794b3Sgavinm 		    chipid, coreid, strandid);
45820c794b3Sgavinm 		return (NULL);
4597aec1d6eScindi 	}
4607aec1d6eScindi 
46120c794b3Sgavinm 	if (!cmi_force_generic)
46220c794b3Sgavinm 		cmi = cmi_load_specific(hdl, &data);
46320c794b3Sgavinm 
46420c794b3Sgavinm 	if (cmi == NULL && (cmi = cmi_load_generic(hdl, &data)) == NULL) {
46520c794b3Sgavinm 		cmn_err(CE_WARN, "There will be no MCA support on chip %d "
46620c794b3Sgavinm 		    "core %d strand %d\n", chipid, coreid, strandid);
46720c794b3Sgavinm 		cmi_hdl_rele(hdl);
4687aec1d6eScindi 		mutex_exit(&cmi_load_lock);
46920c794b3Sgavinm 		return (NULL);
4707aec1d6eScindi 	}
4717aec1d6eScindi 
47220c794b3Sgavinm 	cmi_hdl_setcmi(hdl, cmi, data);
4737aec1d6eScindi 
47420c794b3Sgavinm 	cms_init(hdl);
4757aec1d6eScindi 
476074bb90dSTom Pothier 	cmi_read_smbios(hdl);
477074bb90dSTom Pothier 
47820c794b3Sgavinm 	mutex_exit(&cmi_load_lock);
4797aec1d6eScindi 
48020c794b3Sgavinm 	return (hdl);
4817aec1d6eScindi }
4827aec1d6eScindi 
48320c794b3Sgavinm /*
484a3114836SGerry Liu  * cmi_fini is called on DR deconfigure of a cpu resource.
485a3114836SGerry Liu  * It should not be called at simple offline of a cpu.
48620c794b3Sgavinm  */
4877aec1d6eScindi void
cmi_fini(cmi_hdl_t hdl)48820c794b3Sgavinm cmi_fini(cmi_hdl_t hdl)
4897aec1d6eScindi {
49020c794b3Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
49120c794b3Sgavinm 
49220c794b3Sgavinm 	if (cms_present(hdl))
49320c794b3Sgavinm 		cms_fini(hdl);
49420c794b3Sgavinm 
49520c794b3Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_fini))
49620c794b3Sgavinm 		CMI_OPS(cmi)->cmi_fini(hdl);
49720c794b3Sgavinm 
498a3114836SGerry Liu 	cmi_hdl_destroy(hdl);
4997aec1d6eScindi }
5007aec1d6eScindi 
50120c794b3Sgavinm /*
502e4b86885SCheng Sean Ye  * cmi_post_startup is called from post_startup for the boot cpu only (no
503e4b86885SCheng Sean Ye  * other cpus are started yet).
50420c794b3Sgavinm  */
5057aec1d6eScindi void
cmi_post_startup(void)50620c794b3Sgavinm cmi_post_startup(void)
5077aec1d6eScindi {
50820c794b3Sgavinm 	cmi_hdl_t hdl;
50920c794b3Sgavinm 	cmi_t *cmi;
51020c794b3Sgavinm 
51120c794b3Sgavinm 	if (cmi_no_mca_init != 0 ||
51220c794b3Sgavinm 	    (hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
51320c794b3Sgavinm 		return;
51420c794b3Sgavinm 
51520c794b3Sgavinm 	cmi = HDL2CMI(hdl);
51620c794b3Sgavinm 
51720c794b3Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_post_startup))
51820c794b3Sgavinm 		CMI_OPS(cmi)->cmi_post_startup(hdl);
51920c794b3Sgavinm 
52020c794b3Sgavinm 	cmi_hdl_rele(hdl);
5217aec1d6eScindi }
5227aec1d6eScindi 
5238a40a695Sgavinm /*
5248a40a695Sgavinm  * Called just once from start_other_cpus when all processors are started.
5258a40a695Sgavinm  * This will not be called for each cpu, so the registered op must not
526e4b86885SCheng Sean Ye  * assume it is called as such.  We are not necessarily executing on
527e4b86885SCheng Sean Ye  * the boot cpu.
5288a40a695Sgavinm  */
5293ad553a7Sgavinm void
cmi_post_mpstartup(void)5303ad553a7Sgavinm cmi_post_mpstartup(void)
5313ad553a7Sgavinm {
53220c794b3Sgavinm 	cmi_hdl_t hdl;
53320c794b3Sgavinm 	cmi_t *cmi;
53420c794b3Sgavinm 
53520c794b3Sgavinm 	if (cmi_no_mca_init != 0 ||
53620c794b3Sgavinm 	    (hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
53720c794b3Sgavinm 		return;
53820c794b3Sgavinm 
53920c794b3Sgavinm 	cmi = HDL2CMI(hdl);
54020c794b3Sgavinm 
54120c794b3Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_post_mpstartup))
54220c794b3Sgavinm 		CMI_OPS(cmi)->cmi_post_mpstartup(hdl);
54320c794b3Sgavinm 
54420c794b3Sgavinm 	cmi_hdl_rele(hdl);
54520c794b3Sgavinm }
54620c794b3Sgavinm 
54720c794b3Sgavinm void
cmi_faulted_enter(cmi_hdl_t hdl)54820c794b3Sgavinm cmi_faulted_enter(cmi_hdl_t hdl)
54920c794b3Sgavinm {
55020c794b3Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
55120c794b3Sgavinm 
55220c794b3Sgavinm 	if (cmi_no_mca_init != 0)
55320c794b3Sgavinm 		return;
55420c794b3Sgavinm 
55520c794b3Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_faulted_enter))
55620c794b3Sgavinm 		CMI_OPS(cmi)->cmi_faulted_enter(hdl);
5573ad553a7Sgavinm }
5583ad553a7Sgavinm 
5597aec1d6eScindi void
cmi_faulted_exit(cmi_hdl_t hdl)56020c794b3Sgavinm cmi_faulted_exit(cmi_hdl_t hdl)
5617aec1d6eScindi {
56220c794b3Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
56320c794b3Sgavinm 
56420c794b3Sgavinm 	if (cmi_no_mca_init != 0)
56520c794b3Sgavinm 		return;
56620c794b3Sgavinm 
56720c794b3Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_faulted_exit))
56820c794b3Sgavinm 		CMI_OPS(cmi)->cmi_faulted_exit(hdl);
5697aec1d6eScindi }
5707aec1d6eScindi 
5717aec1d6eScindi void
cmi_mca_init(cmi_hdl_t hdl)57220c794b3Sgavinm cmi_mca_init(cmi_hdl_t hdl)
5737aec1d6eScindi {
57420c794b3Sgavinm 	cmi_t *cmi;
57520c794b3Sgavinm 
57620c794b3Sgavinm 	if (cmi_no_mca_init != 0)
57720c794b3Sgavinm 		return;
57820c794b3Sgavinm 
57920c794b3Sgavinm 	cmi = HDL2CMI(hdl);
58020c794b3Sgavinm 
58120c794b3Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_mca_init))
58220c794b3Sgavinm 		CMI_OPS(cmi)->cmi_mca_init(hdl);
5837aec1d6eScindi }
5847aec1d6eScindi 
58520c794b3Sgavinm #define	CMI_RESPONSE_PANIC		0x0	/* panic must have value 0 */
58620c794b3Sgavinm #define	CMI_RESPONSE_NONE		0x1
58720c794b3Sgavinm #define	CMI_RESPONSE_CKILL		0x2
58820c794b3Sgavinm #define	CMI_RESPONSE_REBOOT		0x3	/* not implemented */
58920c794b3Sgavinm #define	CMI_RESPONSE_ONTRAP_PROT	0x4
59020c794b3Sgavinm #define	CMI_RESPONSE_LOFAULT_PROT	0x5
59120c794b3Sgavinm 
59220c794b3Sgavinm /*
59320c794b3Sgavinm  * Return 0 if we will panic in response to this machine check, otherwise
59420c794b3Sgavinm  * non-zero.  If the caller is cmi_mca_trap in this file then the nonzero
59520c794b3Sgavinm  * return values are to be interpreted from CMI_RESPONSE_* above.
59620c794b3Sgavinm  *
59720c794b3Sgavinm  * This function must just return what will be done without actually
59820c794b3Sgavinm  * doing anything; this includes not changing the regs.
59920c794b3Sgavinm  */
6007aec1d6eScindi int
cmi_mce_response(struct regs * rp,uint64_t disp)60120c794b3Sgavinm cmi_mce_response(struct regs *rp, uint64_t disp)
6027aec1d6eScindi {
60320c794b3Sgavinm 	int panicrsp = cmi_panic_on_uncorrectable_error ? CMI_RESPONSE_PANIC :
60420c794b3Sgavinm 	    CMI_RESPONSE_NONE;
60520c794b3Sgavinm 	on_trap_data_t *otp;
60620c794b3Sgavinm 
60720c794b3Sgavinm 	ASSERT(rp != NULL);	/* don't call for polling, only on #MC */
60820c794b3Sgavinm 
60920c794b3Sgavinm 	/*
61020c794b3Sgavinm 	 * If no bits are set in the disposition then there is nothing to
61120c794b3Sgavinm 	 * worry about and we do not need to trampoline to ontrap or
61220c794b3Sgavinm 	 * lofault handlers.
61320c794b3Sgavinm 	 */
61420c794b3Sgavinm 	if (disp == 0)
61520c794b3Sgavinm 		return (CMI_RESPONSE_NONE);
61620c794b3Sgavinm 
61720c794b3Sgavinm 	/*
61820c794b3Sgavinm 	 * Unconstrained errors cannot be forgiven, even by ontrap or
61920c794b3Sgavinm 	 * lofault protection.  The data is not poisoned and may not
62020c794b3Sgavinm 	 * even belong to the trapped context - eg a writeback of
62120c794b3Sgavinm 	 * data that is found to be bad.
62220c794b3Sgavinm 	 */
62320c794b3Sgavinm 	if (disp & CMI_ERRDISP_UC_UNCONSTRAINED)
62420c794b3Sgavinm 		return (panicrsp);
62520c794b3Sgavinm 
62620c794b3Sgavinm 	/*
62720c794b3Sgavinm 	 * ontrap OT_DATA_EC and lofault protection forgive any disposition
62820c794b3Sgavinm 	 * other than unconstrained, even those normally forced fatal.
62920c794b3Sgavinm 	 */
63020c794b3Sgavinm 	if ((otp = curthread->t_ontrap) != NULL && otp->ot_prot & OT_DATA_EC)
63120c794b3Sgavinm 		return (CMI_RESPONSE_ONTRAP_PROT);
63220c794b3Sgavinm 	else if (curthread->t_lofault)
63320c794b3Sgavinm 		return (CMI_RESPONSE_LOFAULT_PROT);
63420c794b3Sgavinm 
63520c794b3Sgavinm 	/*
63620c794b3Sgavinm 	 * Forced-fatal errors are terminal even in user mode.
63720c794b3Sgavinm 	 */
63820c794b3Sgavinm 	if (disp & CMI_ERRDISP_FORCEFATAL)
63920c794b3Sgavinm 		return (panicrsp);
64020c794b3Sgavinm 
64120c794b3Sgavinm 	/*
64220c794b3Sgavinm 	 * If the trapped context is corrupt or we have no instruction pointer
64320c794b3Sgavinm 	 * to resume at (and aren't trampolining to a fault handler)
64420c794b3Sgavinm 	 * then in the kernel case we must panic and in usermode we
64520c794b3Sgavinm 	 * kill the affected contract.
64620c794b3Sgavinm 	 */
64720c794b3Sgavinm 	if (disp & (CMI_ERRDISP_CURCTXBAD | CMI_ERRDISP_RIPV_INVALID))
64820c794b3Sgavinm 		return (USERMODE(rp->r_cs) ?  CMI_RESPONSE_CKILL : panicrsp);
64920c794b3Sgavinm 
65020c794b3Sgavinm 	/*
65120c794b3Sgavinm 	 * Anything else is harmless
65220c794b3Sgavinm 	 */
65320c794b3Sgavinm 	return (CMI_RESPONSE_NONE);
6547aec1d6eScindi }
6557aec1d6eScindi 
65620c794b3Sgavinm int cma_mca_trap_panic_suppressed = 0;
65720c794b3Sgavinm 
65820c794b3Sgavinm static void
cmi_mca_panic(void)65920c794b3Sgavinm cmi_mca_panic(void)
6607aec1d6eScindi {
66120c794b3Sgavinm 	if (cmi_panic_on_uncorrectable_error) {
66220c794b3Sgavinm 		fm_panic("Unrecoverable Machine-Check Exception");
66320c794b3Sgavinm 	} else {
66420c794b3Sgavinm 		cmn_err(CE_WARN, "suppressing panic from fatal #mc");
66520c794b3Sgavinm 		cma_mca_trap_panic_suppressed++;
66620c794b3Sgavinm 	}
6677aec1d6eScindi }
6687aec1d6eScindi 
66920c794b3Sgavinm 
67020c794b3Sgavinm int cma_mca_trap_contract_kills = 0;
67120c794b3Sgavinm int cma_mca_trap_ontrap_forgiven = 0;
67220c794b3Sgavinm int cma_mca_trap_lofault_forgiven = 0;
67320c794b3Sgavinm 
67420c794b3Sgavinm /*
67520c794b3Sgavinm  * Native #MC handler - we branch to here from mcetrap
67620c794b3Sgavinm  */
67720c794b3Sgavinm /*ARGSUSED*/
6787aec1d6eScindi void
cmi_mca_trap(struct regs * rp)6797aec1d6eScindi cmi_mca_trap(struct regs *rp)
6807aec1d6eScindi {
68120c794b3Sgavinm #ifndef	__xpv
68220c794b3Sgavinm 	cmi_hdl_t hdl = NULL;
68320c794b3Sgavinm 	uint64_t disp;
68420c794b3Sgavinm 	cmi_t *cmi;
68520c794b3Sgavinm 	int s;
68620c794b3Sgavinm 
68720c794b3Sgavinm 	if (cmi_no_mca_init != 0)
68820c794b3Sgavinm 		return;
68920c794b3Sgavinm 
69020c794b3Sgavinm 	/*
69120c794b3Sgavinm 	 * This function can call cmn_err, and the cpu module cmi_mca_trap
69220c794b3Sgavinm 	 * entry point may also elect to call cmn_err (e.g., if it can't
69320c794b3Sgavinm 	 * log the error onto an errorq, say very early in boot).
69420c794b3Sgavinm 	 * We need to let cprintf know that we must not block.
69520c794b3Sgavinm 	 */
69620c794b3Sgavinm 	s = spl8();
69720c794b3Sgavinm 
69820c794b3Sgavinm 	if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU),
69920c794b3Sgavinm 	    cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL ||
70020c794b3Sgavinm 	    (cmi = HDL2CMI(hdl)) == NULL ||
70120c794b3Sgavinm 	    !CMI_OP_PRESENT(cmi, cmi_mca_trap)) {
70220c794b3Sgavinm 
70320c794b3Sgavinm 		cmn_err(CE_WARN, "#MC exception on cpuid %d: %s",
70420c794b3Sgavinm 		    CPU->cpu_id,
70520c794b3Sgavinm 		    hdl ? "handle lookup ok but no #MC handler found" :
70620c794b3Sgavinm 		    "handle lookup failed");
70720c794b3Sgavinm 
70820c794b3Sgavinm 		if (hdl != NULL)
70920c794b3Sgavinm 			cmi_hdl_rele(hdl);
71020c794b3Sgavinm 
71120c794b3Sgavinm 		splx(s);
71220c794b3Sgavinm 		return;
7137aec1d6eScindi 	}
7147aec1d6eScindi 
71520c794b3Sgavinm 	disp = CMI_OPS(cmi)->cmi_mca_trap(hdl, rp);
71620c794b3Sgavinm 
71720c794b3Sgavinm 	switch (cmi_mce_response(rp, disp)) {
71820c794b3Sgavinm 	default:
71920c794b3Sgavinm 		cmn_err(CE_WARN, "Invalid response from cmi_mce_response");
72020c794b3Sgavinm 		/*FALLTHRU*/
7217aec1d6eScindi 
72220c794b3Sgavinm 	case CMI_RESPONSE_PANIC:
72320c794b3Sgavinm 		cmi_mca_panic();
72420c794b3Sgavinm 		break;
7257aec1d6eScindi 
72620c794b3Sgavinm 	case CMI_RESPONSE_NONE:
72720c794b3Sgavinm 		break;
72820c794b3Sgavinm 
72920c794b3Sgavinm 	case CMI_RESPONSE_CKILL:
73020c794b3Sgavinm 		ttolwp(curthread)->lwp_pcb.pcb_flags |= ASYNC_HWERR;
73120c794b3Sgavinm 		aston(curthread);
73220c794b3Sgavinm 		cma_mca_trap_contract_kills++;
73320c794b3Sgavinm 		break;
73420c794b3Sgavinm 
73520c794b3Sgavinm 	case CMI_RESPONSE_ONTRAP_PROT: {
73620c794b3Sgavinm 		on_trap_data_t *otp = curthread->t_ontrap;
73720c794b3Sgavinm 		otp->ot_trap = OT_DATA_EC;
73820c794b3Sgavinm 		rp->r_pc = otp->ot_trampoline;
73920c794b3Sgavinm 		cma_mca_trap_ontrap_forgiven++;
74020c794b3Sgavinm 		break;
74120c794b3Sgavinm 	}
74220c794b3Sgavinm 
74320c794b3Sgavinm 	case CMI_RESPONSE_LOFAULT_PROT:
74420c794b3Sgavinm 		rp->r_r0 = EFAULT;
74520c794b3Sgavinm 		rp->r_pc = curthread->t_lofault;
74620c794b3Sgavinm 		cma_mca_trap_lofault_forgiven++;
74720c794b3Sgavinm 		break;
74820c794b3Sgavinm 	}
74920c794b3Sgavinm 
75020c794b3Sgavinm 	cmi_hdl_rele(hdl);
75120c794b3Sgavinm 	splx(s);
75220c794b3Sgavinm #endif	/* __xpv */
7537aec1d6eScindi }
7547aec1d6eScindi 
7557aec1d6eScindi void
cmi_hdl_poke(cmi_hdl_t hdl)75620c794b3Sgavinm cmi_hdl_poke(cmi_hdl_t hdl)
7577aec1d6eScindi {
75820c794b3Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
75920c794b3Sgavinm 
76020c794b3Sgavinm 	if (!CMI_OP_PRESENT(cmi, cmi_hdl_poke))
76120c794b3Sgavinm 		return;
76220c794b3Sgavinm 
76320c794b3Sgavinm 	CMI_OPS(cmi)->cmi_hdl_poke(hdl);
7647aec1d6eScindi }
7657aec1d6eScindi 
766e4b86885SCheng Sean Ye #ifndef	__xpv
767e3d60c9bSAdrian Frost void
cmi_cmci_trap()768e3d60c9bSAdrian Frost cmi_cmci_trap()
769e3d60c9bSAdrian Frost {
770e3d60c9bSAdrian Frost 	cmi_hdl_t hdl = NULL;
771e3d60c9bSAdrian Frost 	cmi_t *cmi;
772e3d60c9bSAdrian Frost 
773e3d60c9bSAdrian Frost 	if (cmi_no_mca_init != 0)
774e3d60c9bSAdrian Frost 		return;
775e3d60c9bSAdrian Frost 
776e3d60c9bSAdrian Frost 	if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU),
777e3d60c9bSAdrian Frost 	    cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL ||
778e3d60c9bSAdrian Frost 	    (cmi = HDL2CMI(hdl)) == NULL ||
779e3d60c9bSAdrian Frost 	    !CMI_OP_PRESENT(cmi, cmi_cmci_trap)) {
780e3d60c9bSAdrian Frost 
781e3d60c9bSAdrian Frost 		cmn_err(CE_WARN, "CMCI interrupt on cpuid %d: %s",
782e3d60c9bSAdrian Frost 		    CPU->cpu_id,
783e3d60c9bSAdrian Frost 		    hdl ? "handle lookup ok but no CMCI handler found" :
784e3d60c9bSAdrian Frost 		    "handle lookup failed");
785e3d60c9bSAdrian Frost 
786e3d60c9bSAdrian Frost 		if (hdl != NULL)
787e3d60c9bSAdrian Frost 			cmi_hdl_rele(hdl);
788e3d60c9bSAdrian Frost 
789e3d60c9bSAdrian Frost 		return;
790e3d60c9bSAdrian Frost 	}
791e3d60c9bSAdrian Frost 
792e3d60c9bSAdrian Frost 	CMI_OPS(cmi)->cmi_cmci_trap(hdl);
793e3d60c9bSAdrian Frost 
794e3d60c9bSAdrian Frost 	cmi_hdl_rele(hdl);
795e3d60c9bSAdrian Frost }
796e4b86885SCheng Sean Ye #endif	/* __xpv */
797e3d60c9bSAdrian Frost 
7987aec1d6eScindi void
cmi_mc_register(cmi_hdl_t hdl,const cmi_mc_ops_t * mcops,void * mcdata)79920c794b3Sgavinm cmi_mc_register(cmi_hdl_t hdl, const cmi_mc_ops_t *mcops, void *mcdata)
8007aec1d6eScindi {
80120c794b3Sgavinm 	if (!cmi_no_mca_init)
80220c794b3Sgavinm 		cmi_hdl_setmc(hdl, mcops, mcdata);
8037aec1d6eScindi }
8047aec1d6eScindi 
805a3114836SGerry Liu cmi_errno_t
cmi_mc_register_global(const cmi_mc_ops_t * mcops,void * mcdata)806a3114836SGerry Liu cmi_mc_register_global(const cmi_mc_ops_t *mcops, void *mcdata)
807a3114836SGerry Liu {
808a3114836SGerry Liu 	if (!cmi_no_mca_init) {
809a3114836SGerry Liu 		if (cmi_mc_global_ops != NULL || cmi_mc_global_data != NULL ||
810a3114836SGerry Liu 		    mcops == NULL || mcops->cmi_mc_patounum == NULL ||
811a3114836SGerry Liu 		    mcops->cmi_mc_unumtopa == NULL) {
812a3114836SGerry Liu 			return (CMIERR_UNKNOWN);
813a3114836SGerry Liu 		}
814a3114836SGerry Liu 		cmi_mc_global_data = mcdata;
815a3114836SGerry Liu 		cmi_mc_global_ops = mcops;
816a3114836SGerry Liu 	}
817a3114836SGerry Liu 	return (CMI_SUCCESS);
818a3114836SGerry Liu }
819a3114836SGerry Liu 
820e4b86885SCheng Sean Ye void
cmi_mc_sw_memscrub_disable(void)821e4b86885SCheng Sean Ye cmi_mc_sw_memscrub_disable(void)
822e4b86885SCheng Sean Ye {
823e4b86885SCheng Sean Ye 	memscrub_disable();
824e4b86885SCheng Sean Ye }
825e4b86885SCheng Sean Ye 
82620c794b3Sgavinm 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)8274156fc34Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd,
8284156fc34Sgavinm     int syndtype, mc_unum_t *up)
8297aec1d6eScindi {
8307aec1d6eScindi 	const struct cmi_mc_ops *mcops;
83120c794b3Sgavinm 	cmi_hdl_t hdl;
83220c794b3Sgavinm 	cmi_errno_t rv;
83320c794b3Sgavinm 
834a3114836SGerry Liu 	if (cmi_no_mca_init)
835a3114836SGerry Liu 		return (CMIERR_MC_ABSENT);
836a3114836SGerry Liu 
837a3114836SGerry Liu 	if (cmi_mc_global_ops != NULL) {
838a3114836SGerry Liu 		if (cmi_mc_global_ops->cmi_mc_patounum == NULL)
839a3114836SGerry Liu 			return (CMIERR_MC_NOTSUP);
840a3114836SGerry Liu 		return (cmi_mc_global_ops->cmi_mc_patounum(cmi_mc_global_data,
841a3114836SGerry Liu 		    pa, valid_hi, valid_lo, synd, syndtype, up));
842a3114836SGerry Liu 	}
843a3114836SGerry Liu 
844a3114836SGerry Liu 	if ((hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
84520c794b3Sgavinm 		return (CMIERR_MC_ABSENT);
84620c794b3Sgavinm 
84720c794b3Sgavinm 	if ((mcops = cmi_hdl_getmcops(hdl)) == NULL ||
84820c794b3Sgavinm 	    mcops->cmi_mc_patounum == NULL) {
84920c794b3Sgavinm 		cmi_hdl_rele(hdl);
85020c794b3Sgavinm 		return (CMIERR_MC_NOTSUP);
85120c794b3Sgavinm 	}
8527aec1d6eScindi 
85320c794b3Sgavinm 	rv = mcops->cmi_mc_patounum(cmi_hdl_getmcdata(hdl), pa, valid_hi,
85420c794b3Sgavinm 	    valid_lo, synd, syndtype, up);
8557aec1d6eScindi 
85620c794b3Sgavinm 	cmi_hdl_rele(hdl);
85720c794b3Sgavinm 
85820c794b3Sgavinm 	return (rv);
8597aec1d6eScindi }
8607aec1d6eScindi 
86120c794b3Sgavinm cmi_errno_t
cmi_mc_unumtopa(mc_unum_t * up,nvlist_t * nvl,uint64_t * pap)8627aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
8637aec1d6eScindi {
8647aec1d6eScindi 	const struct cmi_mc_ops *mcops;
86520c794b3Sgavinm 	cmi_hdl_t hdl;
86620c794b3Sgavinm 	cmi_errno_t rv;
867e4b86885SCheng Sean Ye 	nvlist_t *hcsp;
8687aec1d6eScindi 
8697aec1d6eScindi 	if (up != NULL && nvl != NULL)
87020c794b3Sgavinm 		return (CMIERR_API);	/* convert from just one form */
87120c794b3Sgavinm 
872a3114836SGerry Liu 	if (cmi_no_mca_init)
873a3114836SGerry Liu 		return (CMIERR_MC_ABSENT);
874a3114836SGerry Liu 
875a3114836SGerry Liu 	if (cmi_mc_global_ops != NULL) {
876a3114836SGerry Liu 		if (cmi_mc_global_ops->cmi_mc_unumtopa == NULL)
877a3114836SGerry Liu 			return (CMIERR_MC_NOTSUP);
878a3114836SGerry Liu 		return (cmi_mc_global_ops->cmi_mc_unumtopa(cmi_mc_global_data,
879a3114836SGerry Liu 		    up, nvl, pap));
880a3114836SGerry Liu 	}
881a3114836SGerry Liu 
882a3114836SGerry Liu 	if ((hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
88320c794b3Sgavinm 		return (CMIERR_MC_ABSENT);
88420c794b3Sgavinm 
88520c794b3Sgavinm 	if ((mcops = cmi_hdl_getmcops(hdl)) == NULL ||
88620c794b3Sgavinm 	    mcops->cmi_mc_unumtopa == NULL) {
88720c794b3Sgavinm 		cmi_hdl_rele(hdl);
88820c794b3Sgavinm 
889e4b86885SCheng Sean Ye 		if (nvl != NULL && nvlist_lookup_nvlist(nvl,
890e4b86885SCheng Sean Ye 		    FM_FMRI_HC_SPECIFIC, &hcsp) == 0 &&
891e4b86885SCheng Sean Ye 		    (nvlist_lookup_uint64(hcsp,
892e4b86885SCheng Sean Ye 		    "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR, pap) == 0 ||
893e4b86885SCheng Sean Ye 		    nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR,
894e4b86885SCheng Sean Ye 		    pap) == 0)) {
89520c794b3Sgavinm 			return (CMIERR_MC_PARTIALUNUMTOPA);
89620c794b3Sgavinm 		} else {
897a3114836SGerry Liu 			return (mcops && mcops->cmi_mc_unumtopa == NULL ?
89820c794b3Sgavinm 			    CMIERR_MC_NOTSUP : CMIERR_MC_ABSENT);
89920c794b3Sgavinm 		}
90020c794b3Sgavinm 	}
9017aec1d6eScindi 
90220c794b3Sgavinm 	rv = mcops->cmi_mc_unumtopa(cmi_hdl_getmcdata(hdl), up, nvl, pap);
9037aec1d6eScindi 
90420c794b3Sgavinm 	cmi_hdl_rele(hdl);
90520c794b3Sgavinm 
90620c794b3Sgavinm 	return (rv);
90720c794b3Sgavinm }
90820c794b3Sgavinm 
90920c794b3Sgavinm void
cmi_mc_logout(cmi_hdl_t hdl,boolean_t ismc,boolean_t sync)91020c794b3Sgavinm cmi_mc_logout(cmi_hdl_t hdl, boolean_t ismc, boolean_t sync)
91120c794b3Sgavinm {
91220c794b3Sgavinm 	const struct cmi_mc_ops *mcops;
91320c794b3Sgavinm 
914a3114836SGerry Liu 	if (cmi_no_mca_init)
91520c794b3Sgavinm 		return;
91620c794b3Sgavinm 
917a3114836SGerry Liu 	if (cmi_mc_global_ops != NULL)
918a3114836SGerry Liu 		mcops = cmi_mc_global_ops;
919a3114836SGerry Liu 	else
920a3114836SGerry Liu 		mcops = cmi_hdl_getmcops(hdl);
921a3114836SGerry Liu 
922a3114836SGerry Liu 	if (mcops != NULL && mcops->cmi_mc_logout != NULL)
92320c794b3Sgavinm 		mcops->cmi_mc_logout(hdl, ismc, sync);
92420c794b3Sgavinm }
92520c794b3Sgavinm 
92620c794b3Sgavinm cmi_errno_t
cmi_hdl_msrinject(cmi_hdl_t hdl,cmi_mca_regs_t * regs,uint_t nregs,int force)92720c794b3Sgavinm cmi_hdl_msrinject(cmi_hdl_t hdl, cmi_mca_regs_t *regs, uint_t nregs,
92820c794b3Sgavinm     int force)
92920c794b3Sgavinm {
93020c794b3Sgavinm 	cmi_t *cmi = cmi_hdl_getcmi(hdl);
931e4b86885SCheng Sean Ye 	cmi_errno_t rc;
93220c794b3Sgavinm 
93320c794b3Sgavinm 	if (!CMI_OP_PRESENT(cmi, cmi_msrinject))
93420c794b3Sgavinm 		return (CMIERR_NOTSUP);
93520c794b3Sgavinm 
936e4b86885SCheng Sean Ye 	cmi_hdl_inj_begin(hdl);
937e4b86885SCheng Sean Ye 	rc = CMI_OPS(cmi)->cmi_msrinject(hdl, regs, nregs, force);
938e4b86885SCheng Sean Ye 	cmi_hdl_inj_end(hdl);
939e4b86885SCheng Sean Ye 
940e4b86885SCheng Sean Ye 	return (rc);
94120c794b3Sgavinm }
94220c794b3Sgavinm 
94320c794b3Sgavinm boolean_t
cmi_panic_on_ue(void)94420c794b3Sgavinm cmi_panic_on_ue(void)
94520c794b3Sgavinm {
94620c794b3Sgavinm 	return (cmi_panic_on_uncorrectable_error ? B_TRUE : B_FALSE);
9477aec1d6eScindi }
948e4b86885SCheng Sean Ye 
949e4b86885SCheng Sean Ye void
cmi_panic_callback(void)950e4b86885SCheng Sean Ye cmi_panic_callback(void)
951e4b86885SCheng Sean Ye {
952e4b86885SCheng Sean Ye 	cmi_hdl_t hdl;
953e4b86885SCheng Sean Ye 	cmi_t *cmi;
954e4b86885SCheng Sean Ye 
955e4b86885SCheng Sean Ye 	if (cmi_no_mca_init || (hdl = cmi_hdl_any()) == NULL)
956e4b86885SCheng Sean Ye 		return;
957e4b86885SCheng Sean Ye 
958e4b86885SCheng Sean Ye 	cmi = cmi_hdl_getcmi(hdl);
959e4b86885SCheng Sean Ye 	if (CMI_OP_PRESENT(cmi, cmi_panic_callback))
960e4b86885SCheng Sean Ye 		CMI_OPS(cmi)->cmi_panic_callback();
961e4b86885SCheng Sean Ye 
962e4b86885SCheng Sean Ye 	cmi_hdl_rele(hdl);
963e4b86885SCheng Sean Ye }
9642a613b59SRobert Mustacchi 
9652a613b59SRobert Mustacchi 
9662a613b59SRobert Mustacchi const char *
cmi_hdl_chipident(cmi_hdl_t hdl)9672a613b59SRobert Mustacchi cmi_hdl_chipident(cmi_hdl_t hdl)
9682a613b59SRobert Mustacchi {
9692a613b59SRobert Mustacchi 	cmi_t *cmi = cmi_hdl_getcmi(hdl);
9702a613b59SRobert Mustacchi 
9712a613b59SRobert Mustacchi 	if (!CMI_OP_PRESENT(cmi, cmi_ident))
9722a613b59SRobert Mustacchi 		return (NULL);
9732a613b59SRobert Mustacchi 
9742a613b59SRobert Mustacchi 	return (CMI_OPS(cmi)->cmi_ident(hdl));
9752a613b59SRobert Mustacchi }
976