xref: /illumos-gate/usr/src/uts/i86pc/os/cmi.c (revision c97ad5cdc75eb73e3cc38542ca3ba783574b0a7a)
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 2007 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 not attempt to load a model-specific
53  * cpu module but will load the generic cpu module instead.
54  */
55 int cmi_force_generic = 0;
56 
57 /*
58  * If cleared for debugging, we will suppress panicking on fatal hardware
59  * errors.  This should *only* be used for debugging; it use can and will
60  * cause data corruption if actual hardware errors are detected by the system.
61  */
62 int cmi_panic_on_uncorrectable_error = 1;
63 
64 static cmi_t *cmi_list;
65 static kmutex_t cmi_load_lock;
66 
67 static int
68 cmi_cpu_match(cpu_t *c1, cpu_t *c2)
69 {
70 	return (cpuid_getfamily(c1) == cpuid_getfamily(c2) &&
71 	    cpuid_getmodel(c1) == cpuid_getmodel(c2) &&
72 	    cpuid_getstep(c1) == cpuid_getstep(c2) &&
73 	    strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0);
74 }
75 
76 static cmi_t *
77 cmi_load_modctl(modctl_t *modp)
78 {
79 	uintptr_t ops;
80 	cmi_t *cmi;
81 
82 	ASSERT(MUTEX_HELD(&cmi_load_lock));
83 
84 	for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) {
85 		if (cmi->cmi_modp == modp)
86 			return (cmi);
87 	}
88 
89 	if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) {
90 		cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops "
91 		    "found", modp->mod_modname);
92 		return (NULL);
93 	}
94 
95 	/*
96 	 * Hold the module in memory.  We call to CPU modules without using the
97 	 * stubs mechanism, so these modules must be manually held in memory.
98 	 * The mod_ref acts as if another loaded module has a dependency on us.
99 	 */
100 	mutex_enter(&mod_lock);
101 	modp->mod_ref++;
102 	mutex_exit(&mod_lock);
103 
104 	cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP);
105 	cmi->cmi_ops = (const cmi_ops_t *)ops;
106 	cmi->cmi_modp = modp;
107 
108 	cmi->cmi_next = cmi_list;
109 	cmi_list = cmi;
110 
111 	return (cmi);
112 }
113 
114 static cmi_t *
115 cmi_load_module(cpu_t *cp)
116 {
117 	modctl_t *modp;
118 	cmi_t *cmi;
119 	int i, modid;
120 	uint_t s[3];
121 
122 	/*
123 	 * Look to see if we've already got a module loaded for a CPU just
124 	 * like this one.  If we do, then we'll re-use it.
125 	 */
126 	ASSERT(MUTEX_HELD(&cmi_load_lock));
127 	mutex_enter(&cpu_lock);
128 
129 	for (i = 0; i < NCPU; i++) {
130 		cpu_t *cp2 = cpu[i];
131 
132 		if (cp2 != NULL && cp2 != cp &&
133 		    cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) {
134 			mutex_exit(&cpu_lock);
135 			return (cp2->cpu_m.mcpu_cmi);
136 		}
137 	}
138 
139 	mutex_exit(&cpu_lock);
140 
141 	/*
142 	 * If we can't find a match, attempt to load the appropriate module.
143 	 * If that also fails, try to load the generic CPU module.
144 	 */
145 	s[0] = cpuid_getfamily(cp);
146 	s[1] = cpuid_getmodel(cp);
147 	s[2] = cpuid_getstep(cp);
148 
149 	modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX,
150 	    cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0]));
151 
152 	if (modid == -1)
153 		modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic");
154 
155 	if (modid == -1)
156 		return (NULL);
157 
158 	modp = mod_hold_by_id(modid);
159 	cmi = cmi_load_modctl(modp);
160 	mod_release_mod(modp);
161 
162 	return (cmi);
163 }
164 
165 static cmi_t *
166 cmi_load_generic(void)
167 {
168 	modctl_t *modp;
169 	cmi_t *cmi;
170 	int modid;
171 
172 	if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1)
173 		return (NULL);
174 
175 	modp = mod_hold_by_id(modid);
176 	cmi = cmi_load_modctl(modp);
177 	mod_release_mod(modp);
178 
179 	return (cmi);
180 }
181 
182 /*
183  * Load a CPU module for the specified CPU, and then call its cmi_init routine.
184  * If the module returns ENOTSUP, try using the generic CPU module instead.
185  * If all else fails, we return -1 and the caller will panic or halt.
186  */
187 int
188 cmi_load(cpu_t *cp)
189 {
190 	int err = ENOENT;
191 	cmi_t *cmi;
192 	void *data;
193 
194 	mutex_enter(&cmi_load_lock);
195 
196 	if (!cmi_force_generic && (
197 	    ((cmi = cmi_load_module(cp)) == NULL) ||
198 	    ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 &&
199 	    err != ENOTSUP))) {
200 		cmn_err(CE_WARN,
201 		    "cpu%d: failed to init cpu module '%s': err=%d",
202 		    cp->cpu_id, cmi ? cmi->cmi_modp->mod_modname : "<>", err);
203 		mutex_exit(&cmi_load_lock);
204 		return (-1);
205 	}
206 
207 	if ((cmi_force_generic || err != 0) &&
208 	    ((cmi = cmi_load_generic()) == NULL ||
209 	    (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) {
210 		cmn_err(CE_WARN,
211 		    "cpu%d: failed to init cpu module '%s': err=%d",
212 		    cp->cpu_id, cmi ? cmi->cmi_modp->mod_modname : "<>", err);
213 		mutex_exit(&cmi_load_lock);
214 		return (-1);
215 	}
216 
217 	ASSERT(cp->cpu_m.mcpu_cmi == NULL);
218 	cp->cpu_m.mcpu_cmi = cmi;
219 	cp->cpu_m.mcpu_cmidata = data;
220 
221 	cmi->cmi_refcnt++;
222 	mutex_exit(&cmi_load_lock);
223 
224 	if (boothowto & RB_VERBOSE) {
225 		printf("cpu%d: initialized cpu module '%s'\n",
226 		    cp->cpu_id, cmi->cmi_modp->mod_modname);
227 	}
228 
229 	return (0);
230 }
231 
232 void
233 cmi_init(void)
234 {
235 	if (cmi_load(CPU) < 0)
236 		panic("failed to load module for cpu%d", CPU->cpu_id);
237 }
238 
239 void
240 cmi_post_init(void)
241 {
242 	CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU));
243 }
244 
245 /*
246  * Called just once from start_other_cpus when all processors are started.
247  * This will not be called for each cpu, so the registered op must not
248  * assume it is called as such.
249  */
250 void
251 cmi_post_mpstartup(void)
252 {
253 	CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU));
254 }
255 
256 void
257 cmi_faulted_enter(cpu_t *cp)
258 {
259 	CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp));
260 }
261 
262 void
263 cmi_faulted_exit(cpu_t *cp)
264 {
265 	CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp));
266 }
267 
268 int
269 cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig)
270 {
271 	return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen,
272 	    cscontig));
273 }
274 
275 void
276 cmi_mca_init(void)
277 {
278 	CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU));
279 }
280 
281 void
282 cmi_mca_trap(struct regs *rp)
283 {
284 	if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) {
285 		if (cmi_panic_on_uncorrectable_error)
286 			fm_panic("Unrecoverable Machine-Check Exception");
287 		else
288 			cmn_err(CE_WARN, "suppressing panic from fatal #mc");
289 	}
290 }
291 
292 int
293 cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs)
294 {
295 	int err;
296 
297 	kpreempt_disable();
298 	err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs);
299 	kpreempt_enable();
300 
301 	return (err);
302 }
303 
304 void
305 cmi_mca_poke(void)
306 {
307 	CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU));
308 }
309 
310 void
311 cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata)
312 {
313 	CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata);
314 }
315 
316 int
317 cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd,
318     int syndtype, mc_unum_t *up)
319 {
320 	const struct cmi_mc_ops *mcops;
321 	cpu_t *cp = CPU;
322 
323 	if (CMI_OPS(cp) == NULL ||
324 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
325 		return (-1);	/* not registered yet */
326 
327 	return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, valid_hi, valid_lo,
328 	    synd, syndtype, up));
329 }
330 
331 int
332 cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap)
333 {
334 	const struct cmi_mc_ops *mcops;
335 	cpu_t *cp = CPU;
336 
337 	if (up != NULL && nvl != NULL)
338 		return (-1);	/* only convert from one or the other form */
339 
340 	if (CMI_OPS(cp) == NULL ||
341 	    (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL)
342 		return (-1);	/* not registered yet */
343 
344 	return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap));
345 }
346