/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright (c) 2010, Intel Corporation. * All rights reserved. */ /* * CPU Module Interface - hardware abstraction. */ #ifdef __xpv #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Variable which determines if the SMBIOS supports x86 generic topology; or * if legacy topolgy enumeration will occur. */ extern int x86gentopo_legacy; /* * Outside of this file consumers use the opaque cmi_hdl_t. This * definition is duplicated in the generic_cpu mdb module, so keep * them in-sync when making changes. */ typedef struct cmi_hdl_impl { enum cmi_hdl_class cmih_class; /* Handle nature */ const struct cmi_hdl_ops *cmih_ops; /* Operations vector */ uint_t cmih_chipid; /* Chipid of cpu resource */ uint_t cmih_procnodeid; /* Nodeid of cpu resource */ uint_t cmih_coreid; /* Core within die */ uint_t cmih_strandid; /* Thread within core */ uint_t cmih_procnodes_per_pkg; /* Nodes in a processor */ boolean_t cmih_mstrand; /* cores are multithreaded */ volatile uint32_t *cmih_refcntp; /* Reference count pointer */ uint64_t cmih_msrsrc; /* MSR data source flags */ void *cmih_hdlpriv; /* cmi_hw.c private data */ void *cmih_spec; /* cmi_hdl_{set,get}_specific */ void *cmih_cmi; /* cpu mod control structure */ void *cmih_cmidata; /* cpu mod private data */ const struct cmi_mc_ops *cmih_mcops; /* Memory-controller ops */ void *cmih_mcdata; /* Memory-controller data */ uint64_t cmih_flags; /* See CMIH_F_* below */ uint16_t cmih_smbiosid; /* SMBIOS Type 4 struct ID */ uint_t cmih_smb_chipid; /* SMBIOS factored chipid */ nvlist_t *cmih_smb_bboard; /* SMBIOS bboard nvlist */ } cmi_hdl_impl_t; #define IMPLHDL(ophdl) ((cmi_hdl_impl_t *)ophdl) #define HDLOPS(hdl) ((hdl)->cmih_ops) #define CMIH_F_INJACTV 0x1ULL #define CMIH_F_DEAD 0x2ULL /* * Ops structure for handle operations. */ struct cmi_hdl_ops { /* * These ops are required in an implementation. */ uint_t (*cmio_vendor)(cmi_hdl_impl_t *); const char *(*cmio_vendorstr)(cmi_hdl_impl_t *); uint_t (*cmio_family)(cmi_hdl_impl_t *); uint_t (*cmio_model)(cmi_hdl_impl_t *); uint_t (*cmio_stepping)(cmi_hdl_impl_t *); uint_t (*cmio_chipid)(cmi_hdl_impl_t *); uint_t (*cmio_procnodeid)(cmi_hdl_impl_t *); uint_t (*cmio_coreid)(cmi_hdl_impl_t *); uint_t (*cmio_strandid)(cmi_hdl_impl_t *); uint_t (*cmio_procnodes_per_pkg)(cmi_hdl_impl_t *); uint_t (*cmio_strand_apicid)(cmi_hdl_impl_t *); uint32_t (*cmio_chiprev)(cmi_hdl_impl_t *); const char *(*cmio_chiprevstr)(cmi_hdl_impl_t *); uint32_t (*cmio_getsockettype)(cmi_hdl_impl_t *); const char *(*cmio_getsocketstr)(cmi_hdl_impl_t *); id_t (*cmio_logical_id)(cmi_hdl_impl_t *); /* * These ops are optional in an implementation. */ ulong_t (*cmio_getcr4)(cmi_hdl_impl_t *); void (*cmio_setcr4)(cmi_hdl_impl_t *, ulong_t); cmi_errno_t (*cmio_rdmsr)(cmi_hdl_impl_t *, uint_t, uint64_t *); cmi_errno_t (*cmio_wrmsr)(cmi_hdl_impl_t *, uint_t, uint64_t); cmi_errno_t (*cmio_msrinterpose)(cmi_hdl_impl_t *, uint_t, uint64_t); void (*cmio_int)(cmi_hdl_impl_t *, int); int (*cmio_online)(cmi_hdl_impl_t *, int, int *); uint16_t (*cmio_smbiosid) (cmi_hdl_impl_t *); uint_t (*cmio_smb_chipid)(cmi_hdl_impl_t *); nvlist_t *(*cmio_smb_bboard)(cmi_hdl_impl_t *); }; static const struct cmi_hdl_ops cmi_hdl_ops; /* * Handles are looked up from contexts such as polling, injection etc * where the context is reasonably well defined (although a poller could * interrupt any old thread holding any old lock). They are also looked * up by machine check handlers, which may strike at inconvenient times * such as during handle initialization or destruction or during handle * lookup (which the #MC handler itself will also have to perform). * * So keeping handles in a linked list makes locking difficult when we * consider #MC handlers. Our solution is to have a look-up table indexed * by that which uniquely identifies a handle - chip/core/strand id - * with each entry a structure including a pointer to a handle * structure for the resource, and a reference count for the handle. * Reference counts are modified atomically. The public cmi_hdl_hold * always succeeds because this can only be used after handle creation * and before the call to destruct, so the hold count is already at least one. * In other functions that lookup a handle (cmi_hdl_lookup, cmi_hdl_any) * we must be certain that the count has not already decrmented to zero * before applying our hold. * * The table is an array of maximum number of chips defined in * CMI_CHIPID_ARR_SZ indexed by the chip id. If the chip is not present, the * entry is NULL. Each entry is a pointer to another array which contains a * list of all strands of the chip. This first level table is allocated when * first we want to populate an entry. The size of the latter (per chip) table * is CMI_MAX_STRANDS_PER_CHIP and it is populated when one of its cpus starts. * * Ideally we should only allocate to the actual number of chips, cores per * chip and strand per core. The number of chips is not available until all * of them are passed. The number of cores and strands are partially available. * For now we stick with the above approach. */ #define CMI_MAX_CHIPID_NBITS 6 /* max chipid of 63 */ #define CMI_MAX_CORES_PER_CHIP_NBITS 4 /* 16 cores per chip max */ #define CMI_MAX_STRANDS_PER_CORE_NBITS 3 /* 8 strands per core max */ #define CMI_MAX_CHIPID ((1 << (CMI_MAX_CHIPID_NBITS)) - 1) #define CMI_MAX_CORES_PER_CHIP(cbits) (1 << (cbits)) #define CMI_MAX_COREID(cbits) ((1 << (cbits)) - 1) #define CMI_MAX_STRANDS_PER_CORE(sbits) (1 << (sbits)) #define CMI_MAX_STRANDID(sbits) ((1 << (sbits)) - 1) #define CMI_MAX_STRANDS_PER_CHIP(cbits, sbits) \ (CMI_MAX_CORES_PER_CHIP(cbits) * CMI_MAX_STRANDS_PER_CORE(sbits)) #define CMI_CHIPID_ARR_SZ (1 << CMI_MAX_CHIPID_NBITS) typedef struct cmi_hdl_ent { volatile uint32_t cmae_refcnt; cmi_hdl_impl_t *cmae_hdlp; } cmi_hdl_ent_t; static cmi_hdl_ent_t *cmi_chip_tab[CMI_CHIPID_ARR_SZ]; /* * Default values for the number of core and strand bits. */ uint_t cmi_core_nbits = CMI_MAX_CORES_PER_CHIP_NBITS; uint_t cmi_strand_nbits = CMI_MAX_STRANDS_PER_CORE_NBITS; static int cmi_ext_topo_check = 0; /* * Controls where we will source PCI config space data. */ #define CMI_PCICFG_FLAG_RD_HWOK 0x0001 #define CMI_PCICFG_FLAG_RD_INTERPOSEOK 0X0002 #define CMI_PCICFG_FLAG_WR_HWOK 0x0004 #define CMI_PCICFG_FLAG_WR_INTERPOSEOK 0X0008 static uint64_t cmi_pcicfg_flags = CMI_PCICFG_FLAG_RD_HWOK | CMI_PCICFG_FLAG_RD_INTERPOSEOK | CMI_PCICFG_FLAG_WR_HWOK | CMI_PCICFG_FLAG_WR_INTERPOSEOK; /* * The flags for individual cpus are kept in their per-cpu handle cmih_msrsrc */ #define CMI_MSR_FLAG_RD_HWOK 0x0001 #define CMI_MSR_FLAG_RD_INTERPOSEOK 0x0002 #define CMI_MSR_FLAG_WR_HWOK 0x0004 #define CMI_MSR_FLAG_WR_INTERPOSEOK 0x0008 int cmi_call_func_ntv_tries = 3; static cmi_errno_t call_func_ntv(int cpuid, xc_func_t func, xc_arg_t arg1, xc_arg_t arg2) { cmi_errno_t rc = -1; int i; kpreempt_disable(); if (CPU->cpu_id == cpuid) { (*func)(arg1, arg2, (xc_arg_t)&rc); } else { /* * This should not happen for a #MC trap or a poll, so * this is likely an error injection or similar. * We will try to cross call with xc_trycall - we * can't guarantee success with xc_call because * the interrupt code in the case of a #MC may * already hold the xc mutex. */ for (i = 0; i < cmi_call_func_ntv_tries; i++) { cpuset_t cpus; CPUSET_ONLY(cpus, cpuid); xc_priority(arg1, arg2, (xc_arg_t)&rc, CPUSET2BV(cpus), func); if (rc != -1) break; DELAY(1); } } kpreempt_enable(); return (rc != -1 ? rc : CMIERR_DEADLOCK); } static uint64_t injcnt; void cmi_hdl_inj_begin(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); if (hdl != NULL) hdl->cmih_flags |= CMIH_F_INJACTV; if (injcnt++ == 0) { cmn_err(CE_NOTE, "Hardware error injection/simulation " "activity noted"); } } void cmi_hdl_inj_end(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); ASSERT(hdl == NULL || hdl->cmih_flags & CMIH_F_INJACTV); if (hdl != NULL) hdl->cmih_flags &= ~CMIH_F_INJACTV; } boolean_t cmi_inj_tainted(void) { return (injcnt != 0 ? B_TRUE : B_FALSE); } /* * ======================================================= * | MSR Interposition | * | ----------------- | * | | * ------------------------------------------------------- */ #define CMI_MSRI_HASHSZ 16 #define CMI_MSRI_HASHIDX(hdl, msr) \ (((uintptr_t)(hdl) >> 3 + (msr)) % (CMI_MSRI_HASHSZ - 1)) struct cmi_msri_bkt { kmutex_t msrib_lock; struct cmi_msri_hashent *msrib_head; }; struct cmi_msri_hashent { struct cmi_msri_hashent *msrie_next; struct cmi_msri_hashent *msrie_prev; cmi_hdl_impl_t *msrie_hdl; uint_t msrie_msrnum; uint64_t msrie_msrval; }; #define CMI_MSRI_MATCH(ent, hdl, req_msr) \ ((ent)->msrie_hdl == (hdl) && (ent)->msrie_msrnum == (req_msr)) static struct cmi_msri_bkt msrihash[CMI_MSRI_HASHSZ]; static void msri_addent(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val) { int idx = CMI_MSRI_HASHIDX(hdl, msr); struct cmi_msri_bkt *hbp = &msrihash[idx]; struct cmi_msri_hashent *hep; mutex_enter(&hbp->msrib_lock); for (hep = hbp->msrib_head; hep != NULL; hep = hep->msrie_next) { if (CMI_MSRI_MATCH(hep, hdl, msr)) break; } if (hep != NULL) { hep->msrie_msrval = val; } else { hep = kmem_alloc(sizeof (*hep), KM_SLEEP); hep->msrie_hdl = hdl; hep->msrie_msrnum = msr; hep->msrie_msrval = val; if (hbp->msrib_head != NULL) hbp->msrib_head->msrie_prev = hep; hep->msrie_next = hbp->msrib_head; hep->msrie_prev = NULL; hbp->msrib_head = hep; } mutex_exit(&hbp->msrib_lock); } /* * Look for a match for the given hanlde and msr. Return 1 with valp * filled if a match is found, otherwise return 0 with valp untouched. */ static int msri_lookup(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t *valp) { int idx = CMI_MSRI_HASHIDX(hdl, msr); struct cmi_msri_bkt *hbp = &msrihash[idx]; struct cmi_msri_hashent *hep; /* * This function is called during #MC trap handling, so we should * consider the possibility that the hash mutex is held by the * interrupted thread. This should not happen because interposition * is an artificial injection mechanism and the #MC is requested * after adding entries, but just in case of a real #MC at an * unlucky moment we'll use mutex_tryenter here. */ if (!mutex_tryenter(&hbp->msrib_lock)) return (0); for (hep = hbp->msrib_head; hep != NULL; hep = hep->msrie_next) { if (CMI_MSRI_MATCH(hep, hdl, msr)) { *valp = hep->msrie_msrval; break; } } mutex_exit(&hbp->msrib_lock); return (hep != NULL); } /* * Remove any interposed value that matches. */ static void msri_rment(cmi_hdl_impl_t *hdl, uint_t msr) { int idx = CMI_MSRI_HASHIDX(hdl, msr); struct cmi_msri_bkt *hbp = &msrihash[idx]; struct cmi_msri_hashent *hep; if (!mutex_tryenter(&hbp->msrib_lock)) return; for (hep = hbp->msrib_head; hep != NULL; hep = hep->msrie_next) { if (CMI_MSRI_MATCH(hep, hdl, msr)) { if (hep->msrie_prev != NULL) hep->msrie_prev->msrie_next = hep->msrie_next; if (hep->msrie_next != NULL) hep->msrie_next->msrie_prev = hep->msrie_prev; if (hbp->msrib_head == hep) hbp->msrib_head = hep->msrie_next; kmem_free(hep, sizeof (*hep)); break; } } mutex_exit(&hbp->msrib_lock); } /* * ======================================================= * | PCI Config Space Interposition | * | ------------------------------ | * | | * ------------------------------------------------------- */ /* * Hash for interposed PCI config space values. We lookup on bus/dev/fun/offset * and then record whether the value stashed was made with a byte, word or * doubleword access; we will only return a hit for an access of the * same size. If you access say a 32-bit register using byte accesses * and then attempt to read the full 32-bit value back you will not obtain * any sort of merged result - you get a lookup miss. */ #define CMI_PCII_HASHSZ 16 #define CMI_PCII_HASHIDX(b, d, f, o) \ (((b) + (d) + (f) + (o)) % (CMI_PCII_HASHSZ - 1)) struct cmi_pcii_bkt { kmutex_t pciib_lock; struct cmi_pcii_hashent *pciib_head; }; struct cmi_pcii_hashent { struct cmi_pcii_hashent *pcii_next; struct cmi_pcii_hashent *pcii_prev; int pcii_bus; int pcii_dev; int pcii_func; int pcii_reg; int pcii_asize; uint32_t pcii_val; }; #define CMI_PCII_MATCH(ent, b, d, f, r, asz) \ ((ent)->pcii_bus == (b) && (ent)->pcii_dev == (d) && \ (ent)->pcii_func == (f) && (ent)->pcii_reg == (r) && \ (ent)->pcii_asize == (asz)) static struct cmi_pcii_bkt pciihash[CMI_PCII_HASHSZ]; /* * Add a new entry to the PCI interpose hash, overwriting any existing * entry that is found. */ static void pcii_addent(int bus, int dev, int func, int reg, uint32_t val, int asz) { int idx = CMI_PCII_HASHIDX(bus, dev, func, reg); struct cmi_pcii_bkt *hbp = &pciihash[idx]; struct cmi_pcii_hashent *hep; cmi_hdl_inj_begin(NULL); mutex_enter(&hbp->pciib_lock); for (hep = hbp->pciib_head; hep != NULL; hep = hep->pcii_next) { if (CMI_PCII_MATCH(hep, bus, dev, func, reg, asz)) break; } if (hep != NULL) { hep->pcii_val = val; } else { hep = kmem_alloc(sizeof (*hep), KM_SLEEP); hep->pcii_bus = bus; hep->pcii_dev = dev; hep->pcii_func = func; hep->pcii_reg = reg; hep->pcii_asize = asz; hep->pcii_val = val; if (hbp->pciib_head != NULL) hbp->pciib_head->pcii_prev = hep; hep->pcii_next = hbp->pciib_head; hep->pcii_prev = NULL; hbp->pciib_head = hep; } mutex_exit(&hbp->pciib_lock); cmi_hdl_inj_end(NULL); } /* * Look for a match for the given bus/dev/func/reg; return 1 with valp * filled if a match is found, otherwise return 0 with valp untouched. */ static int pcii_lookup(int bus, int dev, int func, int reg, int asz, uint32_t *valp) { int idx = CMI_PCII_HASHIDX(bus, dev, func, reg); struct cmi_pcii_bkt *hbp = &pciihash[idx]; struct cmi_pcii_hashent *hep; if (!mutex_tryenter(&hbp->pciib_lock)) return (0); for (hep = hbp->pciib_head; hep != NULL; hep = hep->pcii_next) { if (CMI_PCII_MATCH(hep, bus, dev, func, reg, asz)) { *valp = hep->pcii_val; break; } } mutex_exit(&hbp->pciib_lock); return (hep != NULL); } static void pcii_rment(int bus, int dev, int func, int reg, int asz) { int idx = CMI_PCII_HASHIDX(bus, dev, func, reg); struct cmi_pcii_bkt *hbp = &pciihash[idx]; struct cmi_pcii_hashent *hep; mutex_enter(&hbp->pciib_lock); for (hep = hbp->pciib_head; hep != NULL; hep = hep->pcii_next) { if (CMI_PCII_MATCH(hep, bus, dev, func, reg, asz)) { if (hep->pcii_prev != NULL) hep->pcii_prev->pcii_next = hep->pcii_next; if (hep->pcii_next != NULL) hep->pcii_next->pcii_prev = hep->pcii_prev; if (hbp->pciib_head == hep) hbp->pciib_head = hep->pcii_next; kmem_free(hep, sizeof (*hep)); break; } } mutex_exit(&hbp->pciib_lock); } #ifndef __xpv /* * ======================================================= * | Native methods | * | -------------- | * | | * | These are used when we are running native on bare- | * | metal, or simply don't know any better. | * --------------------------------------------------------- */ #define HDLPRIV(hdl) ((cpu_t *)(hdl)->cmih_hdlpriv) static uint_t ntv_vendor(cmi_hdl_impl_t *hdl) { return (cpuid_getvendor(HDLPRIV(hdl))); } static const char * ntv_vendorstr(cmi_hdl_impl_t *hdl) { return (cpuid_getvendorstr(HDLPRIV(hdl))); } static uint_t ntv_family(cmi_hdl_impl_t *hdl) { return (cpuid_getfamily(HDLPRIV(hdl))); } static uint_t ntv_model(cmi_hdl_impl_t *hdl) { return (cpuid_getmodel(HDLPRIV(hdl))); } static uint_t ntv_stepping(cmi_hdl_impl_t *hdl) { return (cpuid_getstep(HDLPRIV(hdl))); } static uint_t ntv_chipid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_chipid); } static uint_t ntv_procnodeid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_procnodeid); } static uint_t ntv_procnodes_per_pkg(cmi_hdl_impl_t *hdl) { return (hdl->cmih_procnodes_per_pkg); } static uint_t ntv_coreid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_coreid); } static uint_t ntv_strandid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_strandid); } static uint_t ntv_strand_apicid(cmi_hdl_impl_t *hdl) { return (cpuid_get_apicid(HDLPRIV(hdl))); } static uint16_t ntv_smbiosid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_smbiosid); } static uint_t ntv_smb_chipid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_smb_chipid); } static nvlist_t * ntv_smb_bboard(cmi_hdl_impl_t *hdl) { return (hdl->cmih_smb_bboard); } static uint32_t ntv_chiprev(cmi_hdl_impl_t *hdl) { return (cpuid_getchiprev(HDLPRIV(hdl))); } static const char * ntv_chiprevstr(cmi_hdl_impl_t *hdl) { return (cpuid_getchiprevstr(HDLPRIV(hdl))); } static uint32_t ntv_getsockettype(cmi_hdl_impl_t *hdl) { return (cpuid_getsockettype(HDLPRIV(hdl))); } static const char * ntv_getsocketstr(cmi_hdl_impl_t *hdl) { return (cpuid_getsocketstr(HDLPRIV(hdl))); } static id_t ntv_logical_id(cmi_hdl_impl_t *hdl) { return (HDLPRIV(hdl)->cpu_id); } /*ARGSUSED*/ static int ntv_getcr4_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) { ulong_t *dest = (ulong_t *)arg1; cmi_errno_t *rcp = (cmi_errno_t *)arg3; *dest = getcr4(); *rcp = CMI_SUCCESS; return (0); } static ulong_t ntv_getcr4(cmi_hdl_impl_t *hdl) { cpu_t *cp = HDLPRIV(hdl); ulong_t val; (void) call_func_ntv(cp->cpu_id, ntv_getcr4_xc, (xc_arg_t)&val, NULL); return (val); } /*ARGSUSED*/ static int ntv_setcr4_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) { ulong_t val = (ulong_t)arg1; cmi_errno_t *rcp = (cmi_errno_t *)arg3; setcr4(val); *rcp = CMI_SUCCESS; return (0); } static void ntv_setcr4(cmi_hdl_impl_t *hdl, ulong_t val) { cpu_t *cp = HDLPRIV(hdl); (void) call_func_ntv(cp->cpu_id, ntv_setcr4_xc, (xc_arg_t)val, NULL); } volatile uint32_t cmi_trapped_rdmsr; /*ARGSUSED*/ static int ntv_rdmsr_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) { uint_t msr = (uint_t)arg1; uint64_t *valp = (uint64_t *)arg2; cmi_errno_t *rcp = (cmi_errno_t *)arg3; on_trap_data_t otd; if (on_trap(&otd, OT_DATA_ACCESS) == 0) { if (checked_rdmsr(msr, valp) == 0) *rcp = CMI_SUCCESS; else *rcp = CMIERR_NOTSUP; } else { *rcp = CMIERR_MSRGPF; atomic_inc_32(&cmi_trapped_rdmsr); } no_trap(); return (0); } static cmi_errno_t ntv_rdmsr(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t *valp) { cpu_t *cp = HDLPRIV(hdl); if (!(hdl->cmih_msrsrc & CMI_MSR_FLAG_RD_HWOK)) return (CMIERR_INTERPOSE); return (call_func_ntv(cp->cpu_id, ntv_rdmsr_xc, (xc_arg_t)msr, (xc_arg_t)valp)); } volatile uint32_t cmi_trapped_wrmsr; /*ARGSUSED*/ static int ntv_wrmsr_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) { uint_t msr = (uint_t)arg1; uint64_t val = *((uint64_t *)arg2); cmi_errno_t *rcp = (cmi_errno_t *)arg3; on_trap_data_t otd; if (on_trap(&otd, OT_DATA_ACCESS) == 0) { if (checked_wrmsr(msr, val) == 0) *rcp = CMI_SUCCESS; else *rcp = CMIERR_NOTSUP; } else { *rcp = CMIERR_MSRGPF; atomic_inc_32(&cmi_trapped_wrmsr); } no_trap(); return (0); } static cmi_errno_t ntv_wrmsr(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val) { cpu_t *cp = HDLPRIV(hdl); if (!(hdl->cmih_msrsrc & CMI_MSR_FLAG_WR_HWOK)) return (CMI_SUCCESS); return (call_func_ntv(cp->cpu_id, ntv_wrmsr_xc, (xc_arg_t)msr, (xc_arg_t)&val)); } static cmi_errno_t ntv_msrinterpose(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val) { msri_addent(hdl, msr, val); return (CMI_SUCCESS); } /*ARGSUSED*/ static int ntv_int_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) { cmi_errno_t *rcp = (cmi_errno_t *)arg3; int int_no = (int)arg1; if (int_no == T_MCE) int18(); else int_cmci(); *rcp = CMI_SUCCESS; return (0); } static void ntv_int(cmi_hdl_impl_t *hdl, int int_no) { cpu_t *cp = HDLPRIV(hdl); (void) call_func_ntv(cp->cpu_id, ntv_int_xc, (xc_arg_t)int_no, NULL); } static int ntv_online(cmi_hdl_impl_t *hdl, int new_status, int *old_status) { int rc; processorid_t cpuid = HDLPRIV(hdl)->cpu_id; while (mutex_tryenter(&cpu_lock) == 0) { if (hdl->cmih_flags & CMIH_F_DEAD) return (EBUSY); delay(1); } rc = p_online_internal_locked(cpuid, new_status, old_status); mutex_exit(&cpu_lock); return (rc); } #else /* __xpv */ /* * ======================================================= * | xVM dom0 methods | * | ---------------- | * | | * | These are used when we are running as dom0 in | * | a Solaris xVM context. | * --------------------------------------------------------- */ #define HDLPRIV(hdl) ((xen_mc_lcpu_cookie_t)(hdl)->cmih_hdlpriv) extern uint_t _cpuid_vendorstr_to_vendorcode(char *); static uint_t xpv_vendor(cmi_hdl_impl_t *hdl) { return (_cpuid_vendorstr_to_vendorcode((char *)xen_physcpu_vendorstr( HDLPRIV(hdl)))); } static const char * xpv_vendorstr(cmi_hdl_impl_t *hdl) { return (xen_physcpu_vendorstr(HDLPRIV(hdl))); } static uint_t xpv_family(cmi_hdl_impl_t *hdl) { return (xen_physcpu_family(HDLPRIV(hdl))); } static uint_t xpv_model(cmi_hdl_impl_t *hdl) { return (xen_physcpu_model(HDLPRIV(hdl))); } static uint_t xpv_stepping(cmi_hdl_impl_t *hdl) { return (xen_physcpu_stepping(HDLPRIV(hdl))); } static uint_t xpv_chipid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_chipid); } static uint_t xpv_procnodeid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_procnodeid); } static uint_t xpv_procnodes_per_pkg(cmi_hdl_impl_t *hdl) { return (hdl->cmih_procnodes_per_pkg); } static uint_t xpv_coreid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_coreid); } static uint_t xpv_strandid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_strandid); } static uint_t xpv_strand_apicid(cmi_hdl_impl_t *hdl) { return (xen_physcpu_initial_apicid(HDLPRIV(hdl))); } static uint16_t xpv_smbiosid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_smbiosid); } static uint_t xpv_smb_chipid(cmi_hdl_impl_t *hdl) { return (hdl->cmih_smb_chipid); } static nvlist_t * xpv_smb_bboard(cmi_hdl_impl_t *hdl) { return (hdl->cmih_smb_bboard); } extern uint32_t _cpuid_chiprev(uint_t, uint_t, uint_t, uint_t); static uint32_t xpv_chiprev(cmi_hdl_impl_t *hdl) { return (_cpuid_chiprev(xpv_vendor(hdl), xpv_family(hdl), xpv_model(hdl), xpv_stepping(hdl))); } extern const char *_cpuid_chiprevstr(uint_t, uint_t, uint_t, uint_t); static const char * xpv_chiprevstr(cmi_hdl_impl_t *hdl) { return (_cpuid_chiprevstr(xpv_vendor(hdl), xpv_family(hdl), xpv_model(hdl), xpv_stepping(hdl))); } extern uint32_t _cpuid_skt(uint_t, uint_t, uint_t, uint_t); static uint32_t xpv_getsockettype(cmi_hdl_impl_t *hdl) { return (_cpuid_skt(xpv_vendor(hdl), xpv_family(hdl), xpv_model(hdl), xpv_stepping(hdl))); } extern const char *_cpuid_sktstr(uint_t, uint_t, uint_t, uint_t); static const char * xpv_getsocketstr(cmi_hdl_impl_t *hdl) { return (_cpuid_sktstr(xpv_vendor(hdl), xpv_family(hdl), xpv_model(hdl), xpv_stepping(hdl))); } static id_t xpv_logical_id(cmi_hdl_impl_t *hdl) { return (xen_physcpu_logical_id(HDLPRIV(hdl))); } static cmi_errno_t xpv_rdmsr(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t *valp) { switch (msr) { case IA32_MSR_MCG_CAP: *valp = xen_physcpu_mcg_cap(HDLPRIV(hdl)); break; default: return (CMIERR_NOTSUP); } return (CMI_SUCCESS); } /* * Request the hypervisor to write an MSR for us. The hypervisor * will only accept MCA-related MSRs, as this is for MCA error * simulation purposes alone. We will pre-screen MSRs for injection * so we don't bother the HV with bogus requests. We will permit * injection to any MCA bank register, and to MCG_STATUS. */ #define IS_MCA_INJ_MSR(msr) \ (((msr) >= IA32_MSR_MC(0, CTL) && (msr) <= IA32_MSR_MC(10, MISC)) || \ (msr) == IA32_MSR_MCG_STATUS) static cmi_errno_t xpv_wrmsr_cmn(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val, boolean_t intpose) { xen_mc_t xmc; struct xen_mc_msrinject *mci = &xmc.u.mc_msrinject; if (!(hdl->cmih_flags & CMIH_F_INJACTV)) return (CMIERR_NOTSUP); /* for injection use only! */ if (!IS_MCA_INJ_MSR(msr)) return (CMIERR_API); if (panicstr) return (CMIERR_DEADLOCK); mci->mcinj_cpunr = xen_physcpu_logical_id(HDLPRIV(hdl)); mci->mcinj_flags = intpose ? MC_MSRINJ_F_INTERPOSE : 0; mci->mcinj_count = 1; /* learn to batch sometime */ mci->mcinj_msr[0].reg = msr; mci->mcinj_msr[0].value = val; return (HYPERVISOR_mca(XEN_MC_msrinject, &xmc) == 0 ? CMI_SUCCESS : CMIERR_NOTSUP); } static cmi_errno_t xpv_wrmsr(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val) { return (xpv_wrmsr_cmn(hdl, msr, val, B_FALSE)); } static cmi_errno_t xpv_msrinterpose(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val) { return (xpv_wrmsr_cmn(hdl, msr, val, B_TRUE)); } static void xpv_int(cmi_hdl_impl_t *hdl, int int_no) { xen_mc_t xmc; struct xen_mc_mceinject *mce = &xmc.u.mc_mceinject; if (!(hdl->cmih_flags & CMIH_F_INJACTV)) return; if (int_no != T_MCE) { cmn_err(CE_WARN, "xpv_int: int_no %d unimplemented\n", int_no); } mce->mceinj_cpunr = xen_physcpu_logical_id(HDLPRIV(hdl)); (void) HYPERVISOR_mca(XEN_MC_mceinject, &xmc); } static int xpv_online(cmi_hdl_impl_t *hdl, int new_status, int *old_status) { xen_sysctl_t xs; int op, rc, status; new_status &= ~P_FORCED; switch (new_status) { case P_STATUS: op = XEN_SYSCTL_CPU_HOTPLUG_STATUS; break; case P_FAULTED: case P_OFFLINE: op = XEN_SYSCTL_CPU_HOTPLUG_OFFLINE; break; case P_ONLINE: op = XEN_SYSCTL_CPU_HOTPLUG_ONLINE; break; default: return (-1); } xs.cmd = XEN_SYSCTL_cpu_hotplug; xs.interface_version = XEN_SYSCTL_INTERFACE_VERSION; xs.u.cpu_hotplug.cpu = xen_physcpu_logical_id(HDLPRIV(hdl)); xs.u.cpu_hotplug.op = op; if ((rc = HYPERVISOR_sysctl(&xs)) >= 0) { status = rc; rc = 0; switch (status) { case XEN_CPU_HOTPLUG_STATUS_NEW: *old_status = P_OFFLINE; break; case XEN_CPU_HOTPLUG_STATUS_OFFLINE: *old_status = P_FAULTED; break; case XEN_CPU_HOTPLUG_STATUS_ONLINE: *old_status = P_ONLINE; break; default: return (-1); } } return (-rc); } #endif /*ARGSUSED*/ static void * cpu_search(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, uint_t strandid) { #ifdef __xpv xen_mc_lcpu_cookie_t cpi; for (cpi = xen_physcpu_next(NULL); cpi != NULL; cpi = xen_physcpu_next(cpi)) { if (xen_physcpu_chipid(cpi) == chipid && xen_physcpu_coreid(cpi) == coreid && xen_physcpu_strandid(cpi) == strandid) return ((void *)cpi); } return (NULL); #else /* __xpv */ cpu_t *cp, *startcp; kpreempt_disable(); cp = startcp = CPU; do { if (cmi_ntv_hwchipid(cp) == chipid && cmi_ntv_hwcoreid(cp) == coreid && cmi_ntv_hwstrandid(cp) == strandid) { kpreempt_enable(); return ((void *)cp); } cp = cp->cpu_next; } while (cp != startcp); kpreempt_enable(); return (NULL); #endif /* __ xpv */ } static boolean_t cpu_is_cmt(void *priv) { #ifdef __xpv return (xen_physcpu_is_cmt((xen_mc_lcpu_cookie_t)priv)); #else /* __xpv */ cpu_t *cp = (cpu_t *)priv; int strands_per_core = cpuid_get_ncpu_per_chip(cp) / cpuid_get_ncore_per_chip(cp); return (strands_per_core > 1); #endif /* __xpv */ } /* * Find the handle entry of a given cpu identified by a * tuple. */ static cmi_hdl_ent_t * cmi_hdl_ent_lookup(uint_t chipid, uint_t coreid, uint_t strandid) { int max_strands = CMI_MAX_STRANDS_PER_CHIP(cmi_core_nbits, cmi_strand_nbits); /* * Allocate per-chip table which contains a list of handle of * all strands of the chip. */ if (cmi_chip_tab[chipid] == NULL) { size_t sz; cmi_hdl_ent_t *pg; sz = max_strands * sizeof (cmi_hdl_ent_t); pg = kmem_zalloc(sz, KM_SLEEP); /* test and set the per-chip table if it is not allocated */ if (atomic_cas_ptr(&cmi_chip_tab[chipid], NULL, pg) != NULL) kmem_free(pg, sz); /* someone beats us */ } return (cmi_chip_tab[chipid] + ((((coreid) & CMI_MAX_COREID(cmi_core_nbits)) << cmi_strand_nbits) | ((strandid) & CMI_MAX_STRANDID(cmi_strand_nbits)))); } extern void cpuid_get_ext_topo(uint_t, uint_t *, uint_t *); cmi_hdl_t cmi_hdl_create(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, uint_t strandid) { cmi_hdl_impl_t *hdl; void *priv; cmi_hdl_ent_t *ent; uint_t vendor; #ifdef __xpv ASSERT(class == CMI_HDL_SOLARIS_xVM_MCA); #else ASSERT(class == CMI_HDL_NATIVE); #endif if ((priv = cpu_search(class, chipid, coreid, strandid)) == NULL) return (NULL); /* * Assume all chips in the system are the same type. * For Intel, attempt to check if extended topology is available * CPUID.EAX=0xB. If so, get the number of core and strand bits. */ #ifdef __xpv vendor = _cpuid_vendorstr_to_vendorcode( (char *)xen_physcpu_vendorstr((xen_mc_lcpu_cookie_t)priv)); #else vendor = cpuid_getvendor((cpu_t *)priv); #endif if (vendor == X86_VENDOR_Intel && cmi_ext_topo_check == 0) { cpuid_get_ext_topo(vendor, &cmi_core_nbits, &cmi_strand_nbits); cmi_ext_topo_check = 1; } if (chipid > CMI_MAX_CHIPID || coreid > CMI_MAX_COREID(cmi_core_nbits) || strandid > CMI_MAX_STRANDID(cmi_strand_nbits)) return (NULL); hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP); hdl->cmih_class = class; HDLOPS(hdl) = &cmi_hdl_ops; hdl->cmih_chipid = chipid; hdl->cmih_coreid = coreid; hdl->cmih_strandid = strandid; hdl->cmih_mstrand = cpu_is_cmt(priv); hdl->cmih_hdlpriv = priv; #ifdef __xpv hdl->cmih_msrsrc = CMI_MSR_FLAG_RD_INTERPOSEOK | CMI_MSR_FLAG_WR_INTERPOSEOK; /* * XXX: need hypervisor support for procnodeid, for now assume * single-node processors (procnodeid = chipid) */ hdl->cmih_procnodeid = xen_physcpu_chipid((xen_mc_lcpu_cookie_t)priv); hdl->cmih_procnodes_per_pkg = 1; #else /* __xpv */ hdl->cmih_msrsrc = CMI_MSR_FLAG_RD_HWOK | CMI_MSR_FLAG_RD_INTERPOSEOK | CMI_MSR_FLAG_WR_HWOK | CMI_MSR_FLAG_WR_INTERPOSEOK; hdl->cmih_procnodeid = cpuid_get_procnodeid((cpu_t *)priv); hdl->cmih_procnodes_per_pkg = cpuid_get_procnodes_per_pkg((cpu_t *)priv); #endif /* __xpv */ ent = cmi_hdl_ent_lookup(chipid, coreid, strandid); if (ent->cmae_refcnt != 0 || ent->cmae_hdlp != NULL) { /* * Somehow this (chipid, coreid, strandid) id tuple has * already been assigned! This indicates that the * callers logic in determining these values is busted, * or perhaps undermined by bad BIOS setup. Complain, * and refuse to initialize this tuple again as bad things * will happen. */ cmn_err(CE_NOTE, "cmi_hdl_create: chipid %d coreid %d " "strandid %d handle already allocated!", chipid, coreid, strandid); kmem_free(hdl, sizeof (*hdl)); return (NULL); } /* * Once we store a nonzero reference count others can find this * handle via cmi_hdl_lookup etc. This initial hold on the handle * is to be dropped only if some other part of cmi initialization * fails or, if it succeeds, at later cpu deconfigure. Note the * the module private data we hold in cmih_cmi and cmih_cmidata * is still NULL at this point (the caller will fill it with * cmi_hdl_setcmi if it initializes) so consumers of handles * should always be ready for that possibility. */ ent->cmae_hdlp = hdl; hdl->cmih_refcntp = &ent->cmae_refcnt; ent->cmae_refcnt = 1; return ((cmi_hdl_t)hdl); } void cmi_read_smbios(cmi_hdl_t ophdl) { uint_t strand_apicid = UINT_MAX; uint_t chip_inst = UINT_MAX; uint16_t smb_id = USHRT_MAX; int rc = 0; cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); /* set x86gentopo compatibility */ fm_smb_fmacompat(); #ifndef __xpv strand_apicid = ntv_strand_apicid(hdl); #else strand_apicid = xpv_strand_apicid(hdl); #endif if (!x86gentopo_legacy) { /* * If fm_smb_chipinst() or fm_smb_bboard() fails, * topo reverts to legacy mode */ rc = fm_smb_chipinst(strand_apicid, &chip_inst, &smb_id); if (rc == 0) { hdl->cmih_smb_chipid = chip_inst; hdl->cmih_smbiosid = smb_id; } else { #ifdef DEBUG cmn_err(CE_NOTE, "!cmi reads smbios chip info failed"); #endif /* DEBUG */ return; } hdl->cmih_smb_bboard = fm_smb_bboard(strand_apicid); #ifdef DEBUG if (hdl->cmih_smb_bboard == NULL) cmn_err(CE_NOTE, "!cmi reads smbios base boards info failed"); #endif /* DEBUG */ } } void cmi_hdl_hold(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); ASSERT(*hdl->cmih_refcntp != 0); /* must not be the initial hold */ atomic_inc_32(hdl->cmih_refcntp); } static int cmi_hdl_canref(cmi_hdl_ent_t *ent) { volatile uint32_t *refcntp; uint32_t refcnt; refcntp = &ent->cmae_refcnt; refcnt = *refcntp; if (refcnt == 0) { /* * Associated object never existed, is being destroyed, * or has been destroyed. */ return (0); } /* * We cannot use atomic increment here because once the reference * count reaches zero it must never be bumped up again. */ while (refcnt != 0) { if (atomic_cas_32(refcntp, refcnt, refcnt + 1) == refcnt) return (1); refcnt = *refcntp; } /* * Somebody dropped the reference count to 0 after our initial * check. */ return (0); } void cmi_hdl_rele(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); ASSERT(*hdl->cmih_refcntp > 0); atomic_dec_32(hdl->cmih_refcntp); } void cmi_hdl_destroy(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); cmi_hdl_ent_t *ent; /* Release the reference count held by cmi_hdl_create(). */ ASSERT(*hdl->cmih_refcntp > 0); atomic_dec_32(hdl->cmih_refcntp); hdl->cmih_flags |= CMIH_F_DEAD; ent = cmi_hdl_ent_lookup(hdl->cmih_chipid, hdl->cmih_coreid, hdl->cmih_strandid); /* * Use busy polling instead of condition variable here because * cmi_hdl_rele() may be called from #MC handler. */ while (cmi_hdl_canref(ent)) { cmi_hdl_rele(ophdl); delay(1); } ent->cmae_hdlp = NULL; kmem_free(hdl, sizeof (*hdl)); } void cmi_hdl_setspecific(cmi_hdl_t ophdl, void *arg) { IMPLHDL(ophdl)->cmih_spec = arg; } void * cmi_hdl_getspecific(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_spec); } void cmi_hdl_setmc(cmi_hdl_t ophdl, const struct cmi_mc_ops *mcops, void *mcdata) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); ASSERT(hdl->cmih_mcops == NULL && hdl->cmih_mcdata == NULL); hdl->cmih_mcops = mcops; hdl->cmih_mcdata = mcdata; } const struct cmi_mc_ops * cmi_hdl_getmcops(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_mcops); } void * cmi_hdl_getmcdata(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_mcdata); } cmi_hdl_t cmi_hdl_lookup(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, uint_t strandid) { cmi_hdl_ent_t *ent; if (chipid > CMI_MAX_CHIPID || coreid > CMI_MAX_COREID(cmi_core_nbits) || strandid > CMI_MAX_STRANDID(cmi_strand_nbits)) return (NULL); ent = cmi_hdl_ent_lookup(chipid, coreid, strandid); if (class == CMI_HDL_NEUTRAL) #ifdef __xpv class = CMI_HDL_SOLARIS_xVM_MCA; #else class = CMI_HDL_NATIVE; #endif if (!cmi_hdl_canref(ent)) return (NULL); if (ent->cmae_hdlp->cmih_class != class) { cmi_hdl_rele((cmi_hdl_t)ent->cmae_hdlp); return (NULL); } return ((cmi_hdl_t)ent->cmae_hdlp); } cmi_hdl_t cmi_hdl_any(void) { int i, j; cmi_hdl_ent_t *ent; int max_strands = CMI_MAX_STRANDS_PER_CHIP(cmi_core_nbits, cmi_strand_nbits); for (i = 0; i < CMI_CHIPID_ARR_SZ; i++) { if (cmi_chip_tab[i] == NULL) continue; for (j = 0, ent = cmi_chip_tab[i]; j < max_strands; j++, ent++) { if (cmi_hdl_canref(ent)) return ((cmi_hdl_t)ent->cmae_hdlp); } } return (NULL); } void cmi_hdl_walk(int (*cbfunc)(cmi_hdl_t, void *, void *, void *), void *arg1, void *arg2, void *arg3) { int i, j; cmi_hdl_ent_t *ent; int max_strands = CMI_MAX_STRANDS_PER_CHIP(cmi_core_nbits, cmi_strand_nbits); for (i = 0; i < CMI_CHIPID_ARR_SZ; i++) { if (cmi_chip_tab[i] == NULL) continue; for (j = 0, ent = cmi_chip_tab[i]; j < max_strands; j++, ent++) { if (cmi_hdl_canref(ent)) { cmi_hdl_impl_t *hdl = ent->cmae_hdlp; if ((*cbfunc)((cmi_hdl_t)hdl, arg1, arg2, arg3) == CMI_HDL_WALK_DONE) { cmi_hdl_rele((cmi_hdl_t)hdl); return; } cmi_hdl_rele((cmi_hdl_t)hdl); } } } } void cmi_hdl_setcmi(cmi_hdl_t ophdl, void *cmi, void *cmidata) { IMPLHDL(ophdl)->cmih_cmidata = cmidata; IMPLHDL(ophdl)->cmih_cmi = cmi; } void * cmi_hdl_getcmi(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_cmi); } void * cmi_hdl_getcmidata(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_cmidata); } enum cmi_hdl_class cmi_hdl_class(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_class); } #define CMI_HDL_OPFUNC(what, type) \ type \ cmi_hdl_##what(cmi_hdl_t ophdl) \ { \ return (HDLOPS(IMPLHDL(ophdl))-> \ cmio_##what(IMPLHDL(ophdl))); \ } CMI_HDL_OPFUNC(vendor, uint_t) CMI_HDL_OPFUNC(vendorstr, const char *) CMI_HDL_OPFUNC(family, uint_t) CMI_HDL_OPFUNC(model, uint_t) CMI_HDL_OPFUNC(stepping, uint_t) CMI_HDL_OPFUNC(chipid, uint_t) CMI_HDL_OPFUNC(procnodeid, uint_t) CMI_HDL_OPFUNC(coreid, uint_t) CMI_HDL_OPFUNC(strandid, uint_t) CMI_HDL_OPFUNC(procnodes_per_pkg, uint_t) CMI_HDL_OPFUNC(strand_apicid, uint_t) CMI_HDL_OPFUNC(chiprev, uint32_t) CMI_HDL_OPFUNC(chiprevstr, const char *) CMI_HDL_OPFUNC(getsockettype, uint32_t) CMI_HDL_OPFUNC(getsocketstr, const char *) CMI_HDL_OPFUNC(logical_id, id_t) CMI_HDL_OPFUNC(smbiosid, uint16_t) CMI_HDL_OPFUNC(smb_chipid, uint_t) CMI_HDL_OPFUNC(smb_bboard, nvlist_t *) boolean_t cmi_hdl_is_cmt(cmi_hdl_t ophdl) { return (IMPLHDL(ophdl)->cmih_mstrand); } void cmi_hdl_int(cmi_hdl_t ophdl, int num) { if (HDLOPS(IMPLHDL(ophdl))->cmio_int == NULL) return; cmi_hdl_inj_begin(ophdl); HDLOPS(IMPLHDL(ophdl))->cmio_int(IMPLHDL(ophdl), num); cmi_hdl_inj_end(NULL); } int cmi_hdl_online(cmi_hdl_t ophdl, int new_status, int *old_status) { return (HDLOPS(IMPLHDL(ophdl))->cmio_online(IMPLHDL(ophdl), new_status, old_status)); } #ifndef __xpv /* * Return hardware chip instance; cpuid_get_chipid provides this directly. */ uint_t cmi_ntv_hwchipid(cpu_t *cp) { return (cpuid_get_chipid(cp)); } /* * Return hardware node instance; cpuid_get_procnodeid provides this directly. */ uint_t cmi_ntv_hwprocnodeid(cpu_t *cp) { return (cpuid_get_procnodeid(cp)); } /* * Return core instance within a single chip. */ uint_t cmi_ntv_hwcoreid(cpu_t *cp) { return (cpuid_get_pkgcoreid(cp)); } /* * Return strand number within a single core. cpuid_get_clogid numbers * all execution units (strands, or cores in unstranded models) sequentially * within a single chip. */ uint_t cmi_ntv_hwstrandid(cpu_t *cp) { int strands_per_core = cpuid_get_ncpu_per_chip(cp) / cpuid_get_ncore_per_chip(cp); return (cpuid_get_clogid(cp) % strands_per_core); } static void cmi_ntv_hwdisable_mce_xc(void) { ulong_t cr4; cr4 = getcr4(); cr4 = cr4 & (~CR4_MCE); setcr4(cr4); } void cmi_ntv_hwdisable_mce(cmi_hdl_t hdl) { cpuset_t set; cmi_hdl_impl_t *thdl = IMPLHDL(hdl); cpu_t *cp = HDLPRIV(thdl); if (CPU->cpu_id == cp->cpu_id) { cmi_ntv_hwdisable_mce_xc(); } else { CPUSET_ONLY(set, cp->cpu_id); xc_call(NULL, NULL, NULL, CPUSET2BV(set), (xc_func_t)cmi_ntv_hwdisable_mce_xc); } } #endif /* __xpv */ void cmi_hdlconf_rdmsr_nohw(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); hdl->cmih_msrsrc &= ~CMI_MSR_FLAG_RD_HWOK; } void cmi_hdlconf_wrmsr_nohw(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); hdl->cmih_msrsrc &= ~CMI_MSR_FLAG_WR_HWOK; } cmi_errno_t cmi_hdl_rdmsr(cmi_hdl_t ophdl, uint_t msr, uint64_t *valp) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); /* * Regardless of the handle class, we first check for am * interposed value. In the xVM case you probably want to * place interposed values within the hypervisor itself, but * we still allow interposing them in dom0 for test and bringup * purposes. */ if ((hdl->cmih_msrsrc & CMI_MSR_FLAG_RD_INTERPOSEOK) && msri_lookup(hdl, msr, valp)) return (CMI_SUCCESS); if (HDLOPS(hdl)->cmio_rdmsr == NULL) return (CMIERR_NOTSUP); return (HDLOPS(hdl)->cmio_rdmsr(hdl, msr, valp)); } cmi_errno_t cmi_hdl_wrmsr(cmi_hdl_t ophdl, uint_t msr, uint64_t val) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); /* Invalidate any interposed value */ msri_rment(hdl, msr); if (HDLOPS(hdl)->cmio_wrmsr == NULL) return (CMI_SUCCESS); /* pretend all is ok */ return (HDLOPS(hdl)->cmio_wrmsr(hdl, msr, val)); } void cmi_hdl_enable_mce(cmi_hdl_t ophdl) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); ulong_t cr4; if (HDLOPS(hdl)->cmio_getcr4 == NULL || HDLOPS(hdl)->cmio_setcr4 == NULL) return; cr4 = HDLOPS(hdl)->cmio_getcr4(hdl); HDLOPS(hdl)->cmio_setcr4(hdl, cr4 | CR4_MCE); } void cmi_hdl_msrinterpose(cmi_hdl_t ophdl, cmi_mca_regs_t *regs, uint_t nregs) { cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); int i; if (HDLOPS(hdl)->cmio_msrinterpose == NULL) return; cmi_hdl_inj_begin(ophdl); for (i = 0; i < nregs; i++, regs++) HDLOPS(hdl)->cmio_msrinterpose(hdl, regs->cmr_msrnum, regs->cmr_msrval); cmi_hdl_inj_end(ophdl); } /*ARGSUSED*/ void cmi_hdl_msrforward(cmi_hdl_t ophdl, cmi_mca_regs_t *regs, uint_t nregs) { #ifdef __xpv cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); int i; for (i = 0; i < nregs; i++, regs++) msri_addent(hdl, regs->cmr_msrnum, regs->cmr_msrval); #endif } void cmi_pcird_nohw(void) { cmi_pcicfg_flags &= ~CMI_PCICFG_FLAG_RD_HWOK; } void cmi_pciwr_nohw(void) { cmi_pcicfg_flags &= ~CMI_PCICFG_FLAG_WR_HWOK; } static uint32_t cmi_pci_get_cmn(int bus, int dev, int func, int reg, int asz, int *interpose, ddi_acc_handle_t hdl) { uint32_t val; if (cmi_pcicfg_flags & CMI_PCICFG_FLAG_RD_INTERPOSEOK && pcii_lookup(bus, dev, func, reg, asz, &val)) { if (interpose) *interpose = 1; return (val); } if (interpose) *interpose = 0; if (!(cmi_pcicfg_flags & CMI_PCICFG_FLAG_RD_HWOK)) return (0); switch (asz) { case 1: if (hdl) val = pci_config_get8(hdl, (off_t)reg); else val = pci_cfgacc_get8(NULL, PCI_GETBDF(bus, dev, func), reg); break; case 2: if (hdl) val = pci_config_get16(hdl, (off_t)reg); else val = pci_cfgacc_get16(NULL, PCI_GETBDF(bus, dev, func), reg); break; case 4: if (hdl) val = pci_config_get32(hdl, (off_t)reg); else val = pci_cfgacc_get32(NULL, PCI_GETBDF(bus, dev, func), reg); break; default: val = 0; } return (val); } uint8_t cmi_pci_getb(int bus, int dev, int func, int reg, int *interpose, ddi_acc_handle_t hdl) { return ((uint8_t)cmi_pci_get_cmn(bus, dev, func, reg, 1, interpose, hdl)); } uint16_t cmi_pci_getw(int bus, int dev, int func, int reg, int *interpose, ddi_acc_handle_t hdl) { return ((uint16_t)cmi_pci_get_cmn(bus, dev, func, reg, 2, interpose, hdl)); } uint32_t cmi_pci_getl(int bus, int dev, int func, int reg, int *interpose, ddi_acc_handle_t hdl) { return (cmi_pci_get_cmn(bus, dev, func, reg, 4, interpose, hdl)); } void cmi_pci_interposeb(int bus, int dev, int func, int reg, uint8_t val) { pcii_addent(bus, dev, func, reg, val, 1); } void cmi_pci_interposew(int bus, int dev, int func, int reg, uint16_t val) { pcii_addent(bus, dev, func, reg, val, 2); } void cmi_pci_interposel(int bus, int dev, int func, int reg, uint32_t val) { pcii_addent(bus, dev, func, reg, val, 4); } static void cmi_pci_put_cmn(int bus, int dev, int func, int reg, int asz, ddi_acc_handle_t hdl, uint32_t val) { /* * If there is an interposed value for this register invalidate it. */ pcii_rment(bus, dev, func, reg, asz); if (!(cmi_pcicfg_flags & CMI_PCICFG_FLAG_WR_HWOK)) return; switch (asz) { case 1: if (hdl) pci_config_put8(hdl, (off_t)reg, (uint8_t)val); else pci_cfgacc_put8(NULL, PCI_GETBDF(bus, dev, func), reg, (uint8_t)val); break; case 2: if (hdl) pci_config_put16(hdl, (off_t)reg, (uint16_t)val); else pci_cfgacc_put16(NULL, PCI_GETBDF(bus, dev, func), reg, (uint16_t)val); break; case 4: if (hdl) pci_config_put32(hdl, (off_t)reg, val); else pci_cfgacc_put32(NULL, PCI_GETBDF(bus, dev, func), reg, val); break; default: break; } } void cmi_pci_putb(int bus, int dev, int func, int reg, ddi_acc_handle_t hdl, uint8_t val) { cmi_pci_put_cmn(bus, dev, func, reg, 1, hdl, val); } void cmi_pci_putw(int bus, int dev, int func, int reg, ddi_acc_handle_t hdl, uint16_t val) { cmi_pci_put_cmn(bus, dev, func, reg, 2, hdl, val); } void cmi_pci_putl(int bus, int dev, int func, int reg, ddi_acc_handle_t hdl, uint32_t val) { cmi_pci_put_cmn(bus, dev, func, reg, 4, hdl, val); } static const struct cmi_hdl_ops cmi_hdl_ops = { #ifdef __xpv /* * CMI_HDL_SOLARIS_xVM_MCA - ops when we are an xVM dom0 */ xpv_vendor, /* cmio_vendor */ xpv_vendorstr, /* cmio_vendorstr */ xpv_family, /* cmio_family */ xpv_model, /* cmio_model */ xpv_stepping, /* cmio_stepping */ xpv_chipid, /* cmio_chipid */ xpv_procnodeid, /* cmio_procnodeid */ xpv_coreid, /* cmio_coreid */ xpv_strandid, /* cmio_strandid */ xpv_procnodes_per_pkg, /* cmio_procnodes_per_pkg */ xpv_strand_apicid, /* cmio_strand_apicid */ xpv_chiprev, /* cmio_chiprev */ xpv_chiprevstr, /* cmio_chiprevstr */ xpv_getsockettype, /* cmio_getsockettype */ xpv_getsocketstr, /* cmio_getsocketstr */ xpv_logical_id, /* cmio_logical_id */ NULL, /* cmio_getcr4 */ NULL, /* cmio_setcr4 */ xpv_rdmsr, /* cmio_rdmsr */ xpv_wrmsr, /* cmio_wrmsr */ xpv_msrinterpose, /* cmio_msrinterpose */ xpv_int, /* cmio_int */ xpv_online, /* cmio_online */ xpv_smbiosid, /* cmio_smbiosid */ xpv_smb_chipid, /* cmio_smb_chipid */ xpv_smb_bboard /* cmio_smb_bboard */ #else /* __xpv */ /* * CMI_HDL_NATIVE - ops when apparently running on bare-metal */ ntv_vendor, /* cmio_vendor */ ntv_vendorstr, /* cmio_vendorstr */ ntv_family, /* cmio_family */ ntv_model, /* cmio_model */ ntv_stepping, /* cmio_stepping */ ntv_chipid, /* cmio_chipid */ ntv_procnodeid, /* cmio_procnodeid */ ntv_coreid, /* cmio_coreid */ ntv_strandid, /* cmio_strandid */ ntv_procnodes_per_pkg, /* cmio_procnodes_per_pkg */ ntv_strand_apicid, /* cmio_strand_apicid */ ntv_chiprev, /* cmio_chiprev */ ntv_chiprevstr, /* cmio_chiprevstr */ ntv_getsockettype, /* cmio_getsockettype */ ntv_getsocketstr, /* cmio_getsocketstr */ ntv_logical_id, /* cmio_logical_id */ ntv_getcr4, /* cmio_getcr4 */ ntv_setcr4, /* cmio_setcr4 */ ntv_rdmsr, /* cmio_rdmsr */ ntv_wrmsr, /* cmio_wrmsr */ ntv_msrinterpose, /* cmio_msrinterpose */ ntv_int, /* cmio_int */ ntv_online, /* cmio_online */ ntv_smbiosid, /* cmio_smbiosid */ ntv_smb_chipid, /* cmio_smb_chipid */ ntv_smb_bboard /* cmio_smb_bboard */ #endif };