xref: /titanic_51/usr/src/uts/i86pc/os/cmi.c (revision 7aec1d6e253b21f9e9b7ef68b4d81ab9859b51fe)
1*7aec1d6eScindi /*
2*7aec1d6eScindi  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3*7aec1d6eScindi  * Use is subject to license terms.
4*7aec1d6eScindi  */
5*7aec1d6eScindi 
6*7aec1d6eScindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
7*7aec1d6eScindi 
8*7aec1d6eScindi /*
9*7aec1d6eScindi  * Public interface to routines implemented by CPU modules
10*7aec1d6eScindi  */
11*7aec1d6eScindi 
12*7aec1d6eScindi #include <sys/x86_archext.h>
13*7aec1d6eScindi #include <sys/cpu_module_impl.h>
14*7aec1d6eScindi #include <sys/fm/util.h>
15*7aec1d6eScindi #include <sys/reboot.h>
16*7aec1d6eScindi #include <sys/modctl.h>
17*7aec1d6eScindi #include <sys/param.h>
18*7aec1d6eScindi #include <sys/cmn_err.h>
19*7aec1d6eScindi #include <sys/systm.h>
20*7aec1d6eScindi #include <sys/types.h>
21*7aec1d6eScindi 
22*7aec1d6eScindi #define	CPUMOD_SUBDIR	"cpu"
23*7aec1d6eScindi #define	CPUMOD_PREFIX	"cpu"
24*7aec1d6eScindi 
25*7aec1d6eScindi #define	CMI_OPS(cpu) \
26*7aec1d6eScindi 	(cpu)->cpu_m.mcpu_cmi->cmi_ops
27*7aec1d6eScindi #define	CMI_DATA(cpu) \
28*7aec1d6eScindi 	(cpu)->cpu_m.mcpu_cmidata
29*7aec1d6eScindi 
30*7aec1d6eScindi /*
31*7aec1d6eScindi  * If cleared for debugging, we will suppress panicking on fatal hardware
32*7aec1d6eScindi  * errors.  This should *only* be used for debugging; it use can and will
33*7aec1d6eScindi  * cause data corruption if actual hardware errors are detected by the system.
34*7aec1d6eScindi  */
35*7aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1;
36*7aec1d6eScindi 
37*7aec1d6eScindi static cmi_t *cmi_list;
38*7aec1d6eScindi static kmutex_t cmi_load_lock;
39*7aec1d6eScindi 
40*7aec1d6eScindi static int
41*7aec1d6eScindi cmi_cpu_match(cpu_t *c1, cpu_t *c2)
42*7aec1d6eScindi {
43*7aec1d6eScindi 	return (cpuid_getfamily(c1) == cpuid_getfamily(c2) &&
44*7aec1d6eScindi 	    cpuid_getmodel(c1) == cpuid_getmodel(c2) &&
45*7aec1d6eScindi 	    cpuid_getstep(c1) == cpuid_getstep(c2) &&
46*7aec1d6eScindi 	    strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0);
47*7aec1d6eScindi }
48*7aec1d6eScindi 
49*7aec1d6eScindi static cmi_t *
50*7aec1d6eScindi cmi_load_modctl(modctl_t *modp)
51*7aec1d6eScindi {
52*7aec1d6eScindi 	uintptr_t ops;
53*7aec1d6eScindi 	cmi_t *cmi;
54*7aec1d6eScindi 
55*7aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
56*7aec1d6eScindi 
57*7aec1d6eScindi 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
58*7aec1d6eScindi 		if (cmi->cmi_modp == modp)
59*7aec1d6eScindi 			return (cmi);
60*7aec1d6eScindi 	}
61*7aec1d6eScindi 
62*7aec1d6eScindi 	if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) {
63*7aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops "
64*7aec1d6eScindi 		    "found\n", modp->mod_modname);
65*7aec1d6eScindi 		return (NULL);
66*7aec1d6eScindi 	}
67*7aec1d6eScindi 
68*7aec1d6eScindi 	/*
69*7aec1d6eScindi 	 * Hold the module in memory.  We call to CPU modules without using the
70*7aec1d6eScindi 	 * stubs mechanism, so these modules must be manually held in memory.
71*7aec1d6eScindi 	 * The mod_ref acts as if another loaded module has a dependency on us.
72*7aec1d6eScindi 	 */
73*7aec1d6eScindi 	mutex_enter(&mod_lock);
74*7aec1d6eScindi 	modp->mod_ref++;
75*7aec1d6eScindi 	mutex_exit(&mod_lock);
76*7aec1d6eScindi 
77*7aec1d6eScindi 	cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP);
78*7aec1d6eScindi 	cmi->cmi_ops = (const cmi_ops_t *)ops;
79*7aec1d6eScindi 	cmi->cmi_modp = modp;
80*7aec1d6eScindi 
81*7aec1d6eScindi 	cmi->cmi_next = cmi_list;
82*7aec1d6eScindi 	cmi_list = cmi;
83*7aec1d6eScindi 
84*7aec1d6eScindi 	return (cmi);
85*7aec1d6eScindi }
86*7aec1d6eScindi 
87*7aec1d6eScindi static cmi_t *
88*7aec1d6eScindi cmi_load_module(cpu_t *cp)
89*7aec1d6eScindi {
90*7aec1d6eScindi 	modctl_t *modp;
91*7aec1d6eScindi 	cmi_t *cmi;
92*7aec1d6eScindi 	int i, modid;
93*7aec1d6eScindi 	uint_t s[3];
94*7aec1d6eScindi 
95*7aec1d6eScindi 	/*
96*7aec1d6eScindi 	 * Look to see if we've already got a module loaded for a CPU just
97*7aec1d6eScindi 	 * like this one.  If we do, then we'll re-use it.
98*7aec1d6eScindi 	 */
99*7aec1d6eScindi 	ASSERT(MUTEX_HELD(&cmi_load_lock));
100*7aec1d6eScindi 	mutex_enter(&cpu_lock);
101*7aec1d6eScindi 
102*7aec1d6eScindi 	for (i = 0; i < NCPU; i++) {
103*7aec1d6eScindi 		cpu_t *cp2 = cpu[i];
104*7aec1d6eScindi 
105*7aec1d6eScindi 		if (cp2 != NULL && cp2 != cp &&
106*7aec1d6eScindi 		    cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) {
107*7aec1d6eScindi 			mutex_exit(&cpu_lock);
108*7aec1d6eScindi 			return (cp2->cpu_m.mcpu_cmi);
109*7aec1d6eScindi 		}
110*7aec1d6eScindi 	}
111*7aec1d6eScindi 
112*7aec1d6eScindi 	mutex_exit(&cpu_lock);
113*7aec1d6eScindi 
114*7aec1d6eScindi 	/*
115*7aec1d6eScindi 	 * If we can't find a match, attempt to load the appropriate module.
116*7aec1d6eScindi 	 * If that also fails, try to load the generic CPU module.
117*7aec1d6eScindi 	 */
118*7aec1d6eScindi 	s[0] = cpuid_getfamily(cp);
119*7aec1d6eScindi 	s[1] = cpuid_getmodel(cp);
120*7aec1d6eScindi 	s[2] = cpuid_getstep(cp);
121*7aec1d6eScindi 
122*7aec1d6eScindi 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
123*7aec1d6eScindi 	    cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0]));
124*7aec1d6eScindi 
125*7aec1d6eScindi 	if (modid == -1)
126*7aec1d6eScindi 		modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic");
127*7aec1d6eScindi 
128*7aec1d6eScindi 	if (modid == -1)
129*7aec1d6eScindi 		return (NULL);
130*7aec1d6eScindi 
131*7aec1d6eScindi 	modp = mod_hold_by_id(modid);
132*7aec1d6eScindi 	cmi = cmi_load_modctl(modp);
133*7aec1d6eScindi 	mod_release_mod(modp);
134*7aec1d6eScindi 
135*7aec1d6eScindi 	return (cmi);
136*7aec1d6eScindi }
137*7aec1d6eScindi 
138*7aec1d6eScindi static cmi_t *
139*7aec1d6eScindi cmi_load_generic(void)
140*7aec1d6eScindi {
141*7aec1d6eScindi 	modctl_t *modp;
142*7aec1d6eScindi 	cmi_t *cmi;
143*7aec1d6eScindi 	int modid;
144*7aec1d6eScindi 
145*7aec1d6eScindi 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
146*7aec1d6eScindi 		return (NULL);
147*7aec1d6eScindi 
148*7aec1d6eScindi 	modp = mod_hold_by_id(modid);
149*7aec1d6eScindi 	cmi = cmi_load_modctl(modp);
150*7aec1d6eScindi 	mod_release_mod(modp);
151*7aec1d6eScindi 
152*7aec1d6eScindi 	return (cmi);
153*7aec1d6eScindi }
154*7aec1d6eScindi 
155*7aec1d6eScindi /*
156*7aec1d6eScindi  * Load a CPU module for the specified CPU, and then call its cmi_init routine.
157*7aec1d6eScindi  * If the module returns ENOTSUP, try using the generic CPU module instead.
158*7aec1d6eScindi  * If all else fails, we return -1 and the caller will panic or halt.
159*7aec1d6eScindi  */
160*7aec1d6eScindi int
161*7aec1d6eScindi cmi_load(cpu_t *cp)
162*7aec1d6eScindi {
163*7aec1d6eScindi 	int err = ENOENT;
164*7aec1d6eScindi 	cmi_t *cmi;
165*7aec1d6eScindi 	void *data;
166*7aec1d6eScindi 
167*7aec1d6eScindi 	mutex_enter(&cmi_load_lock);
168*7aec1d6eScindi 
169*7aec1d6eScindi 	if ((cmi = cmi_load_module(cp)) == NULL || (
170*7aec1d6eScindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && err != ENOTSUP)) {
171*7aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
172*7aec1d6eScindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
173*7aec1d6eScindi 		mutex_exit(&cmi_load_lock);
174*7aec1d6eScindi 		return (-1);
175*7aec1d6eScindi 	}
176*7aec1d6eScindi 
177*7aec1d6eScindi 	if (err != 0 && ((cmi = cmi_load_generic()) == NULL ||
178*7aec1d6eScindi 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
179*7aec1d6eScindi 		cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d",
180*7aec1d6eScindi 		    cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err);
181*7aec1d6eScindi 		mutex_exit(&cmi_load_lock);
182*7aec1d6eScindi 		return (-1);
183*7aec1d6eScindi 	}
184*7aec1d6eScindi 
185*7aec1d6eScindi 	ASSERT(cp->cpu_m.mcpu_cmi == NULL);
186*7aec1d6eScindi 	cp->cpu_m.mcpu_cmi = cmi;
187*7aec1d6eScindi 	cp->cpu_m.mcpu_cmidata = data;
188*7aec1d6eScindi 
189*7aec1d6eScindi 	cmi->cmi_refcnt++;
190*7aec1d6eScindi 	mutex_exit(&cmi_load_lock);
191*7aec1d6eScindi 
192*7aec1d6eScindi 	if (boothowto & RB_VERBOSE) {
193*7aec1d6eScindi 		printf("cpuid %d: initialized cpumod: %s\n",
194*7aec1d6eScindi 		    cp->cpu_id, cmi->cmi_modp->mod_modname);
195*7aec1d6eScindi 	}
196*7aec1d6eScindi 
197*7aec1d6eScindi 	return (0);
198*7aec1d6eScindi }
199*7aec1d6eScindi 
200*7aec1d6eScindi void
201*7aec1d6eScindi cmi_init(void)
202*7aec1d6eScindi {
203*7aec1d6eScindi 	if (cmi_load(CPU) < 0)
204*7aec1d6eScindi 		panic("failed to load module for CPU %u", CPU->cpu_id);
205*7aec1d6eScindi }
206*7aec1d6eScindi 
207*7aec1d6eScindi void
208*7aec1d6eScindi cmi_post_init(void)
209*7aec1d6eScindi {
210*7aec1d6eScindi 	CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
211*7aec1d6eScindi }
212*7aec1d6eScindi 
213*7aec1d6eScindi void
214*7aec1d6eScindi cmi_faulted_enter(cpu_t *cp)
215*7aec1d6eScindi {
216*7aec1d6eScindi 	CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp));
217*7aec1d6eScindi }
218*7aec1d6eScindi 
219*7aec1d6eScindi void
220*7aec1d6eScindi cmi_faulted_exit(cpu_t *cp)
221*7aec1d6eScindi {
222*7aec1d6eScindi 	CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp));
223*7aec1d6eScindi }
224*7aec1d6eScindi 
225*7aec1d6eScindi int
226*7aec1d6eScindi cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen)
227*7aec1d6eScindi {
228*7aec1d6eScindi 	return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen));
229*7aec1d6eScindi }
230*7aec1d6eScindi 
231*7aec1d6eScindi void
232*7aec1d6eScindi cmi_mca_init(void)
233*7aec1d6eScindi {
234*7aec1d6eScindi 	CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU));
235*7aec1d6eScindi }
236*7aec1d6eScindi 
237*7aec1d6eScindi void
238*7aec1d6eScindi cmi_mca_trap(struct regs *rp)
239*7aec1d6eScindi {
240*7aec1d6eScindi 	if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) {
241*7aec1d6eScindi 		if (cmi_panic_on_uncorrectable_error)
242*7aec1d6eScindi 			fm_panic("Unrecoverable Machine-Check Exception");
243*7aec1d6eScindi 		else
244*7aec1d6eScindi 			cmn_err(CE_WARN, "suppressing panic from fatal #mc");
245*7aec1d6eScindi 	}
246*7aec1d6eScindi }
247*7aec1d6eScindi 
248*7aec1d6eScindi int
249*7aec1d6eScindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs)
250*7aec1d6eScindi {
251*7aec1d6eScindi 	int err;
252*7aec1d6eScindi 
253*7aec1d6eScindi 	kpreempt_disable();
254*7aec1d6eScindi 	err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs);
255*7aec1d6eScindi 	kpreempt_enable();
256*7aec1d6eScindi 
257*7aec1d6eScindi 	return (err);
258*7aec1d6eScindi }
259*7aec1d6eScindi 
260*7aec1d6eScindi void
261*7aec1d6eScindi cmi_mca_poke(void)
262*7aec1d6eScindi {
263*7aec1d6eScindi 	CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU));
264*7aec1d6eScindi }
265*7aec1d6eScindi 
266*7aec1d6eScindi void
267*7aec1d6eScindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata)
268*7aec1d6eScindi {
269*7aec1d6eScindi 	CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata);
270*7aec1d6eScindi }
271*7aec1d6eScindi 
272*7aec1d6eScindi int
273*7aec1d6eScindi cmi_mc_patounum(uint64_t pa, uint32_t synd, int syndtype, mc_unum_t *up)
274*7aec1d6eScindi {
275*7aec1d6eScindi 	const struct cmi_mc_ops *mcops;
276*7aec1d6eScindi 	cpu_t *cp = CPU;
277*7aec1d6eScindi 
278*7aec1d6eScindi 	if (CMI_OPS(cp) == NULL ||
279*7aec1d6eScindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
280*7aec1d6eScindi 		return (-1);	/* not registered yet */
281*7aec1d6eScindi 
282*7aec1d6eScindi 	return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, synd, syndtype, up));
283*7aec1d6eScindi }
284*7aec1d6eScindi 
285*7aec1d6eScindi int
286*7aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
287*7aec1d6eScindi {
288*7aec1d6eScindi 	const struct cmi_mc_ops *mcops;
289*7aec1d6eScindi 	cpu_t *cp = CPU;
290*7aec1d6eScindi 
291*7aec1d6eScindi 	if (up != NULL && nvl != NULL)
292*7aec1d6eScindi 		return (-1);	/* only convert from one or the other form */
293*7aec1d6eScindi 
294*7aec1d6eScindi 	if (CMI_OPS(cp) == NULL ||
295*7aec1d6eScindi 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
296*7aec1d6eScindi 		return (-1);	/* not registered yet */
297*7aec1d6eScindi 
298*7aec1d6eScindi 	return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap));
299*7aec1d6eScindi }
300