xref: /titanic_51/usr/src/uts/i86pc/os/cmi.c (revision 8a40a695ee676a322b094e9afe5375567bfb51e3)
17aec1d6eScindi /*
23ad553a7Sgavinm  * CDDL HEADER START
33ad553a7Sgavinm  *
43ad553a7Sgavinm  * The contents of this file are subject to the terms of the
53ad553a7Sgavinm  * Common Development and Distribution License (the "License").
63ad553a7Sgavinm  * You may not use this file except in compliance with the License.
73ad553a7Sgavinm  *
83ad553a7Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93ad553a7Sgavinm  * or http://www.opensolaris.org/os/licensing.
103ad553a7Sgavinm  * See the License for the specific language governing permissions
113ad553a7Sgavinm  * and limitations under the License.
123ad553a7Sgavinm  *
133ad553a7Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
143ad553a7Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153ad553a7Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
163ad553a7Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
173ad553a7Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
183ad553a7Sgavinm  *
193ad553a7Sgavinm  * CDDL HEADER END
203ad553a7Sgavinm  */
213ad553a7Sgavinm 
223ad553a7Sgavinm /*
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 /*
52*8a40a695Sgavinm  * If cleared for debugging we will not attempt to load a model-specific
53*8a40a695Sgavinm  * cpu module but will load the generic cpu module instead.
54*8a40a695Sgavinm  */
55*8a40a695Sgavinm int cmi_force_generic = 0;
56*8a40a695Sgavinm 
57*8a40a695Sgavinm /*
587aec1d6eScindi  * If cleared for debugging, we will suppress panicking on fatal hardware
597aec1d6eScindi  * errors.  This should *only* be used for debugging; it use can and will
607aec1d6eScindi  * cause data corruption if actual hardware errors are detected by the system.
617aec1d6eScindi  */
627aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1;
637aec1d6eScindi 
647aec1d6eScindi static cmi_t *cmi_list;
657aec1d6eScindi static kmutex_t cmi_load_lock;
667aec1d6eScindi 
677aec1d6eScindi static int
687aec1d6eScindi cmi_cpu_match(cpu_t *c1, cpu_t *c2)
697aec1d6eScindi {
707aec1d6eScindi 	return (cpuid_getfamily(c1) == cpuid_getfamily(c2) &&
717aec1d6eScindi 	    cpuid_getmodel(c1) == cpuid_getmodel(c2) &&
727aec1d6eScindi 	    cpuid_getstep(c1) == cpuid_getstep(c2) &&
737aec1d6eScindi 	    strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0);
747aec1d6eScindi }
757aec1d6eScindi 
767aec1d6eScindi static cmi_t *
777aec1d6eScindi cmi_load_modctl(modctl_t *modp)
787aec1d6eScindi {
797aec1d6eScindi 	uintptr_t ops;
807aec1d6eScindi 	cmi_t *cmi;
817aec1d6eScindi 
827aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
837aec1d6eScindi 
847aec1d6eScindi 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
857aec1d6eScindi 		if (cmi->cmi_modp == modp)
867aec1d6eScindi 			return (cmi);
877aec1d6eScindi 	}
887aec1d6eScindi 
897aec1d6eScindi 	if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) {
907aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops "
917aec1d6eScindi 		    "found\n", modp->mod_modname);
927aec1d6eScindi 		return (NULL);
937aec1d6eScindi 	}
947aec1d6eScindi 
957aec1d6eScindi 	/*
967aec1d6eScindi 	 * Hold the module in memory.  We call to CPU modules without using the
977aec1d6eScindi 	 * stubs mechanism, so these modules must be manually held in memory.
987aec1d6eScindi 	 * The mod_ref acts as if another loaded module has a dependency on us.
997aec1d6eScindi 	 */
1007aec1d6eScindi 	mutex_enter(&mod_lock);
1017aec1d6eScindi 	modp->mod_ref++;
1027aec1d6eScindi 	mutex_exit(&mod_lock);
1037aec1d6eScindi 
1047aec1d6eScindi 	cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP);
1057aec1d6eScindi 	cmi->cmi_ops = (const cmi_ops_t *)ops;
1067aec1d6eScindi 	cmi->cmi_modp = modp;
1077aec1d6eScindi 
1087aec1d6eScindi 	cmi->cmi_next = cmi_list;
1097aec1d6eScindi 	cmi_list = cmi;
1107aec1d6eScindi 
1117aec1d6eScindi 	return (cmi);
1127aec1d6eScindi }
1137aec1d6eScindi 
1147aec1d6eScindi static cmi_t *
1157aec1d6eScindi cmi_load_module(cpu_t *cp)
1167aec1d6eScindi {
1177aec1d6eScindi 	modctl_t *modp;
1187aec1d6eScindi 	cmi_t *cmi;
1197aec1d6eScindi 	int i, modid;
1207aec1d6eScindi 	uint_t s[3];
1217aec1d6eScindi 
1227aec1d6eScindi 	/*
1237aec1d6eScindi 	 * Look to see if we've already got a module loaded for a CPU just
1247aec1d6eScindi 	 * like this one.  If we do, then we'll re-use it.
1257aec1d6eScindi 	 */
1267aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
1277aec1d6eScindi 	mutex_enter(&cpu_lock);
1287aec1d6eScindi 
1297aec1d6eScindi 	for (i = 0; i < NCPU; i++) {
1307aec1d6eScindi 		cpu_t *cp2 = cpu[i];
1317aec1d6eScindi 
1327aec1d6eScindi 		if (cp2 != NULL && cp2 != cp &&
1337aec1d6eScindi 		    cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) {
1347aec1d6eScindi 			mutex_exit(&cpu_lock);
1357aec1d6eScindi 			return (cp2->cpu_m.mcpu_cmi);
1367aec1d6eScindi 		}
1377aec1d6eScindi 	}
1387aec1d6eScindi 
1397aec1d6eScindi 	mutex_exit(&cpu_lock);
1407aec1d6eScindi 
1417aec1d6eScindi 	/*
1427aec1d6eScindi 	 * If we can't find a match, attempt to load the appropriate module.
1437aec1d6eScindi 	 * If that also fails, try to load the generic CPU module.
1447aec1d6eScindi 	 */
1457aec1d6eScindi 	s[0] = cpuid_getfamily(cp);
1467aec1d6eScindi 	s[1] = cpuid_getmodel(cp);
1477aec1d6eScindi 	s[2] = cpuid_getstep(cp);
1487aec1d6eScindi 
1497aec1d6eScindi 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
1507aec1d6eScindi 	    cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0]));
1517aec1d6eScindi 
1527aec1d6eScindi 	if (modid == -1)
1537aec1d6eScindi 		modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic");
1547aec1d6eScindi 
1557aec1d6eScindi 	if (modid == -1)
1567aec1d6eScindi 		return (NULL);
1577aec1d6eScindi 
1587aec1d6eScindi 	modp = mod_hold_by_id(modid);
1597aec1d6eScindi 	cmi = cmi_load_modctl(modp);
1607aec1d6eScindi 	mod_release_mod(modp);
1617aec1d6eScindi 
1627aec1d6eScindi 	return (cmi);
1637aec1d6eScindi }
1647aec1d6eScindi 
1657aec1d6eScindi static cmi_t *
1667aec1d6eScindi cmi_load_generic(void)
1677aec1d6eScindi {
1687aec1d6eScindi 	modctl_t *modp;
1697aec1d6eScindi 	cmi_t *cmi;
1707aec1d6eScindi 	int modid;
1717aec1d6eScindi 
1727aec1d6eScindi 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
1737aec1d6eScindi 		return (NULL);
1747aec1d6eScindi 
1757aec1d6eScindi 	modp = mod_hold_by_id(modid);
1767aec1d6eScindi 	cmi = cmi_load_modctl(modp);
1777aec1d6eScindi 	mod_release_mod(modp);
1787aec1d6eScindi 
1797aec1d6eScindi 	return (cmi);
1807aec1d6eScindi }
1817aec1d6eScindi 
1827aec1d6eScindi /*
1837aec1d6eScindi  * Load a CPU module for the specified CPU, and then call its cmi_init routine.
1847aec1d6eScindi  * If the module returns ENOTSUP, try using the generic CPU module instead.
1857aec1d6eScindi  * If all else fails, we return -1 and the caller will panic or halt.
1867aec1d6eScindi  */
1877aec1d6eScindi int
1887aec1d6eScindi cmi_load(cpu_t *cp)
1897aec1d6eScindi {
1907aec1d6eScindi 	int err = ENOENT;
1917aec1d6eScindi 	cmi_t *cmi;
1927aec1d6eScindi 	void *data;
1937aec1d6eScindi 
1947aec1d6eScindi 	mutex_enter(&cmi_load_lock);
1957aec1d6eScindi 
196*8a40a695Sgavinm 	if (!cmi_force_generic && (
197*8a40a695Sgavinm 	    ((cmi = cmi_load_module(cp)) == NULL) ||
198*8a40a695Sgavinm 	    ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 &&
199*8a40a695Sgavinm 	    err != ENOTSUP))) {
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 
206*8a40a695Sgavinm 	if ((cmi_force_generic || err != 0) &&
207*8a40a695Sgavinm 	    ((cmi = cmi_load_generic()) == NULL ||
2087aec1d6eScindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
2097aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
2107aec1d6eScindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
2117aec1d6eScindi 		mutex_exit(&cmi_load_lock);
2127aec1d6eScindi 		return (-1);
2137aec1d6eScindi 	}
2147aec1d6eScindi 
2157aec1d6eScindi 	ASSERT(cp->cpu_m.mcpu_cmi == NULL);
2167aec1d6eScindi 	cp->cpu_m.mcpu_cmi = cmi;
2177aec1d6eScindi 	cp->cpu_m.mcpu_cmidata = data;
2187aec1d6eScindi 
2197aec1d6eScindi 	cmi->cmi_refcnt++;
2207aec1d6eScindi 	mutex_exit(&cmi_load_lock);
2217aec1d6eScindi 
2227aec1d6eScindi 	if (boothowto & RB_VERBOSE) {
2237aec1d6eScindi 		printf("cpuid %d: initialized cpumod: %s\n",
2247aec1d6eScindi 		    cp->cpu_id, cmi->cmi_modp->mod_modname);
2257aec1d6eScindi 	}
2267aec1d6eScindi 
2277aec1d6eScindi 	return (0);
2287aec1d6eScindi }
2297aec1d6eScindi 
2307aec1d6eScindi void
2317aec1d6eScindi cmi_init(void)
2327aec1d6eScindi {
2337aec1d6eScindi 	if (cmi_load(CPU) < 0)
2347aec1d6eScindi 		panic("failed to load module for CPU %u", CPU->cpu_id);
2357aec1d6eScindi }
2367aec1d6eScindi 
2377aec1d6eScindi void
2387aec1d6eScindi cmi_post_init(void)
2397aec1d6eScindi {
2407aec1d6eScindi 	CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
2417aec1d6eScindi }
2427aec1d6eScindi 
243*8a40a695Sgavinm /*
244*8a40a695Sgavinm  * Called just once from start_other_cpus when all processors are started.
245*8a40a695Sgavinm  * This will not be called for each cpu, so the registered op must not
246*8a40a695Sgavinm  * assume it is called as such.
247*8a40a695Sgavinm  */
2487aec1d6eScindi void
2493ad553a7Sgavinm cmi_post_mpstartup(void)
2503ad553a7Sgavinm {
2513ad553a7Sgavinm 	CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU));
2523ad553a7Sgavinm }
2533ad553a7Sgavinm 
2543ad553a7Sgavinm void
2557aec1d6eScindi cmi_faulted_enter(cpu_t *cp)
2567aec1d6eScindi {
2577aec1d6eScindi 	CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp));
2587aec1d6eScindi }
2597aec1d6eScindi 
2607aec1d6eScindi void
2617aec1d6eScindi cmi_faulted_exit(cpu_t *cp)
2627aec1d6eScindi {
2637aec1d6eScindi 	CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp));
2647aec1d6eScindi }
2657aec1d6eScindi 
2667aec1d6eScindi int
267*8a40a695Sgavinm cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig)
2687aec1d6eScindi {
269*8a40a695Sgavinm 	return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen,
270*8a40a695Sgavinm 	    cscontig));
2717aec1d6eScindi }
2727aec1d6eScindi 
2737aec1d6eScindi void
2747aec1d6eScindi cmi_mca_init(void)
2757aec1d6eScindi {
2767aec1d6eScindi 	CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU));
2777aec1d6eScindi }
2787aec1d6eScindi 
2797aec1d6eScindi void
2807aec1d6eScindi cmi_mca_trap(struct regs *rp)
2817aec1d6eScindi {
2827aec1d6eScindi 	if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) {
2837aec1d6eScindi 		if (cmi_panic_on_uncorrectable_error)
2847aec1d6eScindi 			fm_panic("Unrecoverable Machine-Check Exception");
2857aec1d6eScindi 		else
2867aec1d6eScindi 			cmn_err(CE_WARN, "suppressing panic from fatal #mc");
2877aec1d6eScindi 	}
2887aec1d6eScindi }
2897aec1d6eScindi 
2907aec1d6eScindi int
2917aec1d6eScindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs)
2927aec1d6eScindi {
2937aec1d6eScindi 	int err;
2947aec1d6eScindi 
2957aec1d6eScindi 	kpreempt_disable();
2967aec1d6eScindi 	err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs);
2977aec1d6eScindi 	kpreempt_enable();
2987aec1d6eScindi 
2997aec1d6eScindi 	return (err);
3007aec1d6eScindi }
3017aec1d6eScindi 
3027aec1d6eScindi void
3037aec1d6eScindi cmi_mca_poke(void)
3047aec1d6eScindi {
3057aec1d6eScindi 	CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU));
3067aec1d6eScindi }
3077aec1d6eScindi 
3087aec1d6eScindi void
3097aec1d6eScindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata)
3107aec1d6eScindi {
3117aec1d6eScindi 	CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata);
3127aec1d6eScindi }
3137aec1d6eScindi 
3147aec1d6eScindi int
3157aec1d6eScindi cmi_mc_patounum(uint64_t pa, uint32_t synd, int syndtype, mc_unum_t *up)
3167aec1d6eScindi {
3177aec1d6eScindi 	const struct cmi_mc_ops *mcops;
3187aec1d6eScindi 	cpu_t *cp = CPU;
3197aec1d6eScindi 
3207aec1d6eScindi 	if (CMI_OPS(cp) == NULL ||
3217aec1d6eScindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3227aec1d6eScindi 		return (-1);	/* not registered yet */
3237aec1d6eScindi 
3247aec1d6eScindi 	return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, synd, syndtype, up));
3257aec1d6eScindi }
3267aec1d6eScindi 
3277aec1d6eScindi int
3287aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
3297aec1d6eScindi {
3307aec1d6eScindi 	const struct cmi_mc_ops *mcops;
3317aec1d6eScindi 	cpu_t *cp = CPU;
3327aec1d6eScindi 
3337aec1d6eScindi 	if (up != NULL && nvl != NULL)
3347aec1d6eScindi 		return (-1);	/* only convert from one or the other form */
3357aec1d6eScindi 
3367aec1d6eScindi 	if (CMI_OPS(cp) == NULL ||
3377aec1d6eScindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3387aec1d6eScindi 		return (-1);	/* not registered yet */
3397aec1d6eScindi 
3407aec1d6eScindi 	return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap));
3417aec1d6eScindi }
342