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