xref: /onnv-gate/usr/src/uts/i86pc/os/cmi.c (revision 3164:475efbf127ed)
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 /*
231414Scindi  * Copyright 2006 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 
331414Scindi #include <sys/x86_archext.h>
341414Scindi #include <sys/cpu_module_impl.h>
351414Scindi #include <sys/fm/util.h>
361414Scindi #include <sys/reboot.h>
371414Scindi #include <sys/modctl.h>
381414Scindi #include <sys/param.h>
391414Scindi #include <sys/cmn_err.h>
401414Scindi #include <sys/systm.h>
411414Scindi #include <sys/types.h>
421414Scindi 
431414Scindi #define	CPUMOD_SUBDIR	"cpu"
441414Scindi #define	CPUMOD_PREFIX	"cpu"
451414Scindi 
461414Scindi #define	CMI_OPS(cpu) \
471414Scindi 	(cpu)->cpu_m.mcpu_cmi->cmi_ops
481414Scindi #define	CMI_DATA(cpu) \
491414Scindi 	(cpu)->cpu_m.mcpu_cmidata
501414Scindi 
511414Scindi /*
522869Sgavinm  * If cleared for debugging we will not attempt to load a model-specific
532869Sgavinm  * cpu module but will load the generic cpu module instead.
542869Sgavinm  */
552869Sgavinm int cmi_force_generic = 0;
562869Sgavinm 
572869Sgavinm /*
581414Scindi  * If cleared for debugging, we will suppress panicking on fatal hardware
591414Scindi  * errors.  This should *only* be used for debugging; it use can and will
601414Scindi  * cause data corruption if actual hardware errors are detected by the system.
611414Scindi  */
621414Scindi int cmi_panic_on_uncorrectable_error = 1;
631414Scindi 
641414Scindi static cmi_t *cmi_list;
651414Scindi static kmutex_t cmi_load_lock;
661414Scindi 
671414Scindi static int
681414Scindi cmi_cpu_match(cpu_t *c1, cpu_t *c2)
691414Scindi {
701414Scindi 	return (cpuid_getfamily(c1) == cpuid_getfamily(c2) &&
711414Scindi 	    cpuid_getmodel(c1) == cpuid_getmodel(c2) &&
721414Scindi 	    cpuid_getstep(c1) == cpuid_getstep(c2) &&
731414Scindi 	    strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0);
741414Scindi }
751414Scindi 
761414Scindi static cmi_t *
771414Scindi cmi_load_modctl(modctl_t *modp)
781414Scindi {
791414Scindi 	uintptr_t ops;
801414Scindi 	cmi_t *cmi;
811414Scindi 
821414Scindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
831414Scindi 
841414Scindi 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
851414Scindi 		if (cmi->cmi_modp == modp)
861414Scindi 			return (cmi);
871414Scindi 	}
881414Scindi 
891414Scindi 	if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) {
901414Scindi 		cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops "
911414Scindi 		    "found\n", modp->mod_modname);
921414Scindi 		return (NULL);
931414Scindi 	}
941414Scindi 
951414Scindi 	/*
961414Scindi 	 * Hold the module in memory.  We call to CPU modules without using the
971414Scindi 	 * stubs mechanism, so these modules must be manually held in memory.
981414Scindi 	 * The mod_ref acts as if another loaded module has a dependency on us.
991414Scindi 	 */
1001414Scindi 	mutex_enter(&mod_lock);
1011414Scindi 	modp->mod_ref++;
1021414Scindi 	mutex_exit(&mod_lock);
1031414Scindi 
1041414Scindi 	cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP);
1051414Scindi 	cmi->cmi_ops = (const cmi_ops_t *)ops;
1061414Scindi 	cmi->cmi_modp = modp;
1071414Scindi 
1081414Scindi 	cmi->cmi_next = cmi_list;
1091414Scindi 	cmi_list = cmi;
1101414Scindi 
1111414Scindi 	return (cmi);
1121414Scindi }
1131414Scindi 
1141414Scindi static cmi_t *
1151414Scindi cmi_load_module(cpu_t *cp)
1161414Scindi {
1171414Scindi 	modctl_t *modp;
1181414Scindi 	cmi_t *cmi;
1191414Scindi 	int i, modid;
1201414Scindi 	uint_t s[3];
1211414Scindi 
1221414Scindi 	/*
1231414Scindi 	 * Look to see if we've already got a module loaded for a CPU just
1241414Scindi 	 * like this one.  If we do, then we'll re-use it.
1251414Scindi 	 */
1261414Scindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
1271414Scindi 	mutex_enter(&cpu_lock);
1281414Scindi 
1291414Scindi 	for (i = 0; i < NCPU; i++) {
1301414Scindi 		cpu_t *cp2 = cpu[i];
1311414Scindi 
1321414Scindi 		if (cp2 != NULL && cp2 != cp &&
1331414Scindi 		    cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) {
1341414Scindi 			mutex_exit(&cpu_lock);
1351414Scindi 			return (cp2->cpu_m.mcpu_cmi);
1361414Scindi 		}
1371414Scindi 	}
1381414Scindi 
1391414Scindi 	mutex_exit(&cpu_lock);
1401414Scindi 
1411414Scindi 	/*
1421414Scindi 	 * If we can't find a match, attempt to load the appropriate module.
1431414Scindi 	 * If that also fails, try to load the generic CPU module.
1441414Scindi 	 */
1451414Scindi 	s[0] = cpuid_getfamily(cp);
1461414Scindi 	s[1] = cpuid_getmodel(cp);
1471414Scindi 	s[2] = cpuid_getstep(cp);
1481414Scindi 
1491414Scindi 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
1501414Scindi 	    cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0]));
1511414Scindi 
1521414Scindi 	if (modid == -1)
1531414Scindi 		modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic");
1541414Scindi 
1551414Scindi 	if (modid == -1)
1561414Scindi 		return (NULL);
1571414Scindi 
1581414Scindi 	modp = mod_hold_by_id(modid);
1591414Scindi 	cmi = cmi_load_modctl(modp);
1601414Scindi 	mod_release_mod(modp);
1611414Scindi 
1621414Scindi 	return (cmi);
1631414Scindi }
1641414Scindi 
1651414Scindi static cmi_t *
1661414Scindi cmi_load_generic(void)
1671414Scindi {
1681414Scindi 	modctl_t *modp;
1691414Scindi 	cmi_t *cmi;
1701414Scindi 	int modid;
1711414Scindi 
1721414Scindi 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
1731414Scindi 		return (NULL);
1741414Scindi 
1751414Scindi 	modp = mod_hold_by_id(modid);
1761414Scindi 	cmi = cmi_load_modctl(modp);
1771414Scindi 	mod_release_mod(modp);
1781414Scindi 
1791414Scindi 	return (cmi);
1801414Scindi }
1811414Scindi 
1821414Scindi /*
1831414Scindi  * Load a CPU module for the specified CPU, and then call its cmi_init routine.
1841414Scindi  * If the module returns ENOTSUP, try using the generic CPU module instead.
1851414Scindi  * If all else fails, we return -1 and the caller will panic or halt.
1861414Scindi  */
1871414Scindi int
1881414Scindi cmi_load(cpu_t *cp)
1891414Scindi {
1901414Scindi 	int err = ENOENT;
1911414Scindi 	cmi_t *cmi;
1921414Scindi 	void *data;
1931414Scindi 
1941414Scindi 	mutex_enter(&cmi_load_lock);
1951414Scindi 
1962869Sgavinm 	if (!cmi_force_generic && (
1972869Sgavinm 	    ((cmi = cmi_load_module(cp)) == NULL) ||
1982869Sgavinm 	    ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 &&
1992869Sgavinm 	    err != ENOTSUP))) {
2001414Scindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
2011414Scindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
2021414Scindi 		mutex_exit(&cmi_load_lock);
2031414Scindi 		return (-1);
2041414Scindi 	}
2051414Scindi 
2062869Sgavinm 	if ((cmi_force_generic || err != 0) &&
2072869Sgavinm 	    ((cmi = cmi_load_generic()) == NULL ||
2081414Scindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
2091414Scindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
2101414Scindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
2111414Scindi 		mutex_exit(&cmi_load_lock);
2121414Scindi 		return (-1);
2131414Scindi 	}
2141414Scindi 
2151414Scindi 	ASSERT(cp->cpu_m.mcpu_cmi == NULL);
2161414Scindi 	cp->cpu_m.mcpu_cmi = cmi;
2171414Scindi 	cp->cpu_m.mcpu_cmidata = data;
2181414Scindi 
2191414Scindi 	cmi->cmi_refcnt++;
2201414Scindi 	mutex_exit(&cmi_load_lock);
2211414Scindi 
2221414Scindi 	if (boothowto & RB_VERBOSE) {
2231414Scindi 		printf("cpuid %d: initialized cpumod: %s\n",
2241414Scindi 		    cp->cpu_id, cmi->cmi_modp->mod_modname);
2251414Scindi 	}
2261414Scindi 
2271414Scindi 	return (0);
2281414Scindi }
2291414Scindi 
2301414Scindi void
2311414Scindi cmi_init(void)
2321414Scindi {
2331414Scindi 	if (cmi_load(CPU) < 0)
2341414Scindi 		panic("failed to load module for CPU %u", CPU->cpu_id);
2351414Scindi }
2361414Scindi 
2371414Scindi void
2381414Scindi cmi_post_init(void)
2391414Scindi {
2401414Scindi 	CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
2411414Scindi }
2421414Scindi 
2432869Sgavinm /*
2442869Sgavinm  * Called just once from start_other_cpus when all processors are started.
2452869Sgavinm  * This will not be called for each cpu, so the registered op must not
2462869Sgavinm  * assume it is called as such.
2472869Sgavinm  */
2481414Scindi void
2491642Sgavinm cmi_post_mpstartup(void)
2501642Sgavinm {
2511642Sgavinm 	CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU));
2521642Sgavinm }
2531642Sgavinm 
2541642Sgavinm void
2551414Scindi cmi_faulted_enter(cpu_t *cp)
2561414Scindi {
2571414Scindi 	CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp));
2581414Scindi }
2591414Scindi 
2601414Scindi void
2611414Scindi cmi_faulted_exit(cpu_t *cp)
2621414Scindi {
2631414Scindi 	CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp));
2641414Scindi }
2651414Scindi 
2661414Scindi int
2672869Sgavinm cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig)
2681414Scindi {
2692869Sgavinm 	return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen,
2702869Sgavinm 	    cscontig));
2711414Scindi }
2721414Scindi 
2731414Scindi void
2741414Scindi cmi_mca_init(void)
2751414Scindi {
2761414Scindi 	CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU));
2771414Scindi }
2781414Scindi 
2791414Scindi void
2801414Scindi cmi_mca_trap(struct regs *rp)
2811414Scindi {
2821414Scindi 	if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) {
2831414Scindi 		if (cmi_panic_on_uncorrectable_error)
2841414Scindi 			fm_panic("Unrecoverable Machine-Check Exception");
2851414Scindi 		else
2861414Scindi 			cmn_err(CE_WARN, "suppressing panic from fatal #mc");
2871414Scindi 	}
2881414Scindi }
2891414Scindi 
2901414Scindi int
2911414Scindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs)
2921414Scindi {
2931414Scindi 	int err;
2941414Scindi 
2951414Scindi 	kpreempt_disable();
2961414Scindi 	err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs);
2971414Scindi 	kpreempt_enable();
2981414Scindi 
2991414Scindi 	return (err);
3001414Scindi }
3011414Scindi 
3021414Scindi void
3031414Scindi cmi_mca_poke(void)
3041414Scindi {
3051414Scindi 	CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU));
3061414Scindi }
3071414Scindi 
3081414Scindi void
3091414Scindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata)
3101414Scindi {
3111414Scindi 	CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata);
3121414Scindi }
3131414Scindi 
3141414Scindi int
315*3164Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd,
316*3164Sgavinm     int syndtype, mc_unum_t *up)
3171414Scindi {
3181414Scindi 	const struct cmi_mc_ops *mcops;
3191414Scindi 	cpu_t *cp = CPU;
3201414Scindi 
3211414Scindi 	if (CMI_OPS(cp) == NULL ||
3221414Scindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3231414Scindi 		return (-1);	/* not registered yet */
3241414Scindi 
325*3164Sgavinm 	return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, valid_hi, valid_lo,
326*3164Sgavinm 	    synd, syndtype, up));
3271414Scindi }
3281414Scindi 
3291414Scindi int
3301414Scindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
3311414Scindi {
3321414Scindi 	const struct cmi_mc_ops *mcops;
3331414Scindi 	cpu_t *cp = CPU;
3341414Scindi 
3351414Scindi 	if (up != NULL && nvl != NULL)
3361414Scindi 		return (-1);	/* only convert from one or the other form */
3371414Scindi 
3381414Scindi 	if (CMI_OPS(cp) == NULL ||
3391414Scindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3401414Scindi 		return (-1);	/* not registered yet */
3411414Scindi 
3421414Scindi 	return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap));
3431414Scindi }
344