xref: /illumos-gate/usr/src/uts/i86pc/os/cms.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/cpu_module_ms_impl.h>
29 #include <sys/cpuvar.h>
30 #include <sys/ksynch.h>
31 #include <sys/modctl.h>
32 #include <sys/x86_archext.h>
33 #include <sys/systm.h>
34 #include <sys/cmn_err.h>
35 #include <sys/param.h>
36 #include <sys/reboot.h>
37 
38 /*
39  * Set to prevent model-specific support from initialising.
40  */
41 int cms_no_model_specific = 0;
42 
43 /*
44  * Subdirectory (relative to the module search path) in which we will
45  * look for model-specific modules.
46  */
47 #define	CPUMOD_MS_SUBDIR	"cpu"
48 
49 /*
50  * Cpu model-specific modules have filenames beginning with the following.
51  */
52 #define	CPUMOD_MS_PREFIX	"cpu_ms"
53 
54 #define	HDL2CMS(hdl)		cms_hdl_getcms(hdl)
55 
56 #define	CMS_OPS(cms)		(cms)->cms_ops
57 #define	CMS_OP_PRESENT(cms, op)	((cms) && CMS_OPS(cms)->op != NULL)
58 
59 struct cms_cpuid {
60 	const char *vendor;
61 	uint_t family;
62 	uint_t model;
63 	uint_t stepping;
64 };
65 
66 #define	CMS_MATCH_VENDOR	0	/* Just match on vendor */
67 #define	CMS_MATCH_FAMILY	1	/* Match down to family */
68 #define	CMS_MATCH_MODEL		2	/* Match down to model */
69 #define	CMS_MATCH_STEPPING	3	/* Match down to stepping */
70 
71 /*
72  * Structure used to keep track of modules we have loaded.
73  */
74 typedef struct cms {
75 	struct cms *cms_next;
76 	struct cms *cms_prev;
77 	const cms_ops_t *cms_ops;
78 	struct modctl *cms_modp;
79 	uint_t cms_refcnt;
80 } cms_t;
81 
82 static cms_t *cms_list;
83 static kmutex_t cms_load_lock;
84 
85 /*
86  * We stash a cms_t and associated private data via cmi_hdl_setspecific.
87  */
88 struct cms_ctl {
89 	cms_t *cs_cms;
90 	void *cs_cmsdata;
91 };
92 
93 static cms_t *
94 cms_hdl_getcms(cmi_hdl_t hdl)
95 {
96 	struct cms_ctl *cdp = cmi_hdl_getspecific(hdl);
97 
98 	return (cdp != NULL ? cdp->cs_cms : NULL);
99 }
100 
101 void *
102 cms_hdl_getcmsdata(cmi_hdl_t hdl)
103 {
104 	struct cms_ctl *cdp = cmi_hdl_getspecific(hdl);
105 
106 	return (cdp != NULL ? cdp->cs_cmsdata : NULL);
107 }
108 
109 static void
110 cms_link(cms_t *cms)
111 {
112 	ASSERT(MUTEX_HELD(&cms_load_lock));
113 
114 	cms->cms_prev = NULL;
115 	cms->cms_next = cms_list;
116 	if (cms_list != NULL)
117 		cms_list->cms_prev = cms;
118 	cms_list = cms;
119 }
120 
121 static void
122 cms_unlink(cms_t *cms)
123 {
124 	ASSERT(MUTEX_HELD(&cms_load_lock));
125 	ASSERT(cms->cms_refcnt == 0);
126 
127 	if (cms->cms_prev != NULL)
128 		cms->cms_prev->cms_next = cms->cms_next;
129 
130 	if (cms->cms_next != NULL)
131 		cms->cms_next->cms_prev = cms->cms_prev;
132 
133 	if (cms_list == cms)
134 		cms_list = cms->cms_next;
135 }
136 
137 /*
138  * Hold the module in memory.  We call to CPU modules without using the
139  * stubs mechanism, so these modules must be manually held in memory.
140  * The mod_ref acts as if another loaded module has a dependency on us.
141  */
142 static void
143 cms_hold(cms_t *cms)
144 {
145 	ASSERT(MUTEX_HELD(&cms_load_lock));
146 
147 	mutex_enter(&mod_lock);
148 	cms->cms_modp->mod_ref++;
149 	mutex_exit(&mod_lock);
150 	cms->cms_refcnt++;
151 }
152 
153 static void
154 cms_rele(cms_t *cms)
155 {
156 	ASSERT(MUTEX_HELD(&cms_load_lock));
157 
158 	mutex_enter(&mod_lock);
159 	cms->cms_modp->mod_ref--;
160 	mutex_exit(&mod_lock);
161 
162 	if (--cms->cms_refcnt == 0) {
163 		cms_unlink(cms);
164 		kmem_free(cms, sizeof (cms_t));
165 	}
166 }
167 
168 static cms_ops_t *
169 cms_getops(modctl_t *modp)
170 {
171 	cms_ops_t *ops;
172 
173 	if ((ops = (cms_ops_t *)modlookup_by_modctl(modp, "_cms_ops")) ==
174 	    NULL) {
175 		cmn_err(CE_WARN, "cpu_ms module '%s' is invalid: no _cms_ops "
176 		    "found", modp->mod_modname);
177 		return (NULL);
178 	}
179 
180 	if (ops->cms_init == NULL) {
181 		cmn_err(CE_WARN, "cpu_ms module '%s' is invalid: no cms_init "
182 		    "entry point", modp->mod_modname);
183 		return (NULL);
184 	}
185 
186 	return (ops);
187 }
188 
189 static cms_t *
190 cms_load_modctl(modctl_t *modp)
191 {
192 	cms_ops_t *ops;
193 	uintptr_t ver;
194 	cms_t *cms;
195 	cms_api_ver_t apiver;
196 
197 	ASSERT(MUTEX_HELD(&cms_load_lock));
198 
199 	for (cms = cms_list; cms != NULL; cms = cms->cms_next) {
200 		if (cms->cms_modp == modp)
201 			return (cms);
202 	}
203 
204 	if ((ver = modlookup_by_modctl(modp, "_cms_api_version")) == NULL) {
205 		cmn_err(CE_WARN, "cpu model-specific module '%s' is invalid:  "
206 		    "no _cms_api_version", modp->mod_modname);
207 		return (NULL);
208 	} else {
209 		apiver = *((cms_api_ver_t *)ver);
210 		if (!CMS_API_VERSION_CHKMAGIC(apiver)) {
211 			cmn_err(CE_WARN, "cpu model-specific module '%s' is "
212 			    "invalid: _cms_api_version 0x%x has bad magic",
213 			    modp->mod_modname, apiver);
214 			return (NULL);
215 		}
216 	}
217 
218 	if (apiver != CMS_API_VERSION) {
219 		cmn_err(CE_WARN, "cpu model-specific module '%s' has API "
220 		    "version %d, kernel requires API version %d",
221 		    modp->mod_modname, CMS_API_VERSION_TOPRINT(apiver),
222 		    CMS_API_VERSION_TOPRINT(CMS_API_VERSION));
223 	return (NULL);
224 	}
225 
226 	if ((ops = cms_getops(modp)) == NULL)
227 		return (NULL);
228 
229 	cms = kmem_zalloc(sizeof (cms_t), KM_SLEEP);
230 	cms->cms_ops = ops;
231 	cms->cms_modp = modp;
232 
233 	cms_link(cms);
234 
235 	return (cms);
236 }
237 
238 static int
239 cms_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match)
240 {
241 	if (match >= CMS_MATCH_VENDOR &&
242 	    cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2))
243 		return (0);
244 
245 	if (match >= CMS_MATCH_FAMILY &&
246 	    cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2))
247 		return (0);
248 
249 	if (match >= CMS_MATCH_MODEL &&
250 	    cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2))
251 		return (0);
252 
253 	if (match >= CMS_MATCH_STEPPING &&
254 	    cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2))
255 		return (0);
256 
257 	return (1);
258 }
259 
260 static int
261 cms_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
262 {
263 	cmi_hdl_t thdl = (cmi_hdl_t)arg1;
264 	int match = *((int *)arg2);
265 	cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3;
266 
267 	if (cms_cpu_match(thdl, whdl, match)) {
268 		cmi_hdl_hold(whdl);	/* short-term hold */
269 		*rsltp = whdl;
270 		return (CMI_HDL_WALK_DONE);
271 	} else {
272 		return (CMI_HDL_WALK_NEXT);
273 	}
274 }
275 
276 /*
277  * Look to see if we've already got a module loaded for a CPU just
278  * like this one.  If we do, then we'll re-use it.
279  */
280 static cms_t *
281 cms_search_list(cmi_hdl_t hdl, int match)
282 {
283 	cmi_hdl_t dhdl = NULL;
284 	cms_t *cms = NULL;
285 
286 	ASSERT(MUTEX_HELD(&cms_load_lock));
287 
288 	cmi_hdl_walk(cms_search_list_cb, (void *)hdl, (void *)&match, &dhdl);
289 	if (dhdl) {
290 		cms = HDL2CMS(dhdl);
291 		cmi_hdl_rele(dhdl);	/* held in cms_search_list_cb */
292 	}
293 
294 	return (cms);
295 }
296 
297 /*
298  * Try to find or load a module that offers model-specific support for
299  * this vendor/family/model/stepping combination.  When attempting to load
300  * a module we look in CPUMOD_MS_SUBDIR first for a match on
301  * vendor/family/model/stepping, then on vendor/family/model (ignoring
302  * stepping), then on vendor/family (ignoring model and stepping), then
303  * on vendor alone.
304  */
305 static cms_t *
306 cms_load_module(cmi_hdl_t hdl, int match, int *chosenp)
307 {
308 	modctl_t *modp;
309 	cms_t *cms;
310 	int modid;
311 	uint_t s[3];
312 
313 	ASSERT(MUTEX_HELD(&cms_load_lock));
314 	ASSERT(match == CMS_MATCH_STEPPING || match == CMS_MATCH_MODEL ||
315 	    match == CMS_MATCH_FAMILY || match == CMS_MATCH_VENDOR);
316 
317 	s[0] = cmi_hdl_family(hdl);
318 	s[1] = cmi_hdl_model(hdl);
319 	s[2] = cmi_hdl_stepping(hdl);
320 
321 	/*
322 	 * Have we already loaded a module for a cpu with the same
323 	 * vendor/family/model/stepping?
324 	 */
325 	if ((cms = cms_search_list(hdl, match)) != NULL) {
326 		cms_hold(cms);
327 		return (cms);
328 	}
329 
330 	modid = modload_qualified(CPUMOD_MS_SUBDIR, CPUMOD_MS_PREFIX,
331 	    cmi_hdl_vendorstr(hdl), ".", s, match, chosenp);
332 
333 	if (modid == -1)
334 		return (NULL);
335 
336 	modp = mod_hold_by_id(modid);
337 	cms = cms_load_modctl(modp);
338 	if (cms)
339 		cms_hold(cms);
340 	mod_release_mod(modp);
341 
342 	return (cms);
343 }
344 
345 static cms_t *
346 cms_load_specific(cmi_hdl_t hdl, void **datap)
347 {
348 	cms_t *cms;
349 	int err;
350 	int i;
351 
352 	ASSERT(MUTEX_HELD(&cms_load_lock));
353 
354 	for (i = CMS_MATCH_STEPPING; i >= CMS_MATCH_VENDOR; i--) {
355 		int suffixlevel;
356 
357 		if ((cms = cms_load_module(hdl, i, &suffixlevel)) == NULL)
358 			return (NULL);
359 
360 		/*
361 		 * A module has loaded and has a _cms_ops structure, and the
362 		 * module has been held for this instance.  Call the cms_init
363 		 * entry point - we expect success (0) or ENOTSUP.
364 		 */
365 		if ((err = cms->cms_ops->cms_init(hdl, datap)) == 0) {
366 			if (boothowto & RB_VERBOSE) {
367 				printf("initialized model-specific "
368 				    "module '%s' on chip %d core %d "
369 				    "strand %d\n",
370 				    cms->cms_modp->mod_modname,
371 				    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
372 				    cmi_hdl_strandid(hdl));
373 			}
374 			return (cms);
375 		} else if (err != ENOTSUP) {
376 			cmn_err(CE_WARN, "failed to init model-specific "
377 			    "module '%s' on chip %d core %d strand %d: err=%d",
378 			    cms->cms_modp->mod_modname,
379 			    cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl),
380 			    cmi_hdl_strandid(hdl), err);
381 		}
382 
383 		/*
384 		 * The module failed or declined to init, so release
385 		 * it and potentially change i to be equal to he number
386 		 * of suffices actually used in the last module path.
387 		 */
388 		cms_rele(cms);
389 		i = suffixlevel;
390 	}
391 
392 	return (NULL);
393 }
394 
395 void
396 cms_init(cmi_hdl_t hdl)
397 {
398 	cms_t *cms;
399 	void *data;
400 
401 	if (cms_no_model_specific != 0)
402 		return;
403 
404 	mutex_enter(&cms_load_lock);
405 
406 	if ((cms = cms_load_specific(hdl, &data)) != NULL) {
407 		struct cms_ctl *cdp;
408 
409 		ASSERT(cmi_hdl_getspecific(hdl) == NULL);
410 
411 		cdp = kmem_alloc(sizeof (*cdp), KM_SLEEP);
412 		cdp->cs_cms = cms;
413 		cdp->cs_cmsdata = data;
414 		cmi_hdl_setspecific(hdl, cdp);
415 	}
416 
417 	mutex_exit(&cms_load_lock);
418 }
419 
420 void
421 cms_fini(cmi_hdl_t hdl)
422 {
423 	cms_t *cms = HDL2CMS(hdl);
424 
425 	if (CMS_OP_PRESENT(cms, cms_fini))
426 		CMS_OPS(cms)->cms_fini(hdl);
427 }
428 
429 boolean_t
430 cms_present(cmi_hdl_t hdl)
431 {
432 	return (HDL2CMS(hdl) != NULL ? B_TRUE : B_FALSE);
433 }
434 
435 void
436 cms_post_startup(cmi_hdl_t hdl)
437 {
438 	cms_t *cms = HDL2CMS(hdl);
439 
440 	if (CMS_OP_PRESENT(cms, cms_post_startup))
441 		CMS_OPS(cms)->cms_post_startup(hdl);
442 }
443 
444 void
445 cms_post_mpstartup(cmi_hdl_t hdl)
446 {
447 	cms_t *cms = HDL2CMS(hdl);
448 
449 	if (CMS_OP_PRESENT(cms, cms_post_mpstartup))
450 		CMS_OPS(cms)->cms_post_mpstartup(hdl);
451 }
452 
453 size_t
454 cms_logout_size(cmi_hdl_t hdl)
455 {
456 	cms_t *cms = HDL2CMS(hdl);
457 
458 	if (!CMS_OP_PRESENT(cms, cms_logout_size))
459 		return (0);
460 
461 	return (CMS_OPS(cms)->cms_logout_size(hdl));
462 }
463 
464 uint64_t
465 cms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
466 {
467 	cms_t *cms = HDL2CMS(hdl);
468 
469 	if (!CMS_OP_PRESENT(cms, cms_mcgctl_val))
470 		return (def);
471 
472 	return (CMS_OPS(cms)->cms_mcgctl_val(hdl, nbanks, def));
473 }
474 
475 boolean_t
476 cms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
477 {
478 	cms_t *cms = HDL2CMS(hdl);
479 
480 	if (!CMS_OP_PRESENT(cms, cms_bankctl_skipinit))
481 		return (B_FALSE);
482 
483 	return (CMS_OPS(cms)->cms_bankctl_skipinit(hdl, banknum));
484 }
485 
486 uint64_t
487 cms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
488 {
489 	cms_t *cms = HDL2CMS(hdl);
490 
491 	if (!CMS_OP_PRESENT(cms, cms_bankctl_val))
492 		return (def);
493 
494 	return (CMS_OPS(cms)->cms_bankctl_val(hdl, banknum, def));
495 }
496 
497 boolean_t
498 cms_bankstatus_skipinit(cmi_hdl_t hdl, int banknum)
499 {
500 	cms_t *cms = HDL2CMS(hdl);
501 
502 	if (!CMS_OP_PRESENT(cms, cms_bankstatus_skipinit))
503 		return (B_FALSE);
504 
505 	return (CMS_OPS(cms)->cms_bankstatus_skipinit(hdl, banknum));
506 }
507 
508 uint64_t
509 cms_bankstatus_val(cmi_hdl_t hdl, int banknum, uint64_t def)
510 {
511 	cms_t *cms = HDL2CMS(hdl);
512 
513 	if (!CMS_OP_PRESENT(cms, cms_bankstatus_val))
514 		return (def);
515 
516 	return (CMS_OPS(cms)->cms_bankstatus_val(hdl, banknum, def));
517 }
518 
519 void
520 cms_mca_init(cmi_hdl_t hdl, int nbanks)
521 {
522 	cms_t *cms = HDL2CMS(hdl);
523 
524 	if (CMS_OP_PRESENT(cms, cms_mca_init))
525 		CMS_OPS(cms)->cms_mca_init(hdl, nbanks);
526 }
527 
528 uint64_t
529 cms_poll_ownermask(cmi_hdl_t hdl, hrtime_t poll_interval)
530 {
531 	cms_t *cms = HDL2CMS(hdl);
532 
533 	if (CMS_OP_PRESENT(cms, cms_poll_ownermask))
534 		return (CMS_OPS(cms)->cms_poll_ownermask(hdl, poll_interval));
535 	else
536 		return (-1ULL);		/* poll all banks by default */
537 }
538 
539 void
540 cms_bank_logout(cmi_hdl_t hdl, int banknum, uint64_t status, uint64_t addr,
541     uint64_t misc, void *mslogout)
542 {
543 	cms_t *cms = HDL2CMS(hdl);
544 
545 	if (mslogout != NULL && CMS_OP_PRESENT(cms, cms_bank_logout))
546 		CMS_OPS(cms)->cms_bank_logout(hdl, banknum, status, addr,
547 		    misc, mslogout);
548 }
549 
550 cms_errno_t
551 cms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
552 {
553 	cms_t *cms = HDL2CMS(hdl);
554 
555 	if (CMS_OP_PRESENT(cms, cms_msrinject))
556 		return (CMS_OPS(cms)->cms_msrinject(hdl, msr, val));
557 	else
558 		return (CMSERR_NOTSUP);
559 }
560 
561 uint32_t
562 cms_error_action(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
563     uint64_t addr, uint64_t misc, void *mslogout)
564 {
565 	cms_t *cms = HDL2CMS(hdl);
566 
567 	if (CMS_OP_PRESENT(cms, cms_error_action))
568 		return (CMS_OPS(cms)->cms_error_action(hdl, ismc, banknum,
569 		    status, addr, misc, mslogout));
570 	else
571 		return (0);
572 }
573 
574 cms_cookie_t
575 cms_disp_match(cmi_hdl_t hdl, int banknum, uint64_t status, uint64_t addr,
576     uint64_t misc, void *mslogout)
577 {
578 	cms_t *cms = HDL2CMS(hdl);
579 
580 	if (CMS_OP_PRESENT(cms, cms_disp_match))
581 		return (CMS_OPS(cms)->cms_disp_match(hdl, banknum,
582 		    status, addr, misc, mslogout));
583 	else
584 		return (NULL);
585 
586 }
587 
588 void
589 cms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, const char **cpuclsp,
590     const char **leafclsp)
591 {
592 	cms_t *cms = HDL2CMS(hdl);
593 
594 	if (cpuclsp == NULL || leafclsp == NULL)
595 		return;
596 
597 	*cpuclsp = *leafclsp = NULL;
598 	if (CMS_OP_PRESENT(cms, cms_ereport_class)) {
599 		CMS_OPS(cms)->cms_ereport_class(hdl, mscookie, cpuclsp,
600 		    leafclsp);
601 	}
602 }
603 
604 nvlist_t *
605 cms_ereport_detector(cmi_hdl_t hdl, int bankno, cms_cookie_t mscookie,
606     nv_alloc_t *nva)
607 {
608 	cms_t *cms = HDL2CMS(hdl);
609 
610 	if (CMS_OP_PRESENT(cms, cms_ereport_detector))
611 		return (CMS_OPS(cms)->cms_ereport_detector(hdl, bankno,
612 		    mscookie, nva));
613 	else
614 		return (NULL);
615 
616 }
617 
618 boolean_t
619 cms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
620 {
621 	cms_t *cms = HDL2CMS(hdl);
622 
623 	if (CMS_OP_PRESENT(cms, cms_ereport_includestack)) {
624 		return (CMS_OPS(cms)->cms_ereport_includestack(hdl, mscookie));
625 	} else {
626 		return (B_FALSE);
627 	}
628 }
629 
630 void
631 cms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *nvl, nv_alloc_t *nva,
632     int banknum, uint64_t status, uint64_t addr, uint64_t misc, void *mslogout,
633     cms_cookie_t mscookie)
634 {
635 	cms_t *cms = HDL2CMS(hdl);
636 
637 	if (CMS_OP_PRESENT(cms, cms_ereport_add_logout))
638 		CMS_OPS(cms)->cms_ereport_add_logout(hdl, nvl, nva, banknum,
639 		    status, addr, misc, mslogout, mscookie);
640 
641 }
642