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