11642Sgavinm /* 21642Sgavinm * CDDL HEADER START 31642Sgavinm * 41642Sgavinm * The contents of this file are subject to the terms of the 51642Sgavinm * Common Development and Distribution License (the "License"). 61642Sgavinm * You may not use this file except in compliance with the License. 71642Sgavinm * 81642Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91642Sgavinm * or http://www.opensolaris.org/os/licensing. 101642Sgavinm * See the License for the specific language governing permissions 111642Sgavinm * and limitations under the License. 121642Sgavinm * 131642Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 141642Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151642Sgavinm * If applicable, add the following below this CDDL HEADER, with the 161642Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 171642Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 181642Sgavinm * 191642Sgavinm * CDDL HEADER END 201642Sgavinm */ 211642Sgavinm 221414Scindi /* 231414Scindi * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 241414Scindi * Use is subject to license terms. 251414Scindi */ 261414Scindi 271414Scindi #pragma ident "%Z%%M% %I% %E% SMI" 281414Scindi 291414Scindi /* 301414Scindi * Public interface to routines implemented by CPU modules 311414Scindi */ 321414Scindi 331414Scindi #include <sys/x86_archext.h> 341414Scindi #include <sys/cpu_module_impl.h> 351414Scindi #include <sys/fm/util.h> 361414Scindi #include <sys/reboot.h> 371414Scindi #include <sys/modctl.h> 381414Scindi #include <sys/param.h> 391414Scindi #include <sys/cmn_err.h> 401414Scindi #include <sys/systm.h> 411414Scindi #include <sys/types.h> 421414Scindi 431414Scindi #define CPUMOD_SUBDIR "cpu" 441414Scindi #define CPUMOD_PREFIX "cpu" 451414Scindi 461414Scindi #define CMI_OPS(cpu) \ 471414Scindi (cpu)->cpu_m.mcpu_cmi->cmi_ops 481414Scindi #define CMI_DATA(cpu) \ 491414Scindi (cpu)->cpu_m.mcpu_cmidata 501414Scindi 511414Scindi /* 522869Sgavinm * If cleared for debugging we will not attempt to load a model-specific 532869Sgavinm * cpu module but will load the generic cpu module instead. 542869Sgavinm */ 552869Sgavinm int cmi_force_generic = 0; 562869Sgavinm 572869Sgavinm /* 581414Scindi * If cleared for debugging, we will suppress panicking on fatal hardware 591414Scindi * errors. This should *only* be used for debugging; it use can and will 601414Scindi * cause data corruption if actual hardware errors are detected by the system. 611414Scindi */ 621414Scindi int cmi_panic_on_uncorrectable_error = 1; 631414Scindi 641414Scindi static cmi_t *cmi_list; 651414Scindi static kmutex_t cmi_load_lock; 661414Scindi 671414Scindi static int 681414Scindi cmi_cpu_match(cpu_t *c1, cpu_t *c2) 691414Scindi { 701414Scindi return (cpuid_getfamily(c1) == cpuid_getfamily(c2) && 711414Scindi cpuid_getmodel(c1) == cpuid_getmodel(c2) && 721414Scindi cpuid_getstep(c1) == cpuid_getstep(c2) && 731414Scindi strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0); 741414Scindi } 751414Scindi 761414Scindi static cmi_t * 771414Scindi cmi_load_modctl(modctl_t *modp) 781414Scindi { 791414Scindi uintptr_t ops; 801414Scindi cmi_t *cmi; 811414Scindi 821414Scindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 831414Scindi 841414Scindi for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) { 851414Scindi if (cmi->cmi_modp == modp) 861414Scindi return (cmi); 871414Scindi } 881414Scindi 891414Scindi if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) { 901414Scindi cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops " 911414Scindi "found\n", modp->mod_modname); 921414Scindi return (NULL); 931414Scindi } 941414Scindi 951414Scindi /* 961414Scindi * Hold the module in memory. We call to CPU modules without using the 971414Scindi * stubs mechanism, so these modules must be manually held in memory. 981414Scindi * The mod_ref acts as if another loaded module has a dependency on us. 991414Scindi */ 1001414Scindi mutex_enter(&mod_lock); 1011414Scindi modp->mod_ref++; 1021414Scindi mutex_exit(&mod_lock); 1031414Scindi 1041414Scindi cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP); 1051414Scindi cmi->cmi_ops = (const cmi_ops_t *)ops; 1061414Scindi cmi->cmi_modp = modp; 1071414Scindi 1081414Scindi cmi->cmi_next = cmi_list; 1091414Scindi cmi_list = cmi; 1101414Scindi 1111414Scindi return (cmi); 1121414Scindi } 1131414Scindi 1141414Scindi static cmi_t * 1151414Scindi cmi_load_module(cpu_t *cp) 1161414Scindi { 1171414Scindi modctl_t *modp; 1181414Scindi cmi_t *cmi; 1191414Scindi int i, modid; 1201414Scindi uint_t s[3]; 1211414Scindi 1221414Scindi /* 1231414Scindi * Look to see if we've already got a module loaded for a CPU just 1241414Scindi * like this one. If we do, then we'll re-use it. 1251414Scindi */ 1261414Scindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 1271414Scindi mutex_enter(&cpu_lock); 1281414Scindi 1291414Scindi for (i = 0; i < NCPU; i++) { 1301414Scindi cpu_t *cp2 = cpu[i]; 1311414Scindi 1321414Scindi if (cp2 != NULL && cp2 != cp && 1331414Scindi cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) { 1341414Scindi mutex_exit(&cpu_lock); 1351414Scindi return (cp2->cpu_m.mcpu_cmi); 1361414Scindi } 1371414Scindi } 1381414Scindi 1391414Scindi mutex_exit(&cpu_lock); 1401414Scindi 1411414Scindi /* 1421414Scindi * If we can't find a match, attempt to load the appropriate module. 1431414Scindi * If that also fails, try to load the generic CPU module. 1441414Scindi */ 1451414Scindi s[0] = cpuid_getfamily(cp); 1461414Scindi s[1] = cpuid_getmodel(cp); 1471414Scindi s[2] = cpuid_getstep(cp); 1481414Scindi 1491414Scindi modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX, 1501414Scindi cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0])); 1511414Scindi 1521414Scindi if (modid == -1) 1531414Scindi modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic"); 1541414Scindi 1551414Scindi if (modid == -1) 1561414Scindi return (NULL); 1571414Scindi 1581414Scindi modp = mod_hold_by_id(modid); 1591414Scindi cmi = cmi_load_modctl(modp); 1601414Scindi mod_release_mod(modp); 1611414Scindi 1621414Scindi return (cmi); 1631414Scindi } 1641414Scindi 1651414Scindi static cmi_t * 1661414Scindi cmi_load_generic(void) 1671414Scindi { 1681414Scindi modctl_t *modp; 1691414Scindi cmi_t *cmi; 1701414Scindi int modid; 1711414Scindi 1721414Scindi if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1) 1731414Scindi return (NULL); 1741414Scindi 1751414Scindi modp = mod_hold_by_id(modid); 1761414Scindi cmi = cmi_load_modctl(modp); 1771414Scindi mod_release_mod(modp); 1781414Scindi 1791414Scindi return (cmi); 1801414Scindi } 1811414Scindi 1821414Scindi /* 1831414Scindi * Load a CPU module for the specified CPU, and then call its cmi_init routine. 1841414Scindi * If the module returns ENOTSUP, try using the generic CPU module instead. 1851414Scindi * If all else fails, we return -1 and the caller will panic or halt. 1861414Scindi */ 1871414Scindi int 1881414Scindi cmi_load(cpu_t *cp) 1891414Scindi { 1901414Scindi int err = ENOENT; 1911414Scindi cmi_t *cmi; 1921414Scindi void *data; 1931414Scindi 1941414Scindi mutex_enter(&cmi_load_lock); 1951414Scindi 1962869Sgavinm if (!cmi_force_generic && ( 1972869Sgavinm ((cmi = cmi_load_module(cp)) == NULL) || 1982869Sgavinm ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && 1992869Sgavinm err != ENOTSUP))) { 2001414Scindi cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", 2011414Scindi cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); 2021414Scindi mutex_exit(&cmi_load_lock); 2031414Scindi return (-1); 2041414Scindi } 2051414Scindi 2062869Sgavinm if ((cmi_force_generic || err != 0) && 2072869Sgavinm ((cmi = cmi_load_generic()) == NULL || 2081414Scindi (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) { 2091414Scindi cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", 2101414Scindi cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); 2111414Scindi mutex_exit(&cmi_load_lock); 2121414Scindi return (-1); 2131414Scindi } 2141414Scindi 2151414Scindi ASSERT(cp->cpu_m.mcpu_cmi == NULL); 2161414Scindi cp->cpu_m.mcpu_cmi = cmi; 2171414Scindi cp->cpu_m.mcpu_cmidata = data; 2181414Scindi 2191414Scindi cmi->cmi_refcnt++; 2201414Scindi mutex_exit(&cmi_load_lock); 2211414Scindi 2221414Scindi if (boothowto & RB_VERBOSE) { 2231414Scindi printf("cpuid %d: initialized cpumod: %s\n", 2241414Scindi cp->cpu_id, cmi->cmi_modp->mod_modname); 2251414Scindi } 2261414Scindi 2271414Scindi return (0); 2281414Scindi } 2291414Scindi 2301414Scindi void 2311414Scindi cmi_init(void) 2321414Scindi { 2331414Scindi if (cmi_load(CPU) < 0) 2341414Scindi panic("failed to load module for CPU %u", CPU->cpu_id); 2351414Scindi } 2361414Scindi 2371414Scindi void 2381414Scindi cmi_post_init(void) 2391414Scindi { 2401414Scindi CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU)); 2411414Scindi } 2421414Scindi 2432869Sgavinm /* 2442869Sgavinm * Called just once from start_other_cpus when all processors are started. 2452869Sgavinm * This will not be called for each cpu, so the registered op must not 2462869Sgavinm * assume it is called as such. 2472869Sgavinm */ 2481414Scindi void 2491642Sgavinm cmi_post_mpstartup(void) 2501642Sgavinm { 2511642Sgavinm CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU)); 2521642Sgavinm } 2531642Sgavinm 2541642Sgavinm void 2551414Scindi cmi_faulted_enter(cpu_t *cp) 2561414Scindi { 2571414Scindi CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp)); 2581414Scindi } 2591414Scindi 2601414Scindi void 2611414Scindi cmi_faulted_exit(cpu_t *cp) 2621414Scindi { 2631414Scindi CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp)); 2641414Scindi } 2651414Scindi 2661414Scindi int 2672869Sgavinm cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig) 2681414Scindi { 2692869Sgavinm return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen, 2702869Sgavinm cscontig)); 2711414Scindi } 2721414Scindi 2731414Scindi void 2741414Scindi cmi_mca_init(void) 2751414Scindi { 2761414Scindi CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU)); 2771414Scindi } 2781414Scindi 2791414Scindi void 2801414Scindi cmi_mca_trap(struct regs *rp) 2811414Scindi { 2821414Scindi if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) { 2831414Scindi if (cmi_panic_on_uncorrectable_error) 2841414Scindi fm_panic("Unrecoverable Machine-Check Exception"); 2851414Scindi else 2861414Scindi cmn_err(CE_WARN, "suppressing panic from fatal #mc"); 2871414Scindi } 2881414Scindi } 2891414Scindi 2901414Scindi int 2911414Scindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs) 2921414Scindi { 2931414Scindi int err; 2941414Scindi 2951414Scindi kpreempt_disable(); 2961414Scindi err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs); 2971414Scindi kpreempt_enable(); 2981414Scindi 2991414Scindi return (err); 3001414Scindi } 3011414Scindi 3021414Scindi void 3031414Scindi cmi_mca_poke(void) 3041414Scindi { 3051414Scindi CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU)); 3061414Scindi } 3071414Scindi 3081414Scindi void 3091414Scindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata) 3101414Scindi { 3111414Scindi CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata); 3121414Scindi } 3131414Scindi 3141414Scindi int 315*3164Sgavinm cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd, 316*3164Sgavinm int syndtype, mc_unum_t *up) 3171414Scindi { 3181414Scindi const struct cmi_mc_ops *mcops; 3191414Scindi cpu_t *cp = CPU; 3201414Scindi 3211414Scindi if (CMI_OPS(cp) == NULL || 3221414Scindi (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) 3231414Scindi return (-1); /* not registered yet */ 3241414Scindi 325*3164Sgavinm return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, valid_hi, valid_lo, 326*3164Sgavinm synd, syndtype, up)); 3271414Scindi } 3281414Scindi 3291414Scindi int 3301414Scindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap) 3311414Scindi { 3321414Scindi const struct cmi_mc_ops *mcops; 3331414Scindi cpu_t *cp = CPU; 3341414Scindi 3351414Scindi if (up != NULL && nvl != NULL) 3361414Scindi return (-1); /* only convert from one or the other form */ 3371414Scindi 3381414Scindi if (CMI_OPS(cp) == NULL || 3391414Scindi (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) 3401414Scindi return (-1); /* not registered yet */ 3411414Scindi 3421414Scindi return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap)); 3431414Scindi } 344