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