xref: /illumos-gate/usr/src/uts/i86pc/os/cmi.c (revision 3ad553a7dabf3c8bcb69dd1ceeb13938fa526aed)
1*3ad553a7Sgavinm /*
2*3ad553a7Sgavinm  * CDDL HEADER START
3*3ad553a7Sgavinm  *
4*3ad553a7Sgavinm  * The contents of this file are subject to the terms of the
5*3ad553a7Sgavinm  * Common Development and Distribution License (the "License").
6*3ad553a7Sgavinm  * You may not use this file except in compliance with the License.
7*3ad553a7Sgavinm  *
8*3ad553a7Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3ad553a7Sgavinm  * or http://www.opensolaris.org/os/licensing.
10*3ad553a7Sgavinm  * See the License for the specific language governing permissions
11*3ad553a7Sgavinm  * and limitations under the License.
12*3ad553a7Sgavinm  *
13*3ad553a7Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
14*3ad553a7Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3ad553a7Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
16*3ad553a7Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
17*3ad553a7Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3ad553a7Sgavinm  *
19*3ad553a7Sgavinm  * CDDL HEADER END
20*3ad553a7Sgavinm  */
21*3ad553a7Sgavinm 
227aec1d6eScindi /*
237aec1d6eScindi  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247aec1d6eScindi  * Use is subject to license terms.
257aec1d6eScindi  */
267aec1d6eScindi 
277aec1d6eScindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
287aec1d6eScindi 
297aec1d6eScindi /*
307aec1d6eScindi  * Public interface to routines implemented by CPU modules
317aec1d6eScindi  */
327aec1d6eScindi 
337aec1d6eScindi #include <sys/x86_archext.h>
347aec1d6eScindi #include <sys/cpu_module_impl.h>
357aec1d6eScindi #include <sys/fm/util.h>
367aec1d6eScindi #include <sys/reboot.h>
377aec1d6eScindi #include <sys/modctl.h>
387aec1d6eScindi #include <sys/param.h>
397aec1d6eScindi #include <sys/cmn_err.h>
407aec1d6eScindi #include <sys/systm.h>
417aec1d6eScindi #include <sys/types.h>
427aec1d6eScindi 
437aec1d6eScindi #define	CPUMOD_SUBDIR	"cpu"
447aec1d6eScindi #define	CPUMOD_PREFIX	"cpu"
457aec1d6eScindi 
467aec1d6eScindi #define	CMI_OPS(cpu) \
477aec1d6eScindi 	(cpu)->cpu_m.mcpu_cmi->cmi_ops
487aec1d6eScindi #define	CMI_DATA(cpu) \
497aec1d6eScindi 	(cpu)->cpu_m.mcpu_cmidata
507aec1d6eScindi 
517aec1d6eScindi /*
527aec1d6eScindi  * If cleared for debugging, we will suppress panicking on fatal hardware
537aec1d6eScindi  * errors.  This should *only* be used for debugging; it use can and will
547aec1d6eScindi  * cause data corruption if actual hardware errors are detected by the system.
557aec1d6eScindi  */
567aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1;
577aec1d6eScindi 
587aec1d6eScindi static cmi_t *cmi_list;
597aec1d6eScindi static kmutex_t cmi_load_lock;
607aec1d6eScindi 
617aec1d6eScindi static int
627aec1d6eScindi cmi_cpu_match(cpu_t *c1, cpu_t *c2)
637aec1d6eScindi {
647aec1d6eScindi 	return (cpuid_getfamily(c1) == cpuid_getfamily(c2) &&
657aec1d6eScindi 	    cpuid_getmodel(c1) == cpuid_getmodel(c2) &&
667aec1d6eScindi 	    cpuid_getstep(c1) == cpuid_getstep(c2) &&
677aec1d6eScindi 	    strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0);
687aec1d6eScindi }
697aec1d6eScindi 
707aec1d6eScindi static cmi_t *
717aec1d6eScindi cmi_load_modctl(modctl_t *modp)
727aec1d6eScindi {
737aec1d6eScindi 	uintptr_t ops;
747aec1d6eScindi 	cmi_t *cmi;
757aec1d6eScindi 
767aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
777aec1d6eScindi 
787aec1d6eScindi 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
797aec1d6eScindi 		if (cmi->cmi_modp == modp)
807aec1d6eScindi 			return (cmi);
817aec1d6eScindi 	}
827aec1d6eScindi 
837aec1d6eScindi 	if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) {
847aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops "
857aec1d6eScindi 		    "found\n", modp->mod_modname);
867aec1d6eScindi 		return (NULL);
877aec1d6eScindi 	}
887aec1d6eScindi 
897aec1d6eScindi 	/*
907aec1d6eScindi 	 * Hold the module in memory.  We call to CPU modules without using the
917aec1d6eScindi 	 * stubs mechanism, so these modules must be manually held in memory.
927aec1d6eScindi 	 * The mod_ref acts as if another loaded module has a dependency on us.
937aec1d6eScindi 	 */
947aec1d6eScindi 	mutex_enter(&mod_lock);
957aec1d6eScindi 	modp->mod_ref++;
967aec1d6eScindi 	mutex_exit(&mod_lock);
977aec1d6eScindi 
987aec1d6eScindi 	cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP);
997aec1d6eScindi 	cmi->cmi_ops = (const cmi_ops_t *)ops;
1007aec1d6eScindi 	cmi->cmi_modp = modp;
1017aec1d6eScindi 
1027aec1d6eScindi 	cmi->cmi_next = cmi_list;
1037aec1d6eScindi 	cmi_list = cmi;
1047aec1d6eScindi 
1057aec1d6eScindi 	return (cmi);
1067aec1d6eScindi }
1077aec1d6eScindi 
1087aec1d6eScindi static cmi_t *
1097aec1d6eScindi cmi_load_module(cpu_t *cp)
1107aec1d6eScindi {
1117aec1d6eScindi 	modctl_t *modp;
1127aec1d6eScindi 	cmi_t *cmi;
1137aec1d6eScindi 	int i, modid;
1147aec1d6eScindi 	uint_t s[3];
1157aec1d6eScindi 
1167aec1d6eScindi 	/*
1177aec1d6eScindi 	 * Look to see if we've already got a module loaded for a CPU just
1187aec1d6eScindi 	 * like this one.  If we do, then we'll re-use it.
1197aec1d6eScindi 	 */
1207aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
1217aec1d6eScindi 	mutex_enter(&cpu_lock);
1227aec1d6eScindi 
1237aec1d6eScindi 	for (i = 0; i < NCPU; i++) {
1247aec1d6eScindi 		cpu_t *cp2 = cpu[i];
1257aec1d6eScindi 
1267aec1d6eScindi 		if (cp2 != NULL && cp2 != cp &&
1277aec1d6eScindi 		    cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) {
1287aec1d6eScindi 			mutex_exit(&cpu_lock);
1297aec1d6eScindi 			return (cp2->cpu_m.mcpu_cmi);
1307aec1d6eScindi 		}
1317aec1d6eScindi 	}
1327aec1d6eScindi 
1337aec1d6eScindi 	mutex_exit(&cpu_lock);
1347aec1d6eScindi 
1357aec1d6eScindi 	/*
1367aec1d6eScindi 	 * If we can't find a match, attempt to load the appropriate module.
1377aec1d6eScindi 	 * If that also fails, try to load the generic CPU module.
1387aec1d6eScindi 	 */
1397aec1d6eScindi 	s[0] = cpuid_getfamily(cp);
1407aec1d6eScindi 	s[1] = cpuid_getmodel(cp);
1417aec1d6eScindi 	s[2] = cpuid_getstep(cp);
1427aec1d6eScindi 
1437aec1d6eScindi 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
1447aec1d6eScindi 	    cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0]));
1457aec1d6eScindi 
1467aec1d6eScindi 	if (modid == -1)
1477aec1d6eScindi 		modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic");
1487aec1d6eScindi 
1497aec1d6eScindi 	if (modid == -1)
1507aec1d6eScindi 		return (NULL);
1517aec1d6eScindi 
1527aec1d6eScindi 	modp = mod_hold_by_id(modid);
1537aec1d6eScindi 	cmi = cmi_load_modctl(modp);
1547aec1d6eScindi 	mod_release_mod(modp);
1557aec1d6eScindi 
1567aec1d6eScindi 	return (cmi);
1577aec1d6eScindi }
1587aec1d6eScindi 
1597aec1d6eScindi static cmi_t *
1607aec1d6eScindi cmi_load_generic(void)
1617aec1d6eScindi {
1627aec1d6eScindi 	modctl_t *modp;
1637aec1d6eScindi 	cmi_t *cmi;
1647aec1d6eScindi 	int modid;
1657aec1d6eScindi 
1667aec1d6eScindi 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
1677aec1d6eScindi 		return (NULL);
1687aec1d6eScindi 
1697aec1d6eScindi 	modp = mod_hold_by_id(modid);
1707aec1d6eScindi 	cmi = cmi_load_modctl(modp);
1717aec1d6eScindi 	mod_release_mod(modp);
1727aec1d6eScindi 
1737aec1d6eScindi 	return (cmi);
1747aec1d6eScindi }
1757aec1d6eScindi 
1767aec1d6eScindi /*
1777aec1d6eScindi  * Load a CPU module for the specified CPU, and then call its cmi_init routine.
1787aec1d6eScindi  * If the module returns ENOTSUP, try using the generic CPU module instead.
1797aec1d6eScindi  * If all else fails, we return -1 and the caller will panic or halt.
1807aec1d6eScindi  */
1817aec1d6eScindi int
1827aec1d6eScindi cmi_load(cpu_t *cp)
1837aec1d6eScindi {
1847aec1d6eScindi 	int err = ENOENT;
1857aec1d6eScindi 	cmi_t *cmi;
1867aec1d6eScindi 	void *data;
1877aec1d6eScindi 
1887aec1d6eScindi 	mutex_enter(&cmi_load_lock);
1897aec1d6eScindi 
1907aec1d6eScindi 	if ((cmi = cmi_load_module(cp)) == NULL || (
1917aec1d6eScindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && err != ENOTSUP)) {
1927aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
1937aec1d6eScindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
1947aec1d6eScindi 		mutex_exit(&cmi_load_lock);
1957aec1d6eScindi 		return (-1);
1967aec1d6eScindi 	}
1977aec1d6eScindi 
1987aec1d6eScindi 	if (err != 0 && ((cmi = cmi_load_generic()) == NULL ||
1997aec1d6eScindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
2007aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
2017aec1d6eScindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
2027aec1d6eScindi 		mutex_exit(&cmi_load_lock);
2037aec1d6eScindi 		return (-1);
2047aec1d6eScindi 	}
2057aec1d6eScindi 
2067aec1d6eScindi 	ASSERT(cp->cpu_m.mcpu_cmi == NULL);
2077aec1d6eScindi 	cp->cpu_m.mcpu_cmi = cmi;
2087aec1d6eScindi 	cp->cpu_m.mcpu_cmidata = data;
2097aec1d6eScindi 
2107aec1d6eScindi 	cmi->cmi_refcnt++;
2117aec1d6eScindi 	mutex_exit(&cmi_load_lock);
2127aec1d6eScindi 
2137aec1d6eScindi 	if (boothowto & RB_VERBOSE) {
2147aec1d6eScindi 		printf("cpuid %d: initialized cpumod: %s\n",
2157aec1d6eScindi 		    cp->cpu_id, cmi->cmi_modp->mod_modname);
2167aec1d6eScindi 	}
2177aec1d6eScindi 
2187aec1d6eScindi 	return (0);
2197aec1d6eScindi }
2207aec1d6eScindi 
2217aec1d6eScindi void
2227aec1d6eScindi cmi_init(void)
2237aec1d6eScindi {
2247aec1d6eScindi 	if (cmi_load(CPU) < 0)
2257aec1d6eScindi 		panic("failed to load module for CPU %u", CPU->cpu_id);
2267aec1d6eScindi }
2277aec1d6eScindi 
2287aec1d6eScindi void
2297aec1d6eScindi cmi_post_init(void)
2307aec1d6eScindi {
2317aec1d6eScindi 	CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
2327aec1d6eScindi }
2337aec1d6eScindi 
234*3ad553a7Sgavinm void
235*3ad553a7Sgavinm cmi_post_mpstartup(void)
236*3ad553a7Sgavinm {
237*3ad553a7Sgavinm 	CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU));
238*3ad553a7Sgavinm }
239*3ad553a7Sgavinm 
2407aec1d6eScindi void
2417aec1d6eScindi cmi_faulted_enter(cpu_t *cp)
2427aec1d6eScindi {
2437aec1d6eScindi 	CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp));
2447aec1d6eScindi }
2457aec1d6eScindi 
2467aec1d6eScindi void
2477aec1d6eScindi cmi_faulted_exit(cpu_t *cp)
2487aec1d6eScindi {
2497aec1d6eScindi 	CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp));
2507aec1d6eScindi }
2517aec1d6eScindi 
2527aec1d6eScindi int
2537aec1d6eScindi cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen)
2547aec1d6eScindi {
2557aec1d6eScindi 	return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen));
2567aec1d6eScindi }
2577aec1d6eScindi 
2587aec1d6eScindi void
2597aec1d6eScindi cmi_mca_init(void)
2607aec1d6eScindi {
2617aec1d6eScindi 	CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU));
2627aec1d6eScindi }
2637aec1d6eScindi 
2647aec1d6eScindi void
2657aec1d6eScindi cmi_mca_trap(struct regs *rp)
2667aec1d6eScindi {
2677aec1d6eScindi 	if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) {
2687aec1d6eScindi 		if (cmi_panic_on_uncorrectable_error)
2697aec1d6eScindi 			fm_panic("Unrecoverable Machine-Check Exception");
2707aec1d6eScindi 		else
2717aec1d6eScindi 			cmn_err(CE_WARN, "suppressing panic from fatal #mc");
2727aec1d6eScindi 	}
2737aec1d6eScindi }
2747aec1d6eScindi 
2757aec1d6eScindi int
2767aec1d6eScindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs)
2777aec1d6eScindi {
2787aec1d6eScindi 	int err;
2797aec1d6eScindi 
2807aec1d6eScindi 	kpreempt_disable();
2817aec1d6eScindi 	err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs);
2827aec1d6eScindi 	kpreempt_enable();
2837aec1d6eScindi 
2847aec1d6eScindi 	return (err);
2857aec1d6eScindi }
2867aec1d6eScindi 
2877aec1d6eScindi void
2887aec1d6eScindi cmi_mca_poke(void)
2897aec1d6eScindi {
2907aec1d6eScindi 	CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU));
2917aec1d6eScindi }
2927aec1d6eScindi 
2937aec1d6eScindi void
2947aec1d6eScindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata)
2957aec1d6eScindi {
2967aec1d6eScindi 	CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata);
2977aec1d6eScindi }
2987aec1d6eScindi 
2997aec1d6eScindi int
3007aec1d6eScindi cmi_mc_patounum(uint64_t pa, uint32_t synd, int syndtype, mc_unum_t *up)
3017aec1d6eScindi {
3027aec1d6eScindi 	const struct cmi_mc_ops *mcops;
3037aec1d6eScindi 	cpu_t *cp = CPU;
3047aec1d6eScindi 
3057aec1d6eScindi 	if (CMI_OPS(cp) == NULL ||
3067aec1d6eScindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3077aec1d6eScindi 		return (-1);	/* not registered yet */
3087aec1d6eScindi 
3097aec1d6eScindi 	return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, synd, syndtype, up));
3107aec1d6eScindi }
3117aec1d6eScindi 
3127aec1d6eScindi int
3137aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
3147aec1d6eScindi {
3157aec1d6eScindi 	const struct cmi_mc_ops *mcops;
3167aec1d6eScindi 	cpu_t *cp = CPU;
3177aec1d6eScindi 
3187aec1d6eScindi 	if (up != NULL && nvl != NULL)
3197aec1d6eScindi 		return (-1);	/* only convert from one or the other form */
3207aec1d6eScindi 
3217aec1d6eScindi 	if (CMI_OPS(cp) == NULL ||
3227aec1d6eScindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3237aec1d6eScindi 		return (-1);	/* not registered yet */
3247aec1d6eScindi 
3257aec1d6eScindi 	return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap));
3267aec1d6eScindi }
327