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