17aec1d6eScindi /* 23ad553a7Sgavinm * CDDL HEADER START 33ad553a7Sgavinm * 43ad553a7Sgavinm * The contents of this file are subject to the terms of the 53ad553a7Sgavinm * Common Development and Distribution License (the "License"). 63ad553a7Sgavinm * You may not use this file except in compliance with the License. 73ad553a7Sgavinm * 83ad553a7Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93ad553a7Sgavinm * or http://www.opensolaris.org/os/licensing. 103ad553a7Sgavinm * See the License for the specific language governing permissions 113ad553a7Sgavinm * and limitations under the License. 123ad553a7Sgavinm * 133ad553a7Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 143ad553a7Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153ad553a7Sgavinm * If applicable, add the following below this CDDL HEADER, with the 163ad553a7Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 173ad553a7Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 183ad553a7Sgavinm * 193ad553a7Sgavinm * CDDL HEADER END 203ad553a7Sgavinm */ 213ad553a7Sgavinm 223ad553a7Sgavinm /* 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 /* 52*8a40a695Sgavinm * If cleared for debugging we will not attempt to load a model-specific 53*8a40a695Sgavinm * cpu module but will load the generic cpu module instead. 54*8a40a695Sgavinm */ 55*8a40a695Sgavinm int cmi_force_generic = 0; 56*8a40a695Sgavinm 57*8a40a695Sgavinm /* 587aec1d6eScindi * If cleared for debugging, we will suppress panicking on fatal hardware 597aec1d6eScindi * errors. This should *only* be used for debugging; it use can and will 607aec1d6eScindi * cause data corruption if actual hardware errors are detected by the system. 617aec1d6eScindi */ 627aec1d6eScindi int cmi_panic_on_uncorrectable_error = 1; 637aec1d6eScindi 647aec1d6eScindi static cmi_t *cmi_list; 657aec1d6eScindi static kmutex_t cmi_load_lock; 667aec1d6eScindi 677aec1d6eScindi static int 687aec1d6eScindi cmi_cpu_match(cpu_t *c1, cpu_t *c2) 697aec1d6eScindi { 707aec1d6eScindi return (cpuid_getfamily(c1) == cpuid_getfamily(c2) && 717aec1d6eScindi cpuid_getmodel(c1) == cpuid_getmodel(c2) && 727aec1d6eScindi cpuid_getstep(c1) == cpuid_getstep(c2) && 737aec1d6eScindi strcmp(cpuid_getvendorstr(c1), cpuid_getvendorstr(c2)) == 0); 747aec1d6eScindi } 757aec1d6eScindi 767aec1d6eScindi static cmi_t * 777aec1d6eScindi cmi_load_modctl(modctl_t *modp) 787aec1d6eScindi { 797aec1d6eScindi uintptr_t ops; 807aec1d6eScindi cmi_t *cmi; 817aec1d6eScindi 827aec1d6eScindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 837aec1d6eScindi 847aec1d6eScindi for (cmi = cmi_list; cmi != NULL; cmi = cmi->cmi_next) { 857aec1d6eScindi if (cmi->cmi_modp == modp) 867aec1d6eScindi return (cmi); 877aec1d6eScindi } 887aec1d6eScindi 897aec1d6eScindi if ((ops = modlookup_by_modctl(modp, "_cmi_ops")) == NULL) { 907aec1d6eScindi cmn_err(CE_WARN, "CPU module %s is invalid: no _cmi_ops " 917aec1d6eScindi "found\n", modp->mod_modname); 927aec1d6eScindi return (NULL); 937aec1d6eScindi } 947aec1d6eScindi 957aec1d6eScindi /* 967aec1d6eScindi * Hold the module in memory. We call to CPU modules without using the 977aec1d6eScindi * stubs mechanism, so these modules must be manually held in memory. 987aec1d6eScindi * The mod_ref acts as if another loaded module has a dependency on us. 997aec1d6eScindi */ 1007aec1d6eScindi mutex_enter(&mod_lock); 1017aec1d6eScindi modp->mod_ref++; 1027aec1d6eScindi mutex_exit(&mod_lock); 1037aec1d6eScindi 1047aec1d6eScindi cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP); 1057aec1d6eScindi cmi->cmi_ops = (const cmi_ops_t *)ops; 1067aec1d6eScindi cmi->cmi_modp = modp; 1077aec1d6eScindi 1087aec1d6eScindi cmi->cmi_next = cmi_list; 1097aec1d6eScindi cmi_list = cmi; 1107aec1d6eScindi 1117aec1d6eScindi return (cmi); 1127aec1d6eScindi } 1137aec1d6eScindi 1147aec1d6eScindi static cmi_t * 1157aec1d6eScindi cmi_load_module(cpu_t *cp) 1167aec1d6eScindi { 1177aec1d6eScindi modctl_t *modp; 1187aec1d6eScindi cmi_t *cmi; 1197aec1d6eScindi int i, modid; 1207aec1d6eScindi uint_t s[3]; 1217aec1d6eScindi 1227aec1d6eScindi /* 1237aec1d6eScindi * Look to see if we've already got a module loaded for a CPU just 1247aec1d6eScindi * like this one. If we do, then we'll re-use it. 1257aec1d6eScindi */ 1267aec1d6eScindi ASSERT(MUTEX_HELD(&cmi_load_lock)); 1277aec1d6eScindi mutex_enter(&cpu_lock); 1287aec1d6eScindi 1297aec1d6eScindi for (i = 0; i < NCPU; i++) { 1307aec1d6eScindi cpu_t *cp2 = cpu[i]; 1317aec1d6eScindi 1327aec1d6eScindi if (cp2 != NULL && cp2 != cp && 1337aec1d6eScindi cp2->cpu_m.mcpu_cmi != NULL && cmi_cpu_match(cp, cp2)) { 1347aec1d6eScindi mutex_exit(&cpu_lock); 1357aec1d6eScindi return (cp2->cpu_m.mcpu_cmi); 1367aec1d6eScindi } 1377aec1d6eScindi } 1387aec1d6eScindi 1397aec1d6eScindi mutex_exit(&cpu_lock); 1407aec1d6eScindi 1417aec1d6eScindi /* 1427aec1d6eScindi * If we can't find a match, attempt to load the appropriate module. 1437aec1d6eScindi * If that also fails, try to load the generic CPU module. 1447aec1d6eScindi */ 1457aec1d6eScindi s[0] = cpuid_getfamily(cp); 1467aec1d6eScindi s[1] = cpuid_getmodel(cp); 1477aec1d6eScindi s[2] = cpuid_getstep(cp); 1487aec1d6eScindi 1497aec1d6eScindi modid = modload_qualified(CPUMOD_SUBDIR, CPUMOD_PREFIX, 1507aec1d6eScindi cpuid_getvendorstr(cp), ".", s, sizeof (s) / sizeof (s[0])); 1517aec1d6eScindi 1527aec1d6eScindi if (modid == -1) 1537aec1d6eScindi modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic"); 1547aec1d6eScindi 1557aec1d6eScindi if (modid == -1) 1567aec1d6eScindi return (NULL); 1577aec1d6eScindi 1587aec1d6eScindi modp = mod_hold_by_id(modid); 1597aec1d6eScindi cmi = cmi_load_modctl(modp); 1607aec1d6eScindi mod_release_mod(modp); 1617aec1d6eScindi 1627aec1d6eScindi return (cmi); 1637aec1d6eScindi } 1647aec1d6eScindi 1657aec1d6eScindi static cmi_t * 1667aec1d6eScindi cmi_load_generic(void) 1677aec1d6eScindi { 1687aec1d6eScindi modctl_t *modp; 1697aec1d6eScindi cmi_t *cmi; 1707aec1d6eScindi int modid; 1717aec1d6eScindi 1727aec1d6eScindi if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1) 1737aec1d6eScindi return (NULL); 1747aec1d6eScindi 1757aec1d6eScindi modp = mod_hold_by_id(modid); 1767aec1d6eScindi cmi = cmi_load_modctl(modp); 1777aec1d6eScindi mod_release_mod(modp); 1787aec1d6eScindi 1797aec1d6eScindi return (cmi); 1807aec1d6eScindi } 1817aec1d6eScindi 1827aec1d6eScindi /* 1837aec1d6eScindi * Load a CPU module for the specified CPU, and then call its cmi_init routine. 1847aec1d6eScindi * If the module returns ENOTSUP, try using the generic CPU module instead. 1857aec1d6eScindi * If all else fails, we return -1 and the caller will panic or halt. 1867aec1d6eScindi */ 1877aec1d6eScindi int 1887aec1d6eScindi cmi_load(cpu_t *cp) 1897aec1d6eScindi { 1907aec1d6eScindi int err = ENOENT; 1917aec1d6eScindi cmi_t *cmi; 1927aec1d6eScindi void *data; 1937aec1d6eScindi 1947aec1d6eScindi mutex_enter(&cmi_load_lock); 1957aec1d6eScindi 196*8a40a695Sgavinm if (!cmi_force_generic && ( 197*8a40a695Sgavinm ((cmi = cmi_load_module(cp)) == NULL) || 198*8a40a695Sgavinm ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && 199*8a40a695Sgavinm err != ENOTSUP))) { 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 206*8a40a695Sgavinm if ((cmi_force_generic || err != 0) && 207*8a40a695Sgavinm ((cmi = cmi_load_generic()) == NULL || 2087aec1d6eScindi (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) { 2097aec1d6eScindi cmn_err(CE_WARN, "CPU module %s failed to init CPU %d: err=%d", 2107aec1d6eScindi cmi ? cmi->cmi_modp->mod_modname : "<>", cp->cpu_id, err); 2117aec1d6eScindi mutex_exit(&cmi_load_lock); 2127aec1d6eScindi return (-1); 2137aec1d6eScindi } 2147aec1d6eScindi 2157aec1d6eScindi ASSERT(cp->cpu_m.mcpu_cmi == NULL); 2167aec1d6eScindi cp->cpu_m.mcpu_cmi = cmi; 2177aec1d6eScindi cp->cpu_m.mcpu_cmidata = data; 2187aec1d6eScindi 2197aec1d6eScindi cmi->cmi_refcnt++; 2207aec1d6eScindi mutex_exit(&cmi_load_lock); 2217aec1d6eScindi 2227aec1d6eScindi if (boothowto & RB_VERBOSE) { 2237aec1d6eScindi printf("cpuid %d: initialized cpumod: %s\n", 2247aec1d6eScindi cp->cpu_id, cmi->cmi_modp->mod_modname); 2257aec1d6eScindi } 2267aec1d6eScindi 2277aec1d6eScindi return (0); 2287aec1d6eScindi } 2297aec1d6eScindi 2307aec1d6eScindi void 2317aec1d6eScindi cmi_init(void) 2327aec1d6eScindi { 2337aec1d6eScindi if (cmi_load(CPU) < 0) 2347aec1d6eScindi panic("failed to load module for CPU %u", CPU->cpu_id); 2357aec1d6eScindi } 2367aec1d6eScindi 2377aec1d6eScindi void 2387aec1d6eScindi cmi_post_init(void) 2397aec1d6eScindi { 2407aec1d6eScindi CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU)); 2417aec1d6eScindi } 2427aec1d6eScindi 243*8a40a695Sgavinm /* 244*8a40a695Sgavinm * Called just once from start_other_cpus when all processors are started. 245*8a40a695Sgavinm * This will not be called for each cpu, so the registered op must not 246*8a40a695Sgavinm * assume it is called as such. 247*8a40a695Sgavinm */ 2487aec1d6eScindi void 2493ad553a7Sgavinm cmi_post_mpstartup(void) 2503ad553a7Sgavinm { 2513ad553a7Sgavinm CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU)); 2523ad553a7Sgavinm } 2533ad553a7Sgavinm 2543ad553a7Sgavinm void 2557aec1d6eScindi cmi_faulted_enter(cpu_t *cp) 2567aec1d6eScindi { 2577aec1d6eScindi CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp)); 2587aec1d6eScindi } 2597aec1d6eScindi 2607aec1d6eScindi void 2617aec1d6eScindi cmi_faulted_exit(cpu_t *cp) 2627aec1d6eScindi { 2637aec1d6eScindi CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp)); 2647aec1d6eScindi } 2657aec1d6eScindi 2667aec1d6eScindi int 267*8a40a695Sgavinm cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig) 2687aec1d6eScindi { 269*8a40a695Sgavinm return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen, 270*8a40a695Sgavinm cscontig)); 2717aec1d6eScindi } 2727aec1d6eScindi 2737aec1d6eScindi void 2747aec1d6eScindi cmi_mca_init(void) 2757aec1d6eScindi { 2767aec1d6eScindi CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU)); 2777aec1d6eScindi } 2787aec1d6eScindi 2797aec1d6eScindi void 2807aec1d6eScindi cmi_mca_trap(struct regs *rp) 2817aec1d6eScindi { 2827aec1d6eScindi if (CMI_OPS(CPU)->cmi_mca_trap(CMI_DATA(CPU), rp)) { 2837aec1d6eScindi if (cmi_panic_on_uncorrectable_error) 2847aec1d6eScindi fm_panic("Unrecoverable Machine-Check Exception"); 2857aec1d6eScindi else 2867aec1d6eScindi cmn_err(CE_WARN, "suppressing panic from fatal #mc"); 2877aec1d6eScindi } 2887aec1d6eScindi } 2897aec1d6eScindi 2907aec1d6eScindi int 2917aec1d6eScindi cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs) 2927aec1d6eScindi { 2937aec1d6eScindi int err; 2947aec1d6eScindi 2957aec1d6eScindi kpreempt_disable(); 2967aec1d6eScindi err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs); 2977aec1d6eScindi kpreempt_enable(); 2987aec1d6eScindi 2997aec1d6eScindi return (err); 3007aec1d6eScindi } 3017aec1d6eScindi 3027aec1d6eScindi void 3037aec1d6eScindi cmi_mca_poke(void) 3047aec1d6eScindi { 3057aec1d6eScindi CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU)); 3067aec1d6eScindi } 3077aec1d6eScindi 3087aec1d6eScindi void 3097aec1d6eScindi cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata) 3107aec1d6eScindi { 3117aec1d6eScindi CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata); 3127aec1d6eScindi } 3137aec1d6eScindi 3147aec1d6eScindi int 3157aec1d6eScindi cmi_mc_patounum(uint64_t pa, uint32_t synd, int syndtype, mc_unum_t *up) 3167aec1d6eScindi { 3177aec1d6eScindi const struct cmi_mc_ops *mcops; 3187aec1d6eScindi cpu_t *cp = CPU; 3197aec1d6eScindi 3207aec1d6eScindi if (CMI_OPS(cp) == NULL || 3217aec1d6eScindi (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) 3227aec1d6eScindi return (-1); /* not registered yet */ 3237aec1d6eScindi 3247aec1d6eScindi return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, synd, syndtype, up)); 3257aec1d6eScindi } 3267aec1d6eScindi 3277aec1d6eScindi int 3287aec1d6eScindi cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap) 3297aec1d6eScindi { 3307aec1d6eScindi const struct cmi_mc_ops *mcops; 3317aec1d6eScindi cpu_t *cp = CPU; 3327aec1d6eScindi 3337aec1d6eScindi if (up != NULL && nvl != NULL) 3347aec1d6eScindi return (-1); /* only convert from one or the other form */ 3357aec1d6eScindi 3367aec1d6eScindi if (CMI_OPS(cp) == NULL || 3377aec1d6eScindi (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) 3387aec1d6eScindi return (-1); /* not registered yet */ 3397aec1d6eScindi 3407aec1d6eScindi return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap)); 3417aec1d6eScindi } 342