xref: /onnv-gate/usr/src/uts/i86pc/os/cmi.c (revision 5254:38162db71c7d)
11642Sgavinm /*
21642Sgavinm  * CDDL HEADER START
31642Sgavinm  *
41642Sgavinm  * The contents of this file are subject to the terms of the
51642Sgavinm  * Common Development and Distribution License (the "License").
61642Sgavinm  * You may not use this file except in compliance with the License.
71642Sgavinm  *
81642Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91642Sgavinm  * or http://www.opensolaris.org/os/licensing.
101642Sgavinm  * See the License for the specific language governing permissions
111642Sgavinm  * and limitations under the License.
121642Sgavinm  *
131642Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
141642Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151642Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
161642Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
171642Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
181642Sgavinm  *
191642Sgavinm  * CDDL HEADER END
201642Sgavinm  */
211642Sgavinm 
221414Scindi /*
233446Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
241414Scindi  * Use is subject to license terms.
251414Scindi  */
261414Scindi 
271414Scindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
281414Scindi 
291414Scindi /*
301414Scindi  * Public interface to routines implemented by CPU modules
311414Scindi  */
321414Scindi 
33*5254Sgavinm #include <sys/types.h>
34*5254Sgavinm #include <sys/atomic.h>
351414Scindi #include <sys/x86_archext.h>
361414Scindi #include <sys/cpu_module_impl.h>
37*5254Sgavinm #include <sys/cpu_module_ms.h>
381414Scindi #include <sys/fm/util.h>
391414Scindi #include <sys/reboot.h>
401414Scindi #include <sys/modctl.h>
411414Scindi #include <sys/param.h>
421414Scindi #include <sys/cmn_err.h>
431414Scindi #include <sys/systm.h>
44*5254Sgavinm #include <sys/fm/protocol.h>
45*5254Sgavinm #include <sys/pcb.h>
46*5254Sgavinm #include <sys/ontrap.h>
47*5254Sgavinm #include <sys/psw.h>
48*5254Sgavinm #include <sys/privregs.h>
491414Scindi 
50*5254Sgavinm /*
51*5254Sgavinm  * Set to force cmi_init to fail.
52*5254Sgavinm  */
53*5254Sgavinm int cmi_no_init = 0;
54*5254Sgavinm 
55*5254Sgavinm /*
56*5254Sgavinm  * Set to avoid MCA initialization.
57*5254Sgavinm  */
58*5254Sgavinm int cmi_no_mca_init = 0;
591414Scindi 
601414Scindi /*
612869Sgavinm  * If cleared for debugging we will not attempt to load a model-specific
622869Sgavinm  * cpu module but will load the generic cpu module instead.
632869Sgavinm  */
642869Sgavinm int cmi_force_generic = 0;
652869Sgavinm 
662869Sgavinm /*
671414Scindi  * If cleared for debugging, we will suppress panicking on fatal hardware
681414Scindi  * errors.  This should *only* be used for debugging; it use can and will
691414Scindi  * cause data corruption if actual hardware errors are detected by the system.
701414Scindi  */
711414Scindi int cmi_panic_on_uncorrectable_error = 1;
721414Scindi 
73*5254Sgavinm /*
74*5254Sgavinm  * Subdirectory (relative to the module search path) in which we will
75*5254Sgavinm  * look for cpu modules.
76*5254Sgavinm  */
77*5254Sgavinm #define	CPUMOD_SUBDIR	"cpu"
78*5254Sgavinm 
79*5254Sgavinm /*
80*5254Sgavinm  * CPU modules have a filenames such as "cpu.AuthenticAMD.15" and
81*5254Sgavinm  * "cpu.generic" - the "cpu" prefix is specified by the following.
82*5254Sgavinm  */
83*5254Sgavinm #define	CPUMOD_PREFIX	"cpu"
84*5254Sgavinm 
85*5254Sgavinm /*
86*5254Sgavinm  * Structure used to keep track of cpu modules we have loaded and their ops
87*5254Sgavinm  */
88*5254Sgavinm typedef struct cmi {
89*5254Sgavinm 	struct cmi *cmi_next;
90*5254Sgavinm 	struct cmi *cmi_prev;
91*5254Sgavinm 	const cmi_ops_t *cmi_ops;
92*5254Sgavinm 	struct modctl *cmi_modp;
93*5254Sgavinm 	uint_t cmi_refcnt;
94*5254Sgavinm } cmi_t;
95*5254Sgavinm 
961414Scindi static cmi_t *cmi_list;
971414Scindi static kmutex_t cmi_load_lock;
981414Scindi 
99*5254Sgavinm /*
100*5254Sgavinm  * Functions we need from cmi_hw.c that are not part of the cpu_module.h
101*5254Sgavinm  * interface.
102*5254Sgavinm  */
103*5254Sgavinm extern cmi_hdl_t cmi_hdl_create(enum cmi_hdl_class, uint_t, uint_t, uint_t);
104*5254Sgavinm extern void cmi_hdl_setcmi(cmi_hdl_t, void *, void *);
105*5254Sgavinm extern void *cmi_hdl_getcmi(cmi_hdl_t);
106*5254Sgavinm extern void cmi_hdl_setmc(cmi_hdl_t, const struct cmi_mc_ops *, void *);
107*5254Sgavinm 
108*5254Sgavinm #define	HDL2CMI(hdl)		cmi_hdl_getcmi(hdl)
109*5254Sgavinm 
110*5254Sgavinm #define	CMI_OPS(cmi)		(cmi)->cmi_ops
111*5254Sgavinm #define	CMI_OP_PRESENT(cmi, op)	((cmi) && CMI_OPS(cmi)->op != NULL)
112*5254Sgavinm 
113*5254Sgavinm #define	CMI_MATCH_VENDOR	0	/* Just match on vendor */
114*5254Sgavinm #define	CMI_MATCH_FAMILY	1	/* Match down to family */
115*5254Sgavinm #define	CMI_MATCH_MODEL		2	/* Match down to model */
116*5254Sgavinm #define	CMI_MATCH_STEPPING	3	/* Match down to stepping */
117*5254Sgavinm 
118*5254Sgavinm static void
119*5254Sgavinm cmi_link(cmi_t *cmi)
120*5254Sgavinm {
121*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
122*5254Sgavinm 
123*5254Sgavinm 	cmi->cmi_prev = NULL;
124*5254Sgavinm 	cmi->cmi_next = cmi_list;
125*5254Sgavinm 	if (cmi_list != NULL)
126*5254Sgavinm 		cmi_list->cmi_prev = cmi;
127*5254Sgavinm 	cmi_list = cmi;
128*5254Sgavinm }
129*5254Sgavinm 
130*5254Sgavinm static void
131*5254Sgavinm cmi_unlink(cmi_t *cmi)
1321414Scindi {
133*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
134*5254Sgavinm 	ASSERT(cmi->cmi_refcnt == 0);
135*5254Sgavinm 
136*5254Sgavinm 	if (cmi->cmi_prev != NULL)
137*5254Sgavinm 		cmi->cmi_prev = cmi->cmi_next;
138*5254Sgavinm 
139*5254Sgavinm 	if (cmi->cmi_next != NULL)
140*5254Sgavinm 		cmi->cmi_next->cmi_prev = cmi->cmi_prev;
141*5254Sgavinm 
142*5254Sgavinm 	if (cmi_list == cmi)
143*5254Sgavinm 		cmi_list = cmi->cmi_next;
144*5254Sgavinm }
145*5254Sgavinm 
146*5254Sgavinm /*
147*5254Sgavinm  * Hold the module in memory.  We call to CPU modules without using the
148*5254Sgavinm  * stubs mechanism, so these modules must be manually held in memory.
149*5254Sgavinm  * The mod_ref acts as if another loaded module has a dependency on us.
150*5254Sgavinm  */
151*5254Sgavinm static void
152*5254Sgavinm cmi_hold(cmi_t *cmi)
153*5254Sgavinm {
154*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
155*5254Sgavinm 
156*5254Sgavinm 	mutex_enter(&mod_lock);
157*5254Sgavinm 	cmi->cmi_modp->mod_ref++;
158*5254Sgavinm 	mutex_exit(&mod_lock);
159*5254Sgavinm 	cmi->cmi_refcnt++;
160*5254Sgavinm }
161*5254Sgavinm 
162*5254Sgavinm static void
163*5254Sgavinm cmi_rele(cmi_t *cmi)
164*5254Sgavinm {
165*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
166*5254Sgavinm 
167*5254Sgavinm 	mutex_enter(&mod_lock);
168*5254Sgavinm 	cmi->cmi_modp->mod_ref--;
169*5254Sgavinm 	mutex_exit(&mod_lock);
170*5254Sgavinm 
171*5254Sgavinm 	if (--cmi->cmi_refcnt == 0) {
172*5254Sgavinm 		cmi_unlink(cmi);
173*5254Sgavinm 		kmem_free(cmi, sizeof (cmi_t));
174*5254Sgavinm 	}
175*5254Sgavinm }
176*5254Sgavinm 
177*5254Sgavinm static cmi_ops_t *
178*5254Sgavinm cmi_getops(modctl_t *modp)
179*5254Sgavinm {
180*5254Sgavinm 	cmi_ops_t *ops;
181*5254Sgavinm 
182*5254Sgavinm 	if ((ops = (cmi_ops_t *)modlookup_by_modctl(modp, "_cmi_ops")) ==
183*5254Sgavinm 	    NULL) {
184*5254Sgavinm 		cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops "
185*5254Sgavinm 		    "found", modp->mod_modname);
186*5254Sgavinm 		return (NULL);
187*5254Sgavinm 	}
188*5254Sgavinm 
189*5254Sgavinm 	if (ops->cmi_init == NULL) {
190*5254Sgavinm 		cmn_err(CE_WARN, "cpu module '%s' is invalid: no cmi_init "
191*5254Sgavinm 		    "entry point", modp->mod_modname);
192*5254Sgavinm 		return (NULL);
193*5254Sgavinm 	}
194*5254Sgavinm 
195*5254Sgavinm 	return (ops);
1961414Scindi }
1971414Scindi 
1981414Scindi static cmi_t *
1991414Scindi cmi_load_modctl(modctl_t *modp)
2001414Scindi {
201*5254Sgavinm 	cmi_ops_t *ops;
202*5254Sgavinm 	uintptr_t ver;
2031414Scindi 	cmi_t *cmi;
204*5254Sgavinm 	cmi_api_ver_t apiver;
2051414Scindi 
2061414Scindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
2071414Scindi 
2081414Scindi 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
2091414Scindi 		if (cmi->cmi_modp == modp)
2101414Scindi 			return (cmi);
2111414Scindi 	}
2121414Scindi 
213*5254Sgavinm 	if ((ver = modlookup_by_modctl(modp, "_cmi_api_version")) == NULL) {
214*5254Sgavinm 		/*
215*5254Sgavinm 		 * Apparently a cpu module before versioning was introduced -
216*5254Sgavinm 		 * we call this version 0.
217*5254Sgavinm 		 */
218*5254Sgavinm 		apiver = CMI_API_VERSION_0;
219*5254Sgavinm 	} else {
220*5254Sgavinm 		apiver = *((cmi_api_ver_t *)ver);
221*5254Sgavinm 		if (!CMI_API_VERSION_CHKMAGIC(apiver)) {
222*5254Sgavinm 			cmn_err(CE_WARN, "cpu module '%s' is invalid: "
223*5254Sgavinm 			    "_cmi_api_version 0x%x has bad magic",
224*5254Sgavinm 			    modp->mod_modname, apiver);
225*5254Sgavinm 			return (NULL);
226*5254Sgavinm 		}
227*5254Sgavinm 	}
228*5254Sgavinm 
229*5254Sgavinm 	if (apiver != CMI_API_VERSION) {
230*5254Sgavinm 		cmn_err(CE_WARN, "cpu module '%s' has API version %d, "
231*5254Sgavinm 		    "kernel requires API version %d", modp->mod_modname,
232*5254Sgavinm 		    CMI_API_VERSION_TOPRINT(apiver),
233*5254Sgavinm 		    CMI_API_VERSION_TOPRINT(CMI_API_VERSION));
2341414Scindi 		return (NULL);
2351414Scindi 	}
2361414Scindi 
237*5254Sgavinm 	if ((ops = cmi_getops(modp)) == NULL)
238*5254Sgavinm 		return (NULL);
2391414Scindi 
240*5254Sgavinm 	cmi = kmem_zalloc(sizeof (*cmi), KM_SLEEP);
241*5254Sgavinm 	cmi->cmi_ops = ops;
2421414Scindi 	cmi->cmi_modp = modp;
2431414Scindi 
244*5254Sgavinm 	cmi_link(cmi);
245*5254Sgavinm 
246*5254Sgavinm 	return (cmi);
247*5254Sgavinm }
248*5254Sgavinm 
249*5254Sgavinm static int
250*5254Sgavinm cmi_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match)
251*5254Sgavinm {
252*5254Sgavinm 	if (match >= CMI_MATCH_VENDOR &&
253*5254Sgavinm 	    cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2))
254*5254Sgavinm 		return (0);
255*5254Sgavinm 
256*5254Sgavinm 	if (match >= CMI_MATCH_FAMILY &&
257*5254Sgavinm 	    cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2))
258*5254Sgavinm 		return (0);
259*5254Sgavinm 
260*5254Sgavinm 	if (match >= CMI_MATCH_MODEL &&
261*5254Sgavinm 	    cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2))
262*5254Sgavinm 		return (0);
263*5254Sgavinm 
264*5254Sgavinm 	if (match >= CMI_MATCH_STEPPING &&
265*5254Sgavinm 	    cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2))
266*5254Sgavinm 		return (0);
267*5254Sgavinm 
268*5254Sgavinm 	return (1);
269*5254Sgavinm }
270*5254Sgavinm 
271*5254Sgavinm static int
272*5254Sgavinm cmi_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
273*5254Sgavinm {
274*5254Sgavinm 	cmi_hdl_t thdl = (cmi_hdl_t)arg1;
275*5254Sgavinm 	int match = *((int *)arg2);
276*5254Sgavinm 	cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3;
277*5254Sgavinm 
278*5254Sgavinm 	if (cmi_cpu_match(thdl, whdl, match)) {
279*5254Sgavinm 		cmi_hdl_hold(whdl);	/* short-term hold */
280*5254Sgavinm 		*rsltp = whdl;
281*5254Sgavinm 		return (CMI_HDL_WALK_DONE);
282*5254Sgavinm 	} else {
283*5254Sgavinm 		return (CMI_HDL_WALK_NEXT);
284*5254Sgavinm 	}
285*5254Sgavinm }
286*5254Sgavinm 
287*5254Sgavinm static cmi_t *
288*5254Sgavinm cmi_search_list(cmi_hdl_t hdl, int match)
289*5254Sgavinm {
290*5254Sgavinm 	cmi_hdl_t dhdl = NULL;
291*5254Sgavinm 	cmi_t *cmi = NULL;
292*5254Sgavinm 
293*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
294*5254Sgavinm 
295*5254Sgavinm 	cmi_hdl_walk(cmi_search_list_cb, (void *)hdl, (void *)&match, &dhdl);
296*5254Sgavinm 	if (dhdl) {
297*5254Sgavinm 		cmi = HDL2CMI(dhdl);
298*5254Sgavinm 		cmi_hdl_rele(dhdl);	/* held in cmi_search_list_cb */
299*5254Sgavinm 	}
3001414Scindi 
3011414Scindi 	return (cmi);
3021414Scindi }
3031414Scindi 
3041414Scindi static cmi_t *
305*5254Sgavinm cmi_load_module(cmi_hdl_t hdl, int match, int *chosenp)
3061414Scindi {
3071414Scindi 	modctl_t *modp;
3081414Scindi 	cmi_t *cmi;
309*5254Sgavinm 	int modid;
3101414Scindi 	uint_t s[3];
3111414Scindi 
312*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
313*5254Sgavinm 	ASSERT(match == CMI_MATCH_STEPPING || match == CMI_MATCH_MODEL ||
314*5254Sgavinm 	    match == CMI_MATCH_FAMILY || match == CMI_MATCH_VENDOR);
315*5254Sgavinm 
3161414Scindi 	/*
317*5254Sgavinm 	 * Have we already loaded a module for a cpu with the same
318*5254Sgavinm 	 * vendor/family/model/stepping?
3191414Scindi 	 */
320*5254Sgavinm 	if ((cmi = cmi_search_list(hdl, match)) != NULL) {
321*5254Sgavinm 		cmi_hold(cmi);
322*5254Sgavinm 		return (cmi);
3231414Scindi 	}
3241414Scindi 
325*5254Sgavinm 	s[0] = cmi_hdl_family(hdl);
326*5254Sgavinm 	s[1] = cmi_hdl_model(hdl);
327*5254Sgavinm 	s[2] = cmi_hdl_stepping(hdl);
3281414Scindi 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
329*5254Sgavinm 	    cmi_hdl_vendorstr(hdl), ".", s, match, chosenp);
3301414Scindi 
3311414Scindi 	if (modid == -1)
3321414Scindi 		return (NULL);
3331414Scindi 
3341414Scindi 	modp = mod_hold_by_id(modid);
3351414Scindi 	cmi = cmi_load_modctl(modp);
336*5254Sgavinm 	if (cmi)
337*5254Sgavinm 		cmi_hold(cmi);
3381414Scindi 	mod_release_mod(modp);
3391414Scindi 
3401414Scindi 	return (cmi);
3411414Scindi }
3421414Scindi 
3431414Scindi /*
344*5254Sgavinm  * Try to load a cpu module with specific support for this chip type.
3451414Scindi  */
346*5254Sgavinm static cmi_t *
347*5254Sgavinm cmi_load_specific(cmi_hdl_t hdl, void **datap)
3481414Scindi {
3491414Scindi 	cmi_t *cmi;
350*5254Sgavinm 	int err;
351*5254Sgavinm 	int i;
352*5254Sgavinm 
353*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
354*5254Sgavinm 
355*5254Sgavinm 	for (i = CMI_MATCH_STEPPING; i >= CMI_MATCH_VENDOR; i--) {
356*5254Sgavinm 		int suffixlevel;
357*5254Sgavinm 
358*5254Sgavinm 		if ((cmi = cmi_load_module(hdl, i, &suffixlevel)) == NULL)
359*5254Sgavinm 			return (NULL);
360*5254Sgavinm 
361*5254Sgavinm 		/*
362*5254Sgavinm 		 * A module has loaded and has a _cmi_ops structure, and the
363*5254Sgavinm 		 * module has been held for this instance.  Call its cmi_init
364*5254Sgavinm 		 * entry point - we expect success (0) or ENOTSUP.
365*5254Sgavinm 		 */
366*5254Sgavinm 		if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) == 0) {
367*5254Sgavinm 			if (boothowto & RB_VERBOSE) {
368*5254Sgavinm 				printf("initialized cpu module '%s' on "
369*5254Sgavinm 				    "chip %d core %d strand %d\n",
370*5254Sgavinm 				    cmi->cmi_modp->mod_modname,
371*5254Sgavinm 				    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
372*5254Sgavinm 				    cmi_hdl_strandid(hdl));
373*5254Sgavinm 			}
374*5254Sgavinm 			return (cmi);
375*5254Sgavinm 		} else if (err != ENOTSUP) {
376*5254Sgavinm 			cmn_err(CE_WARN, "failed to init cpu module '%s' on "
377*5254Sgavinm 			    "chip %d core %d strand %d: err=%d\n",
378*5254Sgavinm 			    cmi->cmi_modp->mod_modname,
379*5254Sgavinm 			    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
380*5254Sgavinm 			    cmi_hdl_strandid(hdl), err);
381*5254Sgavinm 		}
382*5254Sgavinm 
383*5254Sgavinm 		/*
384*5254Sgavinm 		 * The module failed or declined to init, so release
385*5254Sgavinm 		 * it and update i to be equal to the number
386*5254Sgavinm 		 * of suffices actually used in the last module path.
387*5254Sgavinm 		 */
388*5254Sgavinm 		cmi_rele(cmi);
389*5254Sgavinm 		i = suffixlevel;
390*5254Sgavinm 	}
391*5254Sgavinm 
392*5254Sgavinm 	return (NULL);
393*5254Sgavinm }
394*5254Sgavinm 
395*5254Sgavinm /*
396*5254Sgavinm  * Load the generic IA32 MCA cpu module, which may still supplement
397*5254Sgavinm  * itself with model-specific support through cpu model-specific modules.
398*5254Sgavinm  */
399*5254Sgavinm static cmi_t *
400*5254Sgavinm cmi_load_generic(cmi_hdl_t hdl, void **datap)
401*5254Sgavinm {
402*5254Sgavinm 	modctl_t *modp;
403*5254Sgavinm 	cmi_t *cmi;
404*5254Sgavinm 	int modid;
405*5254Sgavinm 	int err;
406*5254Sgavinm 
407*5254Sgavinm 	ASSERT(MUTEX_HELD(&cmi_load_lock));
408*5254Sgavinm 
409*5254Sgavinm 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
410*5254Sgavinm 		return (NULL);
411*5254Sgavinm 
412*5254Sgavinm 	modp = mod_hold_by_id(modid);
413*5254Sgavinm 	cmi = cmi_load_modctl(modp);
414*5254Sgavinm 	if (cmi)
415*5254Sgavinm 		cmi_hold(cmi);
416*5254Sgavinm 	mod_release_mod(modp);
417*5254Sgavinm 
418*5254Sgavinm 	if (cmi == NULL)
419*5254Sgavinm 		return (NULL);
420*5254Sgavinm 
421*5254Sgavinm 	if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) != 0) {
422*5254Sgavinm 		if (err != ENOTSUP)
423*5254Sgavinm 			cmn_err(CE_WARN, CPUMOD_PREFIX ".generic failed to "
424*5254Sgavinm 			    "init: err=%d", err);
425*5254Sgavinm 		cmi_rele(cmi);
426*5254Sgavinm 		return (NULL);
427*5254Sgavinm 	}
428*5254Sgavinm 
429*5254Sgavinm 	return (cmi);
430*5254Sgavinm }
431*5254Sgavinm 
432*5254Sgavinm cmi_hdl_t
433*5254Sgavinm cmi_init(enum cmi_hdl_class class, uint_t chipid, uint_t coreid,
434*5254Sgavinm     uint_t strandid)
435*5254Sgavinm {
436*5254Sgavinm 	cmi_t *cmi = NULL;
437*5254Sgavinm 	cmi_hdl_t hdl;
4381414Scindi 	void *data;
4391414Scindi 
440*5254Sgavinm 	if (cmi_no_init) {
441*5254Sgavinm 		cmi_no_mca_init = 1;
442*5254Sgavinm 		return (NULL);
443*5254Sgavinm 	}
444*5254Sgavinm 
4451414Scindi 	mutex_enter(&cmi_load_lock);
4461414Scindi 
447*5254Sgavinm 	if ((hdl = cmi_hdl_create(class, chipid, coreid, strandid)) == NULL) {
4481414Scindi 		mutex_exit(&cmi_load_lock);
449*5254Sgavinm 		cmn_err(CE_WARN, "There will be no MCA support on chip %d "
450*5254Sgavinm 		    "core %d strand %d (cmi_hdl_create returned NULL)\n",
451*5254Sgavinm 		    chipid, coreid, strandid);
452*5254Sgavinm 		return (NULL);
4531414Scindi 	}
4541414Scindi 
455*5254Sgavinm 	if (!cmi_force_generic)
456*5254Sgavinm 		cmi = cmi_load_specific(hdl, &data);
457*5254Sgavinm 
458*5254Sgavinm 	if (cmi == NULL && (cmi = cmi_load_generic(hdl, &data)) == NULL) {
459*5254Sgavinm 		cmn_err(CE_WARN, "There will be no MCA support on chip %d "
460*5254Sgavinm 		    "core %d strand %d\n", chipid, coreid, strandid);
461*5254Sgavinm 		cmi_hdl_rele(hdl);
4621414Scindi 		mutex_exit(&cmi_load_lock);
463*5254Sgavinm 		return (NULL);
4641414Scindi 	}
4651414Scindi 
466*5254Sgavinm 	cmi_hdl_setcmi(hdl, cmi, data);
4671414Scindi 
468*5254Sgavinm 	cms_init(hdl);
469*5254Sgavinm 
4701414Scindi 	mutex_exit(&cmi_load_lock);
4711414Scindi 
472*5254Sgavinm 	return (hdl);
4731414Scindi }
4741414Scindi 
475*5254Sgavinm /*
476*5254Sgavinm  * cmi_fini is not called at the moment.  It is intended to be called
477*5254Sgavinm  * on DR deconfigure of a cpu resource.  It should not be called at
478*5254Sgavinm  * simple offline of a cpu.
479*5254Sgavinm  */
4801414Scindi void
481*5254Sgavinm cmi_fini(cmi_hdl_t hdl)
4821414Scindi {
483*5254Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
484*5254Sgavinm 
485*5254Sgavinm 	if (cms_present(hdl))
486*5254Sgavinm 		cms_fini(hdl);
487*5254Sgavinm 
488*5254Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_fini))
489*5254Sgavinm 		CMI_OPS(cmi)->cmi_fini(hdl);
490*5254Sgavinm 
491*5254Sgavinm 	cmi_hdl_rele(hdl);	/* release hold obtained in cmi_hdl_create */
4921414Scindi }
4931414Scindi 
494*5254Sgavinm /*
495*5254Sgavinm  * cmi_post_startup is called from post_startup for the boot cpu only.
496*5254Sgavinm  */
4971414Scindi void
498*5254Sgavinm cmi_post_startup(void)
4991414Scindi {
500*5254Sgavinm 	cmi_hdl_t hdl;
501*5254Sgavinm 	cmi_t *cmi;
502*5254Sgavinm 
503*5254Sgavinm 	if (cmi_no_mca_init != 0 ||
504*5254Sgavinm 	    (hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
505*5254Sgavinm 		return;
506*5254Sgavinm 
507*5254Sgavinm 	cmi = HDL2CMI(hdl);
508*5254Sgavinm 
509*5254Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_post_startup))
510*5254Sgavinm 		CMI_OPS(cmi)->cmi_post_startup(hdl);
511*5254Sgavinm 
512*5254Sgavinm 	cmi_hdl_rele(hdl);
5131414Scindi }
5141414Scindi 
5152869Sgavinm /*
5162869Sgavinm  * Called just once from start_other_cpus when all processors are started.
5172869Sgavinm  * This will not be called for each cpu, so the registered op must not
5182869Sgavinm  * assume it is called as such.
5192869Sgavinm  */
5201414Scindi void
5211642Sgavinm cmi_post_mpstartup(void)
5221642Sgavinm {
523*5254Sgavinm 	cmi_hdl_t hdl;
524*5254Sgavinm 	cmi_t *cmi;
525*5254Sgavinm 
526*5254Sgavinm 	if (cmi_no_mca_init != 0 ||
527*5254Sgavinm 	    (hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
528*5254Sgavinm 		return;
529*5254Sgavinm 
530*5254Sgavinm 	cmi = HDL2CMI(hdl);
531*5254Sgavinm 
532*5254Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_post_mpstartup))
533*5254Sgavinm 		CMI_OPS(cmi)->cmi_post_mpstartup(hdl);
534*5254Sgavinm 
535*5254Sgavinm 	cmi_hdl_rele(hdl);
5361642Sgavinm }
5371642Sgavinm 
5381642Sgavinm void
539*5254Sgavinm cmi_faulted_enter(cmi_hdl_t hdl)
5401414Scindi {
541*5254Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
542*5254Sgavinm 
543*5254Sgavinm 	if (cmi_no_mca_init != 0)
544*5254Sgavinm 		return;
545*5254Sgavinm 
546*5254Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_faulted_enter))
547*5254Sgavinm 		CMI_OPS(cmi)->cmi_faulted_enter(hdl);
548*5254Sgavinm }
549*5254Sgavinm 
550*5254Sgavinm void
551*5254Sgavinm cmi_faulted_exit(cmi_hdl_t hdl)
552*5254Sgavinm {
553*5254Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
554*5254Sgavinm 
555*5254Sgavinm 	if (cmi_no_mca_init != 0)
556*5254Sgavinm 		return;
557*5254Sgavinm 
558*5254Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_faulted_exit))
559*5254Sgavinm 		CMI_OPS(cmi)->cmi_faulted_exit(hdl);
5601414Scindi }
5611414Scindi 
5621414Scindi void
563*5254Sgavinm cmi_mca_init(cmi_hdl_t hdl)
5641414Scindi {
565*5254Sgavinm 	cmi_t *cmi;
566*5254Sgavinm 
567*5254Sgavinm 	if (cmi_no_mca_init != 0)
568*5254Sgavinm 		return;
569*5254Sgavinm 
570*5254Sgavinm 	cmi = HDL2CMI(hdl);
571*5254Sgavinm 
572*5254Sgavinm 	if (CMI_OP_PRESENT(cmi, cmi_mca_init))
573*5254Sgavinm 		CMI_OPS(cmi)->cmi_mca_init(hdl);
5741414Scindi }
5751414Scindi 
576*5254Sgavinm #define	CMI_RESPONSE_PANIC		0x0	/* panic must have value 0 */
577*5254Sgavinm #define	CMI_RESPONSE_NONE		0x1
578*5254Sgavinm #define	CMI_RESPONSE_CKILL		0x2
579*5254Sgavinm #define	CMI_RESPONSE_REBOOT		0x3	/* not implemented */
580*5254Sgavinm #define	CMI_RESPONSE_ONTRAP_PROT	0x4
581*5254Sgavinm #define	CMI_RESPONSE_LOFAULT_PROT	0x5
582*5254Sgavinm 
583*5254Sgavinm /*
584*5254Sgavinm  * Return 0 if we will panic in response to this machine check, otherwise
585*5254Sgavinm  * non-zero.  If the caller is cmi_mca_trap in this file then the nonzero
586*5254Sgavinm  * return values are to be interpreted from CMI_RESPONSE_* above.
587*5254Sgavinm  *
588*5254Sgavinm  * This function must just return what will be done without actually
589*5254Sgavinm  * doing anything; this includes not changing the regs.
590*5254Sgavinm  */
5911414Scindi int
592*5254Sgavinm cmi_mce_response(struct regs *rp, uint64_t disp)
5931414Scindi {
594*5254Sgavinm 	int panicrsp = cmi_panic_on_uncorrectable_error ? CMI_RESPONSE_PANIC :
595*5254Sgavinm 	    CMI_RESPONSE_NONE;
596*5254Sgavinm 	on_trap_data_t *otp;
597*5254Sgavinm 
598*5254Sgavinm 	ASSERT(rp != NULL);	/* don't call for polling, only on #MC */
599*5254Sgavinm 
600*5254Sgavinm 	/*
601*5254Sgavinm 	 * If no bits are set in the disposition then there is nothing to
602*5254Sgavinm 	 * worry about and we do not need to trampoline to ontrap or
603*5254Sgavinm 	 * lofault handlers.
604*5254Sgavinm 	 */
605*5254Sgavinm 	if (disp == 0)
606*5254Sgavinm 		return (CMI_RESPONSE_NONE);
607*5254Sgavinm 
608*5254Sgavinm 	/*
609*5254Sgavinm 	 * Unconstrained errors cannot be forgiven, even by ontrap or
610*5254Sgavinm 	 * lofault protection.  The data is not poisoned and may not
611*5254Sgavinm 	 * even belong to the trapped context - eg a writeback of
612*5254Sgavinm 	 * data that is found to be bad.
613*5254Sgavinm 	 */
614*5254Sgavinm 	if (disp & CMI_ERRDISP_UC_UNCONSTRAINED)
615*5254Sgavinm 		return (panicrsp);
616*5254Sgavinm 
617*5254Sgavinm 	/*
618*5254Sgavinm 	 * ontrap OT_DATA_EC and lofault protection forgive any disposition
619*5254Sgavinm 	 * other than unconstrained, even those normally forced fatal.
620*5254Sgavinm 	 */
621*5254Sgavinm 	if ((otp = curthread->t_ontrap) != NULL && otp->ot_prot & OT_DATA_EC)
622*5254Sgavinm 		return (CMI_RESPONSE_ONTRAP_PROT);
623*5254Sgavinm 	else if (curthread->t_lofault)
624*5254Sgavinm 		return (CMI_RESPONSE_LOFAULT_PROT);
625*5254Sgavinm 
626*5254Sgavinm 	/*
627*5254Sgavinm 	 * Forced-fatal errors are terminal even in user mode.
628*5254Sgavinm 	 */
629*5254Sgavinm 	if (disp & CMI_ERRDISP_FORCEFATAL)
630*5254Sgavinm 		return (panicrsp);
631*5254Sgavinm 
632*5254Sgavinm 	/*
633*5254Sgavinm 	 * If the trapped context is corrupt or we have no instruction pointer
634*5254Sgavinm 	 * to resume at (and aren't trampolining to a fault handler)
635*5254Sgavinm 	 * then in the kernel case we must panic and in usermode we
636*5254Sgavinm 	 * kill the affected contract.
637*5254Sgavinm 	 */
638*5254Sgavinm 	if (disp & (CMI_ERRDISP_CURCTXBAD | CMI_ERRDISP_RIPV_INVALID))
639*5254Sgavinm 		return (USERMODE(rp->r_cs) ?  CMI_RESPONSE_CKILL : panicrsp);
640*5254Sgavinm 
641*5254Sgavinm 	/*
642*5254Sgavinm 	 * Anything else is harmless
643*5254Sgavinm 	 */
644*5254Sgavinm 	return (CMI_RESPONSE_NONE);
6451414Scindi }
6461414Scindi 
647*5254Sgavinm int cma_mca_trap_panic_suppressed = 0;
648*5254Sgavinm 
649*5254Sgavinm static void
650*5254Sgavinm cmi_mca_panic(void)
6511414Scindi {
652*5254Sgavinm 	if (cmi_panic_on_uncorrectable_error) {
653*5254Sgavinm 		fm_panic("Unrecoverable Machine-Check Exception");
654*5254Sgavinm 	} else {
655*5254Sgavinm 		cmn_err(CE_WARN, "suppressing panic from fatal #mc");
656*5254Sgavinm 		cma_mca_trap_panic_suppressed++;
657*5254Sgavinm 	}
6581414Scindi }
6591414Scindi 
660*5254Sgavinm 
661*5254Sgavinm int cma_mca_trap_contract_kills = 0;
662*5254Sgavinm int cma_mca_trap_ontrap_forgiven = 0;
663*5254Sgavinm int cma_mca_trap_lofault_forgiven = 0;
664*5254Sgavinm 
665*5254Sgavinm /*
666*5254Sgavinm  * Native #MC handler - we branch to here from mcetrap
667*5254Sgavinm  */
668*5254Sgavinm /*ARGSUSED*/
6691414Scindi void
6701414Scindi cmi_mca_trap(struct regs *rp)
6711414Scindi {
672*5254Sgavinm #ifndef	__xpv
673*5254Sgavinm 	cmi_hdl_t hdl = NULL;
674*5254Sgavinm 	uint64_t disp;
675*5254Sgavinm 	cmi_t *cmi;
676*5254Sgavinm 	int s;
677*5254Sgavinm 
678*5254Sgavinm 	if (cmi_no_mca_init != 0)
679*5254Sgavinm 		return;
680*5254Sgavinm 
681*5254Sgavinm 	/*
682*5254Sgavinm 	 * This function can call cmn_err, and the cpu module cmi_mca_trap
683*5254Sgavinm 	 * entry point may also elect to call cmn_err (e.g., if it can't
684*5254Sgavinm 	 * log the error onto an errorq, say very early in boot).
685*5254Sgavinm 	 * We need to let cprintf know that we must not block.
686*5254Sgavinm 	 */
687*5254Sgavinm 	s = spl8();
688*5254Sgavinm 
689*5254Sgavinm 	if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU),
690*5254Sgavinm 	    cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL ||
691*5254Sgavinm 	    (cmi = HDL2CMI(hdl)) == NULL ||
692*5254Sgavinm 	    !CMI_OP_PRESENT(cmi, cmi_mca_trap)) {
693*5254Sgavinm 
694*5254Sgavinm 		cmn_err(CE_WARN, "#MC exception on cpuid %d: %s",
695*5254Sgavinm 		    CPU->cpu_id,
696*5254Sgavinm 		    hdl ? "handle lookup ok but no #MC handler found" :
697*5254Sgavinm 		    "handle lookup failed");
698*5254Sgavinm 
699*5254Sgavinm 		if (hdl != NULL)
700*5254Sgavinm 			cmi_hdl_rele(hdl);
701*5254Sgavinm 
702*5254Sgavinm 		splx(s);
703*5254Sgavinm 		return;
7041414Scindi 	}
705*5254Sgavinm 
706*5254Sgavinm 	disp = CMI_OPS(cmi)->cmi_mca_trap(hdl, rp);
7071414Scindi 
708*5254Sgavinm 	switch (cmi_mce_response(rp, disp)) {
709*5254Sgavinm 	default:
710*5254Sgavinm 		cmn_err(CE_WARN, "Invalid response from cmi_mce_response");
711*5254Sgavinm 		/*FALLTHRU*/
712*5254Sgavinm 
713*5254Sgavinm 	case CMI_RESPONSE_PANIC:
714*5254Sgavinm 		cmi_mca_panic();
715*5254Sgavinm 		break;
716*5254Sgavinm 
717*5254Sgavinm 	case CMI_RESPONSE_NONE:
718*5254Sgavinm 		break;
719*5254Sgavinm 
720*5254Sgavinm 	case CMI_RESPONSE_CKILL:
721*5254Sgavinm 		ttolwp(curthread)->lwp_pcb.pcb_flags |= ASYNC_HWERR;
722*5254Sgavinm 		aston(curthread);
723*5254Sgavinm 		cma_mca_trap_contract_kills++;
724*5254Sgavinm 		break;
7251414Scindi 
726*5254Sgavinm 	case CMI_RESPONSE_ONTRAP_PROT: {
727*5254Sgavinm 		on_trap_data_t *otp = curthread->t_ontrap;
728*5254Sgavinm 		otp->ot_trap = OT_DATA_EC;
729*5254Sgavinm 		rp->r_pc = otp->ot_trampoline;
730*5254Sgavinm 		cma_mca_trap_ontrap_forgiven++;
731*5254Sgavinm 		break;
732*5254Sgavinm 	}
7331414Scindi 
734*5254Sgavinm 	case CMI_RESPONSE_LOFAULT_PROT:
735*5254Sgavinm 		rp->r_r0 = EFAULT;
736*5254Sgavinm 		rp->r_pc = curthread->t_lofault;
737*5254Sgavinm 		cma_mca_trap_lofault_forgiven++;
738*5254Sgavinm 		break;
739*5254Sgavinm 	}
740*5254Sgavinm 
741*5254Sgavinm 	cmi_hdl_rele(hdl);
742*5254Sgavinm 	splx(s);
743*5254Sgavinm #endif	/* __xpv */
7441414Scindi }
7451414Scindi 
7461414Scindi void
747*5254Sgavinm cmi_hdl_poke(cmi_hdl_t hdl)
7481414Scindi {
749*5254Sgavinm 	cmi_t *cmi = HDL2CMI(hdl);
750*5254Sgavinm 
751*5254Sgavinm 	if (!CMI_OP_PRESENT(cmi, cmi_hdl_poke))
752*5254Sgavinm 		return;
753*5254Sgavinm 
754*5254Sgavinm 	CMI_OPS(cmi)->cmi_hdl_poke(hdl);
7551414Scindi }
7561414Scindi 
7571414Scindi void
758*5254Sgavinm cmi_mc_register(cmi_hdl_t hdl, const cmi_mc_ops_t *mcops, void *mcdata)
7591414Scindi {
760*5254Sgavinm 	if (!cmi_no_mca_init)
761*5254Sgavinm 		cmi_hdl_setmc(hdl, mcops, mcdata);
7621414Scindi }
7631414Scindi 
764*5254Sgavinm cmi_errno_t
7653164Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd,
7663164Sgavinm     int syndtype, mc_unum_t *up)
7671414Scindi {
7681414Scindi 	const struct cmi_mc_ops *mcops;
769*5254Sgavinm 	cmi_hdl_t hdl;
770*5254Sgavinm 	cmi_errno_t rv;
771*5254Sgavinm 
772*5254Sgavinm 	if (cmi_no_mca_init ||
773*5254Sgavinm 	    (hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
774*5254Sgavinm 		return (CMIERR_MC_ABSENT);
7751414Scindi 
776*5254Sgavinm 	if ((mcops = cmi_hdl_getmcops(hdl)) == NULL ||
777*5254Sgavinm 	    mcops->cmi_mc_patounum == NULL) {
778*5254Sgavinm 		cmi_hdl_rele(hdl);
779*5254Sgavinm 		return (CMIERR_MC_NOTSUP);
780*5254Sgavinm 	}
7811414Scindi 
782*5254Sgavinm 	rv = mcops->cmi_mc_patounum(cmi_hdl_getmcdata(hdl), pa, valid_hi,
783*5254Sgavinm 	    valid_lo, synd, syndtype, up);
784*5254Sgavinm 
785*5254Sgavinm 	cmi_hdl_rele(hdl);
786*5254Sgavinm 
787*5254Sgavinm 	return (rv);
7881414Scindi }
7891414Scindi 
790*5254Sgavinm cmi_errno_t
7911414Scindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
7921414Scindi {
7931414Scindi 	const struct cmi_mc_ops *mcops;
794*5254Sgavinm 	cmi_hdl_t hdl;
795*5254Sgavinm 	cmi_errno_t rv;
7961414Scindi 
7971414Scindi 	if (up != NULL && nvl != NULL)
798*5254Sgavinm 		return (CMIERR_API);	/* convert from just one form */
799*5254Sgavinm 
800*5254Sgavinm 	if (cmi_no_mca_init ||
801*5254Sgavinm 	    (hdl = cmi_hdl_any()) == NULL)	/* short-term hold */
802*5254Sgavinm 		return (CMIERR_MC_ABSENT);
803*5254Sgavinm 
804*5254Sgavinm 	if ((mcops = cmi_hdl_getmcops(hdl)) == NULL ||
805*5254Sgavinm 	    mcops->cmi_mc_unumtopa == NULL) {
806*5254Sgavinm 		cmi_hdl_rele(hdl);
807*5254Sgavinm 
808*5254Sgavinm 		if (nvl != NULL && nvlist_lookup_uint64(nvl,
809*5254Sgavinm 		    FM_FMRI_MEM_PHYSADDR, pap) == 0) {
810*5254Sgavinm 			return (CMIERR_MC_PARTIALUNUMTOPA);
811*5254Sgavinm 		} else {
812*5254Sgavinm 			return (mcops && mcops->cmi_mc_unumtopa ?
813*5254Sgavinm 			    CMIERR_MC_NOTSUP : CMIERR_MC_ABSENT);
814*5254Sgavinm 		}
815*5254Sgavinm 	}
816*5254Sgavinm 
817*5254Sgavinm 	rv = mcops->cmi_mc_unumtopa(cmi_hdl_getmcdata(hdl), up, nvl, pap);
818*5254Sgavinm 
819*5254Sgavinm 	cmi_hdl_rele(hdl);
820*5254Sgavinm 
821*5254Sgavinm 	return (rv);
822*5254Sgavinm }
8231414Scindi 
824*5254Sgavinm void
825*5254Sgavinm cmi_mc_logout(cmi_hdl_t hdl, boolean_t ismc, boolean_t sync)
826*5254Sgavinm {
827*5254Sgavinm 	const struct cmi_mc_ops *mcops;
828*5254Sgavinm 
829*5254Sgavinm 	if (cmi_no_mca_init || (mcops = cmi_hdl_getmcops(hdl)) == NULL)
830*5254Sgavinm 		return;
831*5254Sgavinm 
832*5254Sgavinm 	if (mcops->cmi_mc_logout != NULL)
833*5254Sgavinm 		mcops->cmi_mc_logout(hdl, ismc, sync);
834*5254Sgavinm }
8351414Scindi 
836*5254Sgavinm cmi_errno_t
837*5254Sgavinm cmi_hdl_msrinject(cmi_hdl_t hdl, cmi_mca_regs_t *regs, uint_t nregs,
838*5254Sgavinm     int force)
839*5254Sgavinm {
840*5254Sgavinm 	cmi_t *cmi = cmi_hdl_getcmi(hdl);
841*5254Sgavinm 
842*5254Sgavinm 	if (!CMI_OP_PRESENT(cmi, cmi_msrinject))
843*5254Sgavinm 		return (CMIERR_NOTSUP);
844*5254Sgavinm 
845*5254Sgavinm 	return (CMI_OPS(cmi)->cmi_msrinject(hdl, regs, nregs, force));
8461414Scindi }
847*5254Sgavinm 
848*5254Sgavinm boolean_t
849*5254Sgavinm cmi_panic_on_ue(void)
850*5254Sgavinm {
851*5254Sgavinm 	return (cmi_panic_on_uncorrectable_error ? B_TRUE : B_FALSE);
852*5254Sgavinm }
853