1*3ad553a7Sgavinm /* 2*3ad553a7Sgavinm * CDDL HEADER START 3*3ad553a7Sgavinm * 4*3ad553a7Sgavinm * The contents of this file are subject to the terms of the 5*3ad553a7Sgavinm * Common Development and Distribution License (the "License"). 6*3ad553a7Sgavinm * You may not use this file except in compliance with the License. 7*3ad553a7Sgavinm * 8*3ad553a7Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3ad553a7Sgavinm * or http://www.opensolaris.org/os/licensing. 10*3ad553a7Sgavinm * See the License for the specific language governing permissions 11*3ad553a7Sgavinm * and limitations under the License. 12*3ad553a7Sgavinm * 13*3ad553a7Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 14*3ad553a7Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3ad553a7Sgavinm * If applicable, add the following below this CDDL HEADER, with the 16*3ad553a7Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 17*3ad553a7Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 18*3ad553a7Sgavinm * 19*3ad553a7Sgavinm * CDDL HEADER END 20*3ad553a7Sgavinm */ 21*3ad553a7Sgavinm 227aec1d6eScindi /* 237aec1d6eScindi * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 247aec1d6eScindi * Use is subject to license terms. 257aec1d6eScindi */ 267aec1d6eScindi 277aec1d6eScindi #pragma ident "%Z%%M% %I% %E% SMI" 287aec1d6eScindi 297aec1d6eScindi /* 307aec1d6eScindi * Public interface to routines implemented by CPU modules 317aec1d6eScindi */ 327aec1d6eScindi 337aec1d6eScindi #include <sys/x86_archext.h> 347aec1d6eScindi #include <sys/cpu_module_impl.h> 357aec1d6eScindi #include <sys/fm/util.h> 367aec1d6eScindi #include <sys/reboot.h> 377aec1d6eScindi #include <sys/modctl.h> 387aec1d6eScindi #include <sys/param.h> 397aec1d6eScindi #include <sys/cmn_err.h> 407aec1d6eScindi #include <sys/systm.h> 417aec1d6eScindi #include <sys/types.h> 427aec1d6eScindi 437aec1d6eScindi #define CPUMOD_SUBDIR "cpu" 447aec1d6eScindi #define CPUMOD_PREFIX "cpu" 457aec1d6eScindi 467aec1d6eScindi #define CMI_OPS(cpu) \ 477aec1d6eScindi (cpu)->cpu_m.mcpu_cmi->cmi_ops 487aec1d6eScindi #define CMI_DATA(cpu) \ 497aec1d6eScindi (cpu)->cpu_m.mcpu_cmidata 507aec1d6eScindi 517aec1d6eScindi /* 527aec1d6eScindi * If cleared for debugging, we will suppress panicking on fatal hardware 537aec1d6eScindi * errors. This should *only* be used for debugging; it use can and will 547aec1d6eScindi * cause data corruption if actual hardware errors are detected by the system. 557aec1d6eScindi */ 567aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1; 577aec1d6eScindi 587aec1d6eScindi static cmi_t *cmi_list; 597aec1d6eScindi static kmutex_t cmi_load_lock; 607aec1d6eScindi 617aec1d6eScindi static int 627aec1d6eScindi cmi_cpu_match(cpu_t *c1, cpu_t *c2) 637aec1d6eScindi { 647aec1d6eScindi return (cpuid_getfamily(c1) == cpuid_getfamily(c2) && 657aec1d6eScindi cpuid_getmodel(c1) == cpuid_getmodel(c2) && 667aec1d6eScindi cpuid_getstep(c1) == cpuid_getstep(c2) && 677aec1d6eScindi strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0); 687aec1d6eScindi } 697aec1d6eScindi 707aec1d6eScindi static cmi_t * 717aec1d6eScindi cmi_load_modctl(modctl_t *modp) 727aec1d6eScindi { 737aec1d6eScindi uintptr_t ops; 747aec1d6eScindi cmi_t *cmi; 757aec1d6eScindi 767aec1d6eScindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 777aec1d6eScindi 787aec1d6eScindi for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) { 797aec1d6eScindi if (cmi->cmi_modp == modp) 807aec1d6eScindi return (cmi); 817aec1d6eScindi } 827aec1d6eScindi 837aec1d6eScindi if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) { 847aec1d6eScindi cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops " 857aec1d6eScindi "found\n", modp->mod_modname); 867aec1d6eScindi return (NULL); 877aec1d6eScindi } 887aec1d6eScindi 897aec1d6eScindi /* 907aec1d6eScindi * Hold the module in memory. We call to CPU modules without using the 917aec1d6eScindi * stubs mechanism, so these modules must be manually held in memory. 927aec1d6eScindi * The mod_ref acts as if another loaded module has a dependency on us. 937aec1d6eScindi */ 947aec1d6eScindi mutex_enter(&mod_lock); 957aec1d6eScindi modp->mod_ref++; 967aec1d6eScindi mutex_exit(&mod_lock); 977aec1d6eScindi 987aec1d6eScindi cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP); 997aec1d6eScindi cmi->cmi_ops = (const cmi_ops_t *)ops; 1007aec1d6eScindi cmi->cmi_modp = modp; 1017aec1d6eScindi 1027aec1d6eScindi cmi->cmi_next = cmi_list; 1037aec1d6eScindi cmi_list = cmi; 1047aec1d6eScindi 1057aec1d6eScindi return (cmi); 1067aec1d6eScindi } 1077aec1d6eScindi 1087aec1d6eScindi static cmi_t * 1097aec1d6eScindi cmi_load_module(cpu_t *cp) 1107aec1d6eScindi { 1117aec1d6eScindi modctl_t *modp; 1127aec1d6eScindi cmi_t *cmi; 1137aec1d6eScindi int i, modid; 1147aec1d6eScindi uint_t s[3]; 1157aec1d6eScindi 1167aec1d6eScindi /* 1177aec1d6eScindi * Look to see if we've already got a module loaded for a CPU just 1187aec1d6eScindi * like this one. If we do, then we'll re-use it. 1197aec1d6eScindi */ 1207aec1d6eScindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 1217aec1d6eScindi mutex_enter(&cpu_lock); 1227aec1d6eScindi 1237aec1d6eScindi for (i = 0; i < NCPU; i++) { 1247aec1d6eScindi cpu_t *cp2 = cpu[i]; 1257aec1d6eScindi 1267aec1d6eScindi if (cp2 != NULL && cp2 != cp && 1277aec1d6eScindi cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) { 1287aec1d6eScindi mutex_exit(&cpu_lock); 1297aec1d6eScindi return (cp2->cpu_m.mcpu_cmi); 1307aec1d6eScindi } 1317aec1d6eScindi } 1327aec1d6eScindi 1337aec1d6eScindi mutex_exit(&cpu_lock); 1347aec1d6eScindi 1357aec1d6eScindi /* 1367aec1d6eScindi * If we can't find a match, attempt to load the appropriate module. 1377aec1d6eScindi * If that also fails, try to load the generic CPU module. 1387aec1d6eScindi */ 1397aec1d6eScindi s[0] = cpuid_getfamily(cp); 1407aec1d6eScindi s[1] = cpuid_getmodel(cp); 1417aec1d6eScindi s[2] = cpuid_getstep(cp); 1427aec1d6eScindi 1437aec1d6eScindi modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX, 1447aec1d6eScindi cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0])); 1457aec1d6eScindi 1467aec1d6eScindi if (modid == -1) 1477aec1d6eScindi modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic"); 1487aec1d6eScindi 1497aec1d6eScindi if (modid == -1) 1507aec1d6eScindi return (NULL); 1517aec1d6eScindi 1527aec1d6eScindi modp = mod_hold_by_id(modid); 1537aec1d6eScindi cmi = cmi_load_modctl(modp); 1547aec1d6eScindi mod_release_mod(modp); 1557aec1d6eScindi 1567aec1d6eScindi return (cmi); 1577aec1d6eScindi } 1587aec1d6eScindi 1597aec1d6eScindi static cmi_t * 1607aec1d6eScindi cmi_load_generic(void) 1617aec1d6eScindi { 1627aec1d6eScindi modctl_t *modp; 1637aec1d6eScindi cmi_t *cmi; 1647aec1d6eScindi int modid; 1657aec1d6eScindi 1667aec1d6eScindi if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1) 1677aec1d6eScindi return (NULL); 1687aec1d6eScindi 1697aec1d6eScindi modp = mod_hold_by_id(modid); 1707aec1d6eScindi cmi = cmi_load_modctl(modp); 1717aec1d6eScindi mod_release_mod(modp); 1727aec1d6eScindi 1737aec1d6eScindi return (cmi); 1747aec1d6eScindi } 1757aec1d6eScindi 1767aec1d6eScindi /* 1777aec1d6eScindi * Load a CPU module for the specified CPU, and then call its cmi_init routine. 1787aec1d6eScindi * If the module returns ENOTSUP, try using the generic CPU module instead. 1797aec1d6eScindi * If all else fails, we return -1 and the caller will panic or halt. 1807aec1d6eScindi */ 1817aec1d6eScindi int 1827aec1d6eScindi cmi_load(cpu_t *cp) 1837aec1d6eScindi { 1847aec1d6eScindi int err = ENOENT; 1857aec1d6eScindi cmi_t *cmi; 1867aec1d6eScindi void *data; 1877aec1d6eScindi 1887aec1d6eScindi mutex_enter(&cmi_load_lock); 1897aec1d6eScindi 1907aec1d6eScindi if ((cmi = cmi_load_module(cp)) == NULL || ( 1917aec1d6eScindi (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && err != ENOTSUP)) { 1927aec1d6eScindi cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", 1937aec1d6eScindi cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); 1947aec1d6eScindi mutex_exit(&cmi_load_lock); 1957aec1d6eScindi return (-1); 1967aec1d6eScindi } 1977aec1d6eScindi 1987aec1d6eScindi if (err != 0 && ((cmi = cmi_load_generic()) == NULL || 1997aec1d6eScindi (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) { 2007aec1d6eScindi cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", 2017aec1d6eScindi cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); 2027aec1d6eScindi mutex_exit(&cmi_load_lock); 2037aec1d6eScindi return (-1); 2047aec1d6eScindi } 2057aec1d6eScindi 2067aec1d6eScindi ASSERT(cp->cpu_m.mcpu_cmi == NULL); 2077aec1d6eScindi cp->cpu_m.mcpu_cmi = cmi; 2087aec1d6eScindi cp->cpu_m.mcpu_cmidata = data; 2097aec1d6eScindi 2107aec1d6eScindi cmi->cmi_refcnt++; 2117aec1d6eScindi mutex_exit(&cmi_load_lock); 2127aec1d6eScindi 2137aec1d6eScindi if (boothowto & RB_VERBOSE) { 2147aec1d6eScindi printf("cpuid %d: initialized cpumod: %s\n", 2157aec1d6eScindi cp->cpu_id, cmi->cmi_modp->mod_modname); 2167aec1d6eScindi } 2177aec1d6eScindi 2187aec1d6eScindi return (0); 2197aec1d6eScindi } 2207aec1d6eScindi 2217aec1d6eScindi void 2227aec1d6eScindi cmi_init(void) 2237aec1d6eScindi { 2247aec1d6eScindi if (cmi_load(CPU) < 0) 2257aec1d6eScindi panic("failed to load module for CPU %u", CPU->cpu_id); 2267aec1d6eScindi } 2277aec1d6eScindi 2287aec1d6eScindi void 2297aec1d6eScindi cmi_post_init(void) 2307aec1d6eScindi { 2317aec1d6eScindi CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU)); 2327aec1d6eScindi } 2337aec1d6eScindi 234*3ad553a7Sgavinm void 235*3ad553a7Sgavinm cmi_post_mpstartup(void) 236*3ad553a7Sgavinm { 237*3ad553a7Sgavinm CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU)); 238*3ad553a7Sgavinm } 239*3ad553a7Sgavinm 2407aec1d6eScindi void 2417aec1d6eScindi cmi_faulted_enter(cpu_t *cp) 2427aec1d6eScindi { 2437aec1d6eScindi CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp)); 2447aec1d6eScindi } 2457aec1d6eScindi 2467aec1d6eScindi void 2477aec1d6eScindi cmi_faulted_exit(cpu_t *cp) 2487aec1d6eScindi { 2497aec1d6eScindi CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp)); 2507aec1d6eScindi } 2517aec1d6eScindi 2527aec1d6eScindi int 2537aec1d6eScindi cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen) 2547aec1d6eScindi { 2557aec1d6eScindi return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen)); 2567aec1d6eScindi } 2577aec1d6eScindi 2587aec1d6eScindi void 2597aec1d6eScindi cmi_mca_init(void) 2607aec1d6eScindi { 2617aec1d6eScindi CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU)); 2627aec1d6eScindi } 2637aec1d6eScindi 2647aec1d6eScindi void 2657aec1d6eScindi cmi_mca_trap(struct regs *rp) 2667aec1d6eScindi { 2677aec1d6eScindi if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) { 2687aec1d6eScindi if (cmi_panic_on_uncorrectable_error) 2697aec1d6eScindi fm_panic("Unrecoverable Machine-Check Exception"); 2707aec1d6eScindi else 2717aec1d6eScindi cmn_err(CE_WARN, "suppressing panic from fatal #mc"); 2727aec1d6eScindi } 2737aec1d6eScindi } 2747aec1d6eScindi 2757aec1d6eScindi int 2767aec1d6eScindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs) 2777aec1d6eScindi { 2787aec1d6eScindi int err; 2797aec1d6eScindi 2807aec1d6eScindi kpreempt_disable(); 2817aec1d6eScindi err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs); 2827aec1d6eScindi kpreempt_enable(); 2837aec1d6eScindi 2847aec1d6eScindi return (err); 2857aec1d6eScindi } 2867aec1d6eScindi 2877aec1d6eScindi void 2887aec1d6eScindi cmi_mca_poke(void) 2897aec1d6eScindi { 2907aec1d6eScindi CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU)); 2917aec1d6eScindi } 2927aec1d6eScindi 2937aec1d6eScindi void 2947aec1d6eScindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata) 2957aec1d6eScindi { 2967aec1d6eScindi CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata); 2977aec1d6eScindi } 2987aec1d6eScindi 2997aec1d6eScindi int 3007aec1d6eScindi cmi_mc_patounum(uint64_t pa, uint32_t synd, int syndtype, mc_unum_t *up) 3017aec1d6eScindi { 3027aec1d6eScindi const struct cmi_mc_ops *mcops; 3037aec1d6eScindi cpu_t *cp = CPU; 3047aec1d6eScindi 3057aec1d6eScindi if (CMI_OPS(cp) == NULL || 3067aec1d6eScindi (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) 3077aec1d6eScindi return (-1); /* not registered yet */ 3087aec1d6eScindi 3097aec1d6eScindi return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, synd, syndtype, up)); 3107aec1d6eScindi } 3117aec1d6eScindi 3127aec1d6eScindi int 3137aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap) 3147aec1d6eScindi { 3157aec1d6eScindi const struct cmi_mc_ops *mcops; 3167aec1d6eScindi cpu_t *cp = CPU; 3177aec1d6eScindi 3187aec1d6eScindi if (up != NULL && nvl != NULL) 3197aec1d6eScindi return (-1); /* only convert from one or the other form */ 3207aec1d6eScindi 3217aec1d6eScindi if (CMI_OPS(cp) == NULL || 3227aec1d6eScindi (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) 3237aec1d6eScindi return (-1); /* not registered yet */ 3247aec1d6eScindi 3257aec1d6eScindi return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap)); 3267aec1d6eScindi } 327