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