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