xref: /onnv-gate/usr/src/uts/i86pc/os/cmi.c (revision 3446:5903aece022d)
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 /*
23*3446Smrj  * 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 
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) {
90*3446Smrj 		cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops "
91*3446Smrj 		    "found", 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))) {
200*3446Smrj 		cmn_err(CE_WARN,
201*3446Smrj 		    "cpu%d: failed to init cpu module '%s': err=%d",
202*3446Smrj 		    cp->cpu_id, cmi ? cmi->cmi_modp->mod_modname : "<>", err);
2031414Scindi 		mutex_exit(&cmi_load_lock);
2041414Scindi 		return (-1);
2051414Scindi 	}
2061414Scindi 
2072869Sgavinm 	if ((cmi_force_generic || err != 0) &&
2082869Sgavinm 	    ((cmi = cmi_load_generic()) == NULL ||
2091414Scindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
210*3446Smrj 		cmn_err(CE_WARN,
211*3446Smrj 		    "cpu%d: failed to init cpu module '%s': err=%d",
212*3446Smrj 		    cp->cpu_id, cmi ? cmi->cmi_modp->mod_modname : "<>", err);
2131414Scindi 		mutex_exit(&cmi_load_lock);
2141414Scindi 		return (-1);
2151414Scindi 	}
2161414Scindi 
2171414Scindi 	ASSERT(cp->cpu_m.mcpu_cmi == NULL);
2181414Scindi 	cp->cpu_m.mcpu_cmi = cmi;
2191414Scindi 	cp->cpu_m.mcpu_cmidata = data;
2201414Scindi 
2211414Scindi 	cmi->cmi_refcnt++;
2221414Scindi 	mutex_exit(&cmi_load_lock);
2231414Scindi 
2241414Scindi 	if (boothowto & RB_VERBOSE) {
225*3446Smrj 		printf("cpu%d: initialized cpu module '%s'\n",
2261414Scindi 		    cp->cpu_id, cmi->cmi_modp->mod_modname);
2271414Scindi 	}
2281414Scindi 
2291414Scindi 	return (0);
2301414Scindi }
2311414Scindi 
2321414Scindi void
2331414Scindi cmi_init(void)
2341414Scindi {
2351414Scindi 	if (cmi_load(CPU) < 0)
236*3446Smrj 		panic("failed to load module for cpu%d", CPU->cpu_id);
2371414Scindi }
2381414Scindi 
2391414Scindi void
2401414Scindi cmi_post_init(void)
2411414Scindi {
2421414Scindi 	CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
2431414Scindi }
2441414Scindi 
2452869Sgavinm /*
2462869Sgavinm  * Called just once from start_other_cpus when all processors are started.
2472869Sgavinm  * This will not be called for each cpu, so the registered op must not
2482869Sgavinm  * assume it is called as such.
2492869Sgavinm  */
2501414Scindi void
2511642Sgavinm cmi_post_mpstartup(void)
2521642Sgavinm {
2531642Sgavinm 	CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU));
2541642Sgavinm }
2551642Sgavinm 
2561642Sgavinm void
2571414Scindi cmi_faulted_enter(cpu_t *cp)
2581414Scindi {
2591414Scindi 	CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp));
2601414Scindi }
2611414Scindi 
2621414Scindi void
2631414Scindi cmi_faulted_exit(cpu_t *cp)
2641414Scindi {
2651414Scindi 	CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp));
2661414Scindi }
2671414Scindi 
2681414Scindi int
2692869Sgavinm cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig)
2701414Scindi {
2712869Sgavinm 	return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen,
2722869Sgavinm 	    cscontig));
2731414Scindi }
2741414Scindi 
2751414Scindi void
2761414Scindi cmi_mca_init(void)
2771414Scindi {
2781414Scindi 	CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU));
2791414Scindi }
2801414Scindi 
2811414Scindi void
2821414Scindi cmi_mca_trap(struct regs *rp)
2831414Scindi {
2841414Scindi 	if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) {
2851414Scindi 		if (cmi_panic_on_uncorrectable_error)
2861414Scindi 			fm_panic("Unrecoverable Machine-Check Exception");
2871414Scindi 		else
2881414Scindi 			cmn_err(CE_WARN, "suppressing panic from fatal #mc");
2891414Scindi 	}
2901414Scindi }
2911414Scindi 
2921414Scindi int
2931414Scindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs)
2941414Scindi {
2951414Scindi 	int err;
2961414Scindi 
2971414Scindi 	kpreempt_disable();
2981414Scindi 	err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs);
2991414Scindi 	kpreempt_enable();
3001414Scindi 
3011414Scindi 	return (err);
3021414Scindi }
3031414Scindi 
3041414Scindi void
3051414Scindi cmi_mca_poke(void)
3061414Scindi {
3071414Scindi 	CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU));
3081414Scindi }
3091414Scindi 
3101414Scindi void
3111414Scindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata)
3121414Scindi {
3131414Scindi 	CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata);
3141414Scindi }
3151414Scindi 
3161414Scindi int
3173164Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd,
3183164Sgavinm     int syndtype, mc_unum_t *up)
3191414Scindi {
3201414Scindi 	const struct cmi_mc_ops *mcops;
3211414Scindi 	cpu_t *cp = CPU;
3221414Scindi 
3231414Scindi 	if (CMI_OPS(cp) == NULL ||
3241414Scindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3251414Scindi 		return (-1);	/* not registered yet */
3261414Scindi 
3273164Sgavinm 	return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, valid_hi, valid_lo,
3283164Sgavinm 	    synd, syndtype, up));
3291414Scindi }
3301414Scindi 
3311414Scindi int
3321414Scindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
3331414Scindi {
3341414Scindi 	const struct cmi_mc_ops *mcops;
3351414Scindi 	cpu_t *cp = CPU;
3361414Scindi 
3371414Scindi 	if (up != NULL && nvl != NULL)
3381414Scindi 		return (-1);	/* only convert from one or the other form */
3391414Scindi 
3401414Scindi 	if (CMI_OPS(cp) == NULL ||
3411414Scindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
3421414Scindi 		return (-1);	/* not registered yet */
3431414Scindi 
3441414Scindi 	return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap));
3451414Scindi }
346