xref: /illumos-gate/usr/src/uts/i86pc/os/cms.c (revision 4da99751)
120c794b3Sgavinm /*
220c794b3Sgavinm  * CDDL HEADER START
320c794b3Sgavinm  *
420c794b3Sgavinm  * The contents of this file are subject to the terms of the
520c794b3Sgavinm  * Common Development and Distribution License (the "License").
620c794b3Sgavinm  * You may not use this file except in compliance with the License.
720c794b3Sgavinm  *
820c794b3Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
920c794b3Sgavinm  * or http://www.opensolaris.org/os/licensing.
1020c794b3Sgavinm  * See the License for the specific language governing permissions
1120c794b3Sgavinm  * and limitations under the License.
1220c794b3Sgavinm  *
1320c794b3Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
1420c794b3Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1520c794b3Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
1620c794b3Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
1720c794b3Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
1820c794b3Sgavinm  *
1920c794b3Sgavinm  * CDDL HEADER END
2020c794b3Sgavinm  */
2120c794b3Sgavinm 
2220c794b3Sgavinm /*
23c84b7bbeSAdrian Frost  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
2520c794b3Sgavinm  */
26a3114836SGerry Liu /*
27a3114836SGerry Liu  * Copyright (c) 2010, Intel Corporation.
28a3114836SGerry Liu  * All rights reserved.
29a3114836SGerry Liu  */
3020c794b3Sgavinm 
3115c07adcSJohn Levon /*
3215c07adcSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
3315c07adcSJohn Levon  */
3415c07adcSJohn Levon 
3520c794b3Sgavinm #include <sys/types.h>
3620c794b3Sgavinm #include <sys/cpu_module_ms_impl.h>
3720c794b3Sgavinm #include <sys/cpuvar.h>
3820c794b3Sgavinm #include <sys/ksynch.h>
3920c794b3Sgavinm #include <sys/modctl.h>
4020c794b3Sgavinm #include <sys/x86_archext.h>
4120c794b3Sgavinm #include <sys/systm.h>
4220c794b3Sgavinm #include <sys/cmn_err.h>
4320c794b3Sgavinm #include <sys/param.h>
4420c794b3Sgavinm #include <sys/reboot.h>
4520c794b3Sgavinm 
4620c794b3Sgavinm /*
4720c794b3Sgavinm  * Set to prevent model-specific support from initialising.
4820c794b3Sgavinm  */
4920c794b3Sgavinm int cms_no_model_specific = 0;
5020c794b3Sgavinm 
5120c794b3Sgavinm /*
5220c794b3Sgavinm  * Subdirectory (relative to the module search path) in which we will
5320c794b3Sgavinm  * look for model-specific modules.
5420c794b3Sgavinm  */
5520c794b3Sgavinm #define	CPUMOD_MS_SUBDIR	"cpu"
5620c794b3Sgavinm 
5720c794b3Sgavinm /*
5820c794b3Sgavinm  * Cpu model-specific modules have filenames beginning with the following.
5920c794b3Sgavinm  */
6020c794b3Sgavinm #define	CPUMOD_MS_PREFIX	"cpu_ms"
6120c794b3Sgavinm 
6220c794b3Sgavinm #define	HDL2CMS(hdl)		cms_hdl_getcms(hdl)
6320c794b3Sgavinm 
6420c794b3Sgavinm #define	CMS_OPS(cms)		(cms)->cms_ops
6520c794b3Sgavinm #define	CMS_OP_PRESENT(cms, op)	((cms) && CMS_OPS(cms)->op != NULL)
6620c794b3Sgavinm 
6720c794b3Sgavinm struct cms_cpuid {
6820c794b3Sgavinm 	const char *vendor;
6920c794b3Sgavinm 	uint_t family;
7020c794b3Sgavinm 	uint_t model;
7120c794b3Sgavinm 	uint_t stepping;
7220c794b3Sgavinm };
7320c794b3Sgavinm 
7420c794b3Sgavinm #define	CMS_MATCH_VENDOR	0	/* Just match on vendor */
7520c794b3Sgavinm #define	CMS_MATCH_FAMILY	1	/* Match down to family */
7620c794b3Sgavinm #define	CMS_MATCH_MODEL		2	/* Match down to model */
7720c794b3Sgavinm #define	CMS_MATCH_STEPPING	3	/* Match down to stepping */
7820c794b3Sgavinm 
7920c794b3Sgavinm /*
8020c794b3Sgavinm  * Structure used to keep track of modules we have loaded.
8120c794b3Sgavinm  */
8220c794b3Sgavinm typedef struct cms {
8320c794b3Sgavinm 	struct cms *cms_next;
8420c794b3Sgavinm 	struct cms *cms_prev;
8520c794b3Sgavinm 	const cms_ops_t *cms_ops;
8620c794b3Sgavinm 	struct modctl *cms_modp;
8720c794b3Sgavinm 	uint_t cms_refcnt;
8820c794b3Sgavinm } cms_t;
8920c794b3Sgavinm 
9020c794b3Sgavinm static cms_t *cms_list;
9120c794b3Sgavinm static kmutex_t cms_load_lock;
9220c794b3Sgavinm 
9320c794b3Sgavinm /*
9420c794b3Sgavinm  * We stash a cms_t and associated private data via cmi_hdl_setspecific.
9520c794b3Sgavinm  */
9620c794b3Sgavinm struct cms_ctl {
9720c794b3Sgavinm 	cms_t *cs_cms;
9820c794b3Sgavinm 	void *cs_cmsdata;
9920c794b3Sgavinm };
10020c794b3Sgavinm 
10120c794b3Sgavinm static cms_t *
cms_hdl_getcms(cmi_hdl_t hdl)10220c794b3Sgavinm cms_hdl_getcms(cmi_hdl_t hdl)
10320c794b3Sgavinm {
10420c794b3Sgavinm 	struct cms_ctl *cdp = cmi_hdl_getspecific(hdl);
10520c794b3Sgavinm 
10620c794b3Sgavinm 	return (cdp != NULL ? cdp->cs_cms : NULL);
10720c794b3Sgavinm }
10820c794b3Sgavinm 
10920c794b3Sgavinm void *
cms_hdl_getcmsdata(cmi_hdl_t hdl)11020c794b3Sgavinm cms_hdl_getcmsdata(cmi_hdl_t hdl)
11120c794b3Sgavinm {
11220c794b3Sgavinm 	struct cms_ctl *cdp = cmi_hdl_getspecific(hdl);
11320c794b3Sgavinm 
11420c794b3Sgavinm 	return (cdp != NULL ? cdp->cs_cmsdata : NULL);
11520c794b3Sgavinm }
11620c794b3Sgavinm 
11720c794b3Sgavinm static void
cms_link(cms_t * cms)11820c794b3Sgavinm cms_link(cms_t *cms)
11920c794b3Sgavinm {
12020c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
12120c794b3Sgavinm 
12220c794b3Sgavinm 	cms->cms_prev = NULL;
12320c794b3Sgavinm 	cms->cms_next = cms_list;
12420c794b3Sgavinm 	if (cms_list != NULL)
12520c794b3Sgavinm 		cms_list->cms_prev = cms;
12620c794b3Sgavinm 	cms_list = cms;
12720c794b3Sgavinm }
12820c794b3Sgavinm 
12920c794b3Sgavinm static void
cms_unlink(cms_t * cms)13020c794b3Sgavinm cms_unlink(cms_t *cms)
13120c794b3Sgavinm {
13220c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
13320c794b3Sgavinm 	ASSERT(cms->cms_refcnt == 0);
13420c794b3Sgavinm 
13520c794b3Sgavinm 	if (cms->cms_prev != NULL)
13620c794b3Sgavinm 		cms->cms_prev->cms_next = cms->cms_next;
13720c794b3Sgavinm 
13820c794b3Sgavinm 	if (cms->cms_next != NULL)
13920c794b3Sgavinm 		cms->cms_next->cms_prev = cms->cms_prev;
14020c794b3Sgavinm 
14120c794b3Sgavinm 	if (cms_list == cms)
14220c794b3Sgavinm 		cms_list = cms->cms_next;
14320c794b3Sgavinm }
14420c794b3Sgavinm 
14520c794b3Sgavinm /*
14620c794b3Sgavinm  * Hold the module in memory.  We call to CPU modules without using the
14720c794b3Sgavinm  * stubs mechanism, so these modules must be manually held in memory.
14820c794b3Sgavinm  * The mod_ref acts as if another loaded module has a dependency on us.
14920c794b3Sgavinm  */
15020c794b3Sgavinm static void
cms_hold(cms_t * cms)15120c794b3Sgavinm cms_hold(cms_t *cms)
15220c794b3Sgavinm {
15320c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
15420c794b3Sgavinm 
15520c794b3Sgavinm 	mutex_enter(&mod_lock);
15620c794b3Sgavinm 	cms->cms_modp->mod_ref++;
15720c794b3Sgavinm 	mutex_exit(&mod_lock);
15820c794b3Sgavinm 	cms->cms_refcnt++;
15920c794b3Sgavinm }
16020c794b3Sgavinm 
16120c794b3Sgavinm static void
cms_rele(cms_t * cms)16220c794b3Sgavinm cms_rele(cms_t *cms)
16320c794b3Sgavinm {
16420c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
16520c794b3Sgavinm 
16620c794b3Sgavinm 	mutex_enter(&mod_lock);
16720c794b3Sgavinm 	cms->cms_modp->mod_ref--;
16820c794b3Sgavinm 	mutex_exit(&mod_lock);
16920c794b3Sgavinm 
17020c794b3Sgavinm 	if (--cms->cms_refcnt == 0) {
17120c794b3Sgavinm 		cms_unlink(cms);
17220c794b3Sgavinm 		kmem_free(cms, sizeof (cms_t));
17320c794b3Sgavinm 	}
17420c794b3Sgavinm }
17520c794b3Sgavinm 
17620c794b3Sgavinm static cms_ops_t *
cms_getops(modctl_t * modp)17720c794b3Sgavinm cms_getops(modctl_t *modp)
17820c794b3Sgavinm {
17920c794b3Sgavinm 	cms_ops_t *ops;
18020c794b3Sgavinm 
18120c794b3Sgavinm 	if ((ops = (cms_ops_t *)modlookup_by_modctl(modp, "_cms_ops")) ==
18220c794b3Sgavinm 	    NULL) {
18320c794b3Sgavinm 		cmn_err(CE_WARN, "cpu_ms module '%s' is invalid: no _cms_ops "
18420c794b3Sgavinm 		    "found", modp->mod_modname);
18520c794b3Sgavinm 		return (NULL);
18620c794b3Sgavinm 	}
18720c794b3Sgavinm 
18820c794b3Sgavinm 	if (ops->cms_init == NULL) {
18920c794b3Sgavinm 		cmn_err(CE_WARN, "cpu_ms module '%s' is invalid: no cms_init "
19020c794b3Sgavinm 		    "entry point", modp->mod_modname);
19120c794b3Sgavinm 		return (NULL);
19220c794b3Sgavinm 	}
19320c794b3Sgavinm 
19420c794b3Sgavinm 	return (ops);
19520c794b3Sgavinm }
19620c794b3Sgavinm 
19720c794b3Sgavinm static cms_t *
cms_load_modctl(modctl_t * modp)19820c794b3Sgavinm cms_load_modctl(modctl_t *modp)
19920c794b3Sgavinm {
20020c794b3Sgavinm 	cms_ops_t *ops;
20120c794b3Sgavinm 	uintptr_t ver;
20220c794b3Sgavinm 	cms_t *cms;
20320c794b3Sgavinm 	cms_api_ver_t apiver;
20420c794b3Sgavinm 
20520c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
20620c794b3Sgavinm 
20720c794b3Sgavinm 	for (cms = cms_list; cms != NULL; cms = cms->cms_next) {
20820c794b3Sgavinm 		if (cms->cms_modp == modp)
20920c794b3Sgavinm 			return (cms);
21020c794b3Sgavinm 	}
21120c794b3Sgavinm 
212*4da99751SToomas Soome 	if ((ver = modlookup_by_modctl(modp, "_cms_api_version")) == 0) {
21320c794b3Sgavinm 		cmn_err(CE_WARN, "cpu model-specific module '%s' is invalid:  "
21420c794b3Sgavinm 		    "no _cms_api_version", modp->mod_modname);
21520c794b3Sgavinm 		return (NULL);
21620c794b3Sgavinm 	} else {
21720c794b3Sgavinm 		apiver = *((cms_api_ver_t *)ver);
21820c794b3Sgavinm 		if (!CMS_API_VERSION_CHKMAGIC(apiver)) {
21920c794b3Sgavinm 			cmn_err(CE_WARN, "cpu model-specific module '%s' is "
22020c794b3Sgavinm 			    "invalid: _cms_api_version 0x%x has bad magic",
22120c794b3Sgavinm 			    modp->mod_modname, apiver);
22220c794b3Sgavinm 			return (NULL);
22320c794b3Sgavinm 		}
22420c794b3Sgavinm 	}
22520c794b3Sgavinm 
22620c794b3Sgavinm 	if (apiver != CMS_API_VERSION) {
22720c794b3Sgavinm 		cmn_err(CE_WARN, "cpu model-specific module '%s' has API "
22820c794b3Sgavinm 		    "version %d, kernel requires API version %d",
22920c794b3Sgavinm 		    modp->mod_modname, CMS_API_VERSION_TOPRINT(apiver),
23020c794b3Sgavinm 		    CMS_API_VERSION_TOPRINT(CMS_API_VERSION));
23115c07adcSJohn Levon 		return (NULL);
23220c794b3Sgavinm 	}
23320c794b3Sgavinm 
23420c794b3Sgavinm 	if ((ops = cms_getops(modp)) == NULL)
23520c794b3Sgavinm 		return (NULL);
23620c794b3Sgavinm 
23720c794b3Sgavinm 	cms = kmem_zalloc(sizeof (cms_t), KM_SLEEP);
23820c794b3Sgavinm 	cms->cms_ops = ops;
23920c794b3Sgavinm 	cms->cms_modp = modp;
24020c794b3Sgavinm 
24120c794b3Sgavinm 	cms_link(cms);
24220c794b3Sgavinm 
24320c794b3Sgavinm 	return (cms);
24420c794b3Sgavinm }
24520c794b3Sgavinm 
24620c794b3Sgavinm static int
cms_cpu_match(cmi_hdl_t hdl1,cmi_hdl_t hdl2,int match)24720c794b3Sgavinm cms_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match)
24820c794b3Sgavinm {
24920c794b3Sgavinm 	if (match >= CMS_MATCH_VENDOR &&
25020c794b3Sgavinm 	    cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2))
25120c794b3Sgavinm 		return (0);
25220c794b3Sgavinm 
25320c794b3Sgavinm 	if (match >= CMS_MATCH_FAMILY &&
25420c794b3Sgavinm 	    cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2))
25520c794b3Sgavinm 		return (0);
25620c794b3Sgavinm 
25720c794b3Sgavinm 	if (match >= CMS_MATCH_MODEL &&
25820c794b3Sgavinm 	    cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2))
25920c794b3Sgavinm 		return (0);
26020c794b3Sgavinm 
26120c794b3Sgavinm 	if (match >= CMS_MATCH_STEPPING &&
26220c794b3Sgavinm 	    cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2))
26320c794b3Sgavinm 		return (0);
26420c794b3Sgavinm 
26520c794b3Sgavinm 	return (1);
26620c794b3Sgavinm }
26720c794b3Sgavinm 
26820c794b3Sgavinm static int
cms_search_list_cb(cmi_hdl_t whdl,void * arg1,void * arg2,void * arg3)26920c794b3Sgavinm cms_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
27020c794b3Sgavinm {
27120c794b3Sgavinm 	cmi_hdl_t thdl = (cmi_hdl_t)arg1;
27220c794b3Sgavinm 	int match = *((int *)arg2);
27320c794b3Sgavinm 	cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3;
27420c794b3Sgavinm 
27520c794b3Sgavinm 	if (cms_cpu_match(thdl, whdl, match)) {
27620c794b3Sgavinm 		cmi_hdl_hold(whdl);	/* short-term hold */
27720c794b3Sgavinm 		*rsltp = whdl;
27820c794b3Sgavinm 		return (CMI_HDL_WALK_DONE);
27920c794b3Sgavinm 	} else {
28020c794b3Sgavinm 		return (CMI_HDL_WALK_NEXT);
28120c794b3Sgavinm 	}
28220c794b3Sgavinm }
28320c794b3Sgavinm 
28420c794b3Sgavinm /*
28520c794b3Sgavinm  * Look to see if we've already got a module loaded for a CPU just
28620c794b3Sgavinm  * like this one.  If we do, then we'll re-use it.
28720c794b3Sgavinm  */
28820c794b3Sgavinm static cms_t *
cms_search_list(cmi_hdl_t hdl,int match)28920c794b3Sgavinm cms_search_list(cmi_hdl_t hdl, int match)
29020c794b3Sgavinm {
29120c794b3Sgavinm 	cmi_hdl_t dhdl = NULL;
29220c794b3Sgavinm 	cms_t *cms = NULL;
29320c794b3Sgavinm 
29420c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
29520c794b3Sgavinm 
29620c794b3Sgavinm 	cmi_hdl_walk(cms_search_list_cb, (void *)hdl, (void *)&match, &dhdl);
29720c794b3Sgavinm 	if (dhdl) {
29820c794b3Sgavinm 		cms = HDL2CMS(dhdl);
29920c794b3Sgavinm 		cmi_hdl_rele(dhdl);	/* held in cms_search_list_cb */
30020c794b3Sgavinm 	}
30120c794b3Sgavinm 
30220c794b3Sgavinm 	return (cms);
30320c794b3Sgavinm }
30420c794b3Sgavinm 
30520c794b3Sgavinm /*
30620c794b3Sgavinm  * Try to find or load a module that offers model-specific support for
30720c794b3Sgavinm  * this vendor/family/model/stepping combination.  When attempting to load
30820c794b3Sgavinm  * a module we look in CPUMOD_MS_SUBDIR first for a match on
30920c794b3Sgavinm  * vendor/family/model/stepping, then on vendor/family/model (ignoring
31020c794b3Sgavinm  * stepping), then on vendor/family (ignoring model and stepping), then
31120c794b3Sgavinm  * on vendor alone.
31220c794b3Sgavinm  */
31320c794b3Sgavinm static cms_t *
cms_load_module(cmi_hdl_t hdl,int match,int * chosenp)31420c794b3Sgavinm cms_load_module(cmi_hdl_t hdl, int match, int *chosenp)
31520c794b3Sgavinm {
31620c794b3Sgavinm 	modctl_t *modp;
31720c794b3Sgavinm 	cms_t *cms;
31820c794b3Sgavinm 	int modid;
31920c794b3Sgavinm 	uint_t s[3];
32020c794b3Sgavinm 
32120c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
32220c794b3Sgavinm 	ASSERT(match == CMS_MATCH_STEPPING || match == CMS_MATCH_MODEL ||
32320c794b3Sgavinm 	    match == CMS_MATCH_FAMILY || match == CMS_MATCH_VENDOR);
32420c794b3Sgavinm 
32520c794b3Sgavinm 	s[0] = cmi_hdl_family(hdl);
32620c794b3Sgavinm 	s[1] = cmi_hdl_model(hdl);
32720c794b3Sgavinm 	s[2] = cmi_hdl_stepping(hdl);
32820c794b3Sgavinm 
32920c794b3Sgavinm 	/*
33020c794b3Sgavinm 	 * Have we already loaded a module for a cpu with the same
33120c794b3Sgavinm 	 * vendor/family/model/stepping?
33220c794b3Sgavinm 	 */
33320c794b3Sgavinm 	if ((cms = cms_search_list(hdl, match)) != NULL) {
33420c794b3Sgavinm 		cms_hold(cms);
33520c794b3Sgavinm 		return (cms);
33620c794b3Sgavinm 	}
33720c794b3Sgavinm 
33820c794b3Sgavinm 	modid = modload_qualified(CPUMOD_MS_SUBDIR, CPUMOD_MS_PREFIX,
33920c794b3Sgavinm 	    cmi_hdl_vendorstr(hdl), ".", s, match, chosenp);
34020c794b3Sgavinm 
34120c794b3Sgavinm 	if (modid == -1)
34220c794b3Sgavinm 		return (NULL);
34320c794b3Sgavinm 
34420c794b3Sgavinm 	modp = mod_hold_by_id(modid);
34520c794b3Sgavinm 	cms = cms_load_modctl(modp);
34620c794b3Sgavinm 	if (cms)
34720c794b3Sgavinm 		cms_hold(cms);
34820c794b3Sgavinm 	mod_release_mod(modp);
34920c794b3Sgavinm 
35020c794b3Sgavinm 	return (cms);
35120c794b3Sgavinm }
35220c794b3Sgavinm 
35320c794b3Sgavinm static cms_t *
cms_load_specific(cmi_hdl_t hdl,void ** datap)35420c794b3Sgavinm cms_load_specific(cmi_hdl_t hdl, void **datap)
35520c794b3Sgavinm {
35620c794b3Sgavinm 	cms_t *cms;
35720c794b3Sgavinm 	int err;
35820c794b3Sgavinm 	int i;
35920c794b3Sgavinm 
36020c794b3Sgavinm 	ASSERT(MUTEX_HELD(&cms_load_lock));
36120c794b3Sgavinm 
36220c794b3Sgavinm 	for (i = CMS_MATCH_STEPPING; i >= CMS_MATCH_VENDOR; i--) {
36320c794b3Sgavinm 		int suffixlevel;
36420c794b3Sgavinm 
36520c794b3Sgavinm 		if ((cms = cms_load_module(hdl, i, &suffixlevel)) == NULL)
36620c794b3Sgavinm 			return (NULL);
36720c794b3Sgavinm 
36820c794b3Sgavinm 		/*
36920c794b3Sgavinm 		 * A module has loaded and has a _cms_ops structure, and the
37020c794b3Sgavinm 		 * module has been held for this instance.  Call the cms_init
37120c794b3Sgavinm 		 * entry point - we expect success (0) or ENOTSUP.
37220c794b3Sgavinm 		 */
37320c794b3Sgavinm 		if ((err = cms->cms_ops->cms_init(hdl, datap)) == 0) {
37420c794b3Sgavinm 			if (boothowto & RB_VERBOSE) {
37520c794b3Sgavinm 				printf("initialized model-specific "
37620c794b3Sgavinm 				    "module '%s' on chip %d core %d "
37720c794b3Sgavinm 				    "strand %d\n",
37820c794b3Sgavinm 				    cms->cms_modp->mod_modname,
37920c794b3Sgavinm 				    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
38020c794b3Sgavinm 				    cmi_hdl_strandid(hdl));
38120c794b3Sgavinm 			}
38220c794b3Sgavinm 			return (cms);
38320c794b3Sgavinm 		} else if (err != ENOTSUP) {
38420c794b3Sgavinm 			cmn_err(CE_WARN, "failed to init model-specific "
38520c794b3Sgavinm 			    "module '%s' on chip %d core %d strand %d: err=%d",
38620c794b3Sgavinm 			    cms->cms_modp->mod_modname,
38720c794b3Sgavinm 			    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
38820c794b3Sgavinm 			    cmi_hdl_strandid(hdl), err);
38920c794b3Sgavinm 		}
39020c794b3Sgavinm 
39120c794b3Sgavinm 		/*
39220c794b3Sgavinm 		 * The module failed or declined to init, so release
39348bbca81SDaniel Hoffman 		 * it and potentially change i to be equal to the number
39420c794b3Sgavinm 		 * of suffices actually used in the last module path.
39520c794b3Sgavinm 		 */
39620c794b3Sgavinm 		cms_rele(cms);
39720c794b3Sgavinm 		i = suffixlevel;
39820c794b3Sgavinm 	}
39920c794b3Sgavinm 
40020c794b3Sgavinm 	return (NULL);
40120c794b3Sgavinm }
40220c794b3Sgavinm 
40320c794b3Sgavinm void
cms_init(cmi_hdl_t hdl)40420c794b3Sgavinm cms_init(cmi_hdl_t hdl)
40520c794b3Sgavinm {
40620c794b3Sgavinm 	cms_t *cms;
40720c794b3Sgavinm 	void *data;
40820c794b3Sgavinm 
40920c794b3Sgavinm 	if (cms_no_model_specific != 0)
41020c794b3Sgavinm 		return;
41120c794b3Sgavinm 
41220c794b3Sgavinm 	mutex_enter(&cms_load_lock);
41320c794b3Sgavinm 
41420c794b3Sgavinm 	if ((cms = cms_load_specific(hdl, &data)) != NULL) {
41520c794b3Sgavinm 		struct cms_ctl *cdp;
41620c794b3Sgavinm 
41720c794b3Sgavinm 		ASSERT(cmi_hdl_getspecific(hdl) == NULL);
41820c794b3Sgavinm 
41920c794b3Sgavinm 		cdp = kmem_alloc(sizeof (*cdp), KM_SLEEP);
42020c794b3Sgavinm 		cdp->cs_cms = cms;
42120c794b3Sgavinm 		cdp->cs_cmsdata = data;
42220c794b3Sgavinm 		cmi_hdl_setspecific(hdl, cdp);
42320c794b3Sgavinm 	}
42420c794b3Sgavinm 
42520c794b3Sgavinm 	mutex_exit(&cms_load_lock);
42620c794b3Sgavinm }
42720c794b3Sgavinm 
42820c794b3Sgavinm void
cms_fini(cmi_hdl_t hdl)42920c794b3Sgavinm cms_fini(cmi_hdl_t hdl)
43020c794b3Sgavinm {
43120c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
432a3114836SGerry Liu 	struct cms_ctl *cdp;
43320c794b3Sgavinm 
43420c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_fini))
43520c794b3Sgavinm 		CMS_OPS(cms)->cms_fini(hdl);
436a3114836SGerry Liu 
437a3114836SGerry Liu 	mutex_enter(&cms_load_lock);
438a3114836SGerry Liu 	cdp = (struct cms_ctl *)cmi_hdl_getspecific(hdl);
439a3114836SGerry Liu 	if (cdp != NULL) {
440a3114836SGerry Liu 		if (cdp->cs_cms != NULL)
441a3114836SGerry Liu 			cms_rele(cdp->cs_cms);
442a3114836SGerry Liu 		kmem_free(cdp, sizeof (*cdp));
443a3114836SGerry Liu 	}
444a3114836SGerry Liu 	mutex_exit(&cms_load_lock);
44520c794b3Sgavinm }
44620c794b3Sgavinm 
44720c794b3Sgavinm boolean_t
cms_present(cmi_hdl_t hdl)44820c794b3Sgavinm cms_present(cmi_hdl_t hdl)
44920c794b3Sgavinm {
45020c794b3Sgavinm 	return (HDL2CMS(hdl) != NULL ? B_TRUE : B_FALSE);
45120c794b3Sgavinm }
45220c794b3Sgavinm 
45320c794b3Sgavinm void
cms_post_startup(cmi_hdl_t hdl)45420c794b3Sgavinm cms_post_startup(cmi_hdl_t hdl)
45520c794b3Sgavinm {
45620c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
45720c794b3Sgavinm 
45820c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_post_startup))
45920c794b3Sgavinm 		CMS_OPS(cms)->cms_post_startup(hdl);
46020c794b3Sgavinm }
46120c794b3Sgavinm 
46220c794b3Sgavinm void
cms_post_mpstartup(cmi_hdl_t hdl)46320c794b3Sgavinm cms_post_mpstartup(cmi_hdl_t hdl)
46420c794b3Sgavinm {
46520c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
46620c794b3Sgavinm 
46720c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_post_mpstartup))
46820c794b3Sgavinm 		CMS_OPS(cms)->cms_post_mpstartup(hdl);
46920c794b3Sgavinm }
47020c794b3Sgavinm 
47120c794b3Sgavinm size_t
cms_logout_size(cmi_hdl_t hdl)47220c794b3Sgavinm cms_logout_size(cmi_hdl_t hdl)
47320c794b3Sgavinm {
47420c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
47520c794b3Sgavinm 
47620c794b3Sgavinm 	if (!CMS_OP_PRESENT(cms, cms_logout_size))
47720c794b3Sgavinm 		return (0);
47820c794b3Sgavinm 
47920c794b3Sgavinm 	return (CMS_OPS(cms)->cms_logout_size(hdl));
48020c794b3Sgavinm }
48120c794b3Sgavinm 
48220c794b3Sgavinm uint64_t
cms_mcgctl_val(cmi_hdl_t hdl,int nbanks,uint64_t def)48320c794b3Sgavinm cms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
48420c794b3Sgavinm {
48520c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
48620c794b3Sgavinm 
48720c794b3Sgavinm 	if (!CMS_OP_PRESENT(cms, cms_mcgctl_val))
48820c794b3Sgavinm 		return (def);
48920c794b3Sgavinm 
49020c794b3Sgavinm 	return (CMS_OPS(cms)->cms_mcgctl_val(hdl, nbanks, def));
49120c794b3Sgavinm }
49220c794b3Sgavinm 
49320c794b3Sgavinm boolean_t
cms_bankctl_skipinit(cmi_hdl_t hdl,int banknum)49420c794b3Sgavinm cms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
49520c794b3Sgavinm {
49620c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
49720c794b3Sgavinm 
49820c794b3Sgavinm 	if (!CMS_OP_PRESENT(cms, cms_bankctl_skipinit))
49920c794b3Sgavinm 		return (B_FALSE);
50020c794b3Sgavinm 
50120c794b3Sgavinm 	return (CMS_OPS(cms)->cms_bankctl_skipinit(hdl, banknum));
50220c794b3Sgavinm }
50320c794b3Sgavinm 
50420c794b3Sgavinm uint64_t
cms_bankctl_val(cmi_hdl_t hdl,int banknum,uint64_t def)50520c794b3Sgavinm cms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
50620c794b3Sgavinm {
50720c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
50820c794b3Sgavinm 
50920c794b3Sgavinm 	if (!CMS_OP_PRESENT(cms, cms_bankctl_val))
51020c794b3Sgavinm 		return (def);
51120c794b3Sgavinm 
51220c794b3Sgavinm 	return (CMS_OPS(cms)->cms_bankctl_val(hdl, banknum, def));
51320c794b3Sgavinm }
51420c794b3Sgavinm 
51520c794b3Sgavinm boolean_t
cms_bankstatus_skipinit(cmi_hdl_t hdl,int banknum)51620c794b3Sgavinm cms_bankstatus_skipinit(cmi_hdl_t hdl, int banknum)
51720c794b3Sgavinm {
51820c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
51920c794b3Sgavinm 
52020c794b3Sgavinm 	if (!CMS_OP_PRESENT(cms, cms_bankstatus_skipinit))
52120c794b3Sgavinm 		return (B_FALSE);
52220c794b3Sgavinm 
52320c794b3Sgavinm 	return (CMS_OPS(cms)->cms_bankstatus_skipinit(hdl, banknum));
52420c794b3Sgavinm }
52520c794b3Sgavinm 
52620c794b3Sgavinm uint64_t
cms_bankstatus_val(cmi_hdl_t hdl,int banknum,uint64_t def)52720c794b3Sgavinm cms_bankstatus_val(cmi_hdl_t hdl, int banknum, uint64_t def)
52820c794b3Sgavinm {
52920c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
53020c794b3Sgavinm 
53120c794b3Sgavinm 	if (!CMS_OP_PRESENT(cms, cms_bankstatus_val))
53220c794b3Sgavinm 		return (def);
53320c794b3Sgavinm 
53420c794b3Sgavinm 	return (CMS_OPS(cms)->cms_bankstatus_val(hdl, banknum, def));
53520c794b3Sgavinm }
53620c794b3Sgavinm 
53720c794b3Sgavinm void
cms_mca_init(cmi_hdl_t hdl,int nbanks)53820c794b3Sgavinm cms_mca_init(cmi_hdl_t hdl, int nbanks)
53920c794b3Sgavinm {
54020c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
54120c794b3Sgavinm 
54220c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_mca_init))
54320c794b3Sgavinm 		CMS_OPS(cms)->cms_mca_init(hdl, nbanks);
54420c794b3Sgavinm }
54520c794b3Sgavinm 
54620c794b3Sgavinm uint64_t
cms_poll_ownermask(cmi_hdl_t hdl,hrtime_t poll_interval)54720c794b3Sgavinm cms_poll_ownermask(cmi_hdl_t hdl, hrtime_t poll_interval)
54820c794b3Sgavinm {
54920c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
55020c794b3Sgavinm 
55120c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_poll_ownermask))
55220c794b3Sgavinm 		return (CMS_OPS(cms)->cms_poll_ownermask(hdl, poll_interval));
55320c794b3Sgavinm 	else
55420c794b3Sgavinm 		return (-1ULL);		/* poll all banks by default */
55520c794b3Sgavinm }
55620c794b3Sgavinm 
55720c794b3Sgavinm void
cms_bank_logout(cmi_hdl_t hdl,int banknum,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout)55820c794b3Sgavinm cms_bank_logout(cmi_hdl_t hdl, int banknum, uint64_t status, uint64_t addr,
55920c794b3Sgavinm     uint64_t misc, void *mslogout)
56020c794b3Sgavinm {
56120c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
56220c794b3Sgavinm 
56320c794b3Sgavinm 	if (mslogout != NULL && CMS_OP_PRESENT(cms, cms_bank_logout))
56420c794b3Sgavinm 		CMS_OPS(cms)->cms_bank_logout(hdl, banknum, status, addr,
56520c794b3Sgavinm 		    misc, mslogout);
56620c794b3Sgavinm }
56720c794b3Sgavinm 
56820c794b3Sgavinm cms_errno_t
cms_msrinject(cmi_hdl_t hdl,uint_t msr,uint64_t val)56920c794b3Sgavinm cms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
57020c794b3Sgavinm {
57120c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
57220c794b3Sgavinm 
57320c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_msrinject))
57420c794b3Sgavinm 		return (CMS_OPS(cms)->cms_msrinject(hdl, msr, val));
57520c794b3Sgavinm 	else
57620c794b3Sgavinm 		return (CMSERR_NOTSUP);
57720c794b3Sgavinm }
57820c794b3Sgavinm 
57920c794b3Sgavinm uint32_t
cms_error_action(cmi_hdl_t hdl,int ismc,int banknum,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout)58020c794b3Sgavinm cms_error_action(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
58120c794b3Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
58220c794b3Sgavinm {
58320c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
58420c794b3Sgavinm 
58520c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_error_action))
58620c794b3Sgavinm 		return (CMS_OPS(cms)->cms_error_action(hdl, ismc, banknum,
58720c794b3Sgavinm 		    status, addr, misc, mslogout));
58820c794b3Sgavinm 	else
58920c794b3Sgavinm 		return (0);
59020c794b3Sgavinm }
59120c794b3Sgavinm 
59220c794b3Sgavinm cms_cookie_t
cms_disp_match(cmi_hdl_t hdl,int ismc,int banknum,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout)593c84b7bbeSAdrian Frost cms_disp_match(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
594c84b7bbeSAdrian Frost     uint64_t addr, uint64_t misc, void *mslogout)
59520c794b3Sgavinm {
59620c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
59720c794b3Sgavinm 
59820c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_disp_match))
599c84b7bbeSAdrian Frost 		return (CMS_OPS(cms)->cms_disp_match(hdl, ismc, banknum,
60020c794b3Sgavinm 		    status, addr, misc, mslogout));
60120c794b3Sgavinm 	else
60220c794b3Sgavinm 		return (NULL);
60320c794b3Sgavinm 
60420c794b3Sgavinm }
60520c794b3Sgavinm 
60620c794b3Sgavinm void
cms_ereport_class(cmi_hdl_t hdl,cms_cookie_t mscookie,const char ** cpuclsp,const char ** leafclsp)60720c794b3Sgavinm cms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, const char **cpuclsp,
60820c794b3Sgavinm     const char **leafclsp)
60920c794b3Sgavinm {
61020c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
61120c794b3Sgavinm 
61220c794b3Sgavinm 	if (cpuclsp == NULL || leafclsp == NULL)
61320c794b3Sgavinm 		return;
61420c794b3Sgavinm 
61520c794b3Sgavinm 	*cpuclsp = *leafclsp = NULL;
61620c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_ereport_class)) {
61720c794b3Sgavinm 		CMS_OPS(cms)->cms_ereport_class(hdl, mscookie, cpuclsp,
61820c794b3Sgavinm 		    leafclsp);
61920c794b3Sgavinm 	}
62020c794b3Sgavinm }
62120c794b3Sgavinm 
62220c794b3Sgavinm nvlist_t *
cms_ereport_detector(cmi_hdl_t hdl,int bankno,cms_cookie_t mscookie,nv_alloc_t * nva)623491f61a1SYanmin Sun cms_ereport_detector(cmi_hdl_t hdl, int bankno, cms_cookie_t mscookie,
624491f61a1SYanmin Sun     nv_alloc_t *nva)
62520c794b3Sgavinm {
62620c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
62720c794b3Sgavinm 
62820c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_ereport_detector))
629491f61a1SYanmin Sun 		return (CMS_OPS(cms)->cms_ereport_detector(hdl, bankno,
630491f61a1SYanmin Sun 		    mscookie, nva));
63120c794b3Sgavinm 	else
63220c794b3Sgavinm 		return (NULL);
63320c794b3Sgavinm 
63420c794b3Sgavinm }
63520c794b3Sgavinm 
63620c794b3Sgavinm boolean_t
cms_ereport_includestack(cmi_hdl_t hdl,cms_cookie_t mscookie)63720c794b3Sgavinm cms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
63820c794b3Sgavinm {
63920c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
64020c794b3Sgavinm 
64120c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_ereport_includestack)) {
64220c794b3Sgavinm 		return (CMS_OPS(cms)->cms_ereport_includestack(hdl, mscookie));
64320c794b3Sgavinm 	} else {
64420c794b3Sgavinm 		return (B_FALSE);
64520c794b3Sgavinm 	}
64620c794b3Sgavinm }
64720c794b3Sgavinm 
64820c794b3Sgavinm void
cms_ereport_add_logout(cmi_hdl_t hdl,nvlist_t * nvl,nv_alloc_t * nva,int banknum,uint64_t status,uint64_t addr,uint64_t misc,void * mslogout,cms_cookie_t mscookie)64920c794b3Sgavinm cms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *nvl, nv_alloc_t *nva,
65020c794b3Sgavinm     int banknum, uint64_t status, uint64_t addr, uint64_t misc, void *mslogout,
65120c794b3Sgavinm     cms_cookie_t mscookie)
65220c794b3Sgavinm {
65320c794b3Sgavinm 	cms_t *cms = HDL2CMS(hdl);
65420c794b3Sgavinm 
65520c794b3Sgavinm 	if (CMS_OP_PRESENT(cms, cms_ereport_add_logout))
65620c794b3Sgavinm 		CMS_OPS(cms)->cms_ereport_add_logout(hdl, nvl, nva, banknum,
65720c794b3Sgavinm 		    status, addr, misc, mslogout, mscookie);
65820c794b3Sgavinm 
65920c794b3Sgavinm }
660