cmi_hw.c revision 7991dd244dd6e9bd35355640fc39c8fe3300c4fb
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* CPU Module Interface - hardware abstraction.
*/
#ifdef __xpv
#include <sys/xpv_user.h>
#endif
#include <sys/cpu_module.h>
#include <sys/x86_archext.h>
#include <sys/pci_cfgspace.h>
#include <sys/archsystm.h>
#include <sys/controlregs.h>
#include <sys/processor.h>
#include <sys/cpu_module_impl.h>
/*
* 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 {
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 */
void *cmih_mcdata; /* Memory-controller data */
#define CMIH_F_INJACTV 0x1ULL
/*
* Ops structure for handle operations.
*/
struct cmi_hdl_ops {
/*
* These ops are required in an implementation.
*/
const char *(*cmio_vendorstr)(cmi_hdl_impl_t *);
const char *(*cmio_chiprevstr)(cmi_hdl_impl_t *);
const char *(*cmio_getsocketstr)(cmi_hdl_impl_t *);
/*
* These ops are optional in an implementation.
*/
void (*cmio_int)(cmi_hdl_impl_t *, int);
int (*cmio_online)(cmi_hdl_impl_t *, int, int *);
};
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
* 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_STRANDS_PER_CHIP (CMI_MAX_CORES_PER_CHIP * \
/*
* Handle array indexing within a per-chip table
* [6:3] = Core in package,
* [2:0] = Strand in core,
*/
#define CMI_HDL_ARR_IDX_CORE(coreid) \
#define CMI_HDL_ARR_IDX_STRAND(strandid) \
typedef struct cmi_hdl_ent {
volatile uint32_t cmae_refcnt;
/*
* 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 =
/*
* 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
{
int i;
} 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++) {
if (rc != -1)
break;
DELAY(1);
}
}
}
void
{
if (injcnt++ == 0) {
"activity noted");
}
}
void
{
}
cmi_inj_tainted(void)
{
}
/*
* =======================================================
* | MSR Interposition |
* | ----------------- |
* | |
* -------------------------------------------------------
*/
#define CMI_MSRI_HASHSZ 16
struct cmi_msri_bkt {
struct cmi_msri_hashent *msrib_head;
};
struct cmi_msri_hashent {
struct cmi_msri_hashent *msrie_next;
struct cmi_msri_hashent *msrie_prev;
};
static void
{
struct cmi_msri_hashent *hep;
break;
}
} else {
}
}
/*
* 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
{
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.
*/
return (0);
break;
}
}
}
/*
* Remove any interposed value that matches.
*/
static void
{
struct cmi_msri_hashent *hep;
return;
break;
}
}
}
/*
* =======================================================
* | PCI Config Space Interposition |
* | ------------------------------ |
* | |
* -------------------------------------------------------
*/
/*
* 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 {
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;
};
/*
* Add a new entry to the PCI interpose hash, overwriting any existing
* entry that is found.
*/
static void
{
struct cmi_pcii_hashent *hep;
break;
}
} else {
}
}
/*
* filled if a match is found, otherwise return 0 with valp untouched.
*/
static int
{
struct cmi_pcii_hashent *hep;
return (0);
break;
}
}
}
static void
{
struct cmi_pcii_hashent *hep;
break;
}
}
}
#ifndef __xpv
/*
* =======================================================
* | Native methods |
* | -------------- |
* | |
* | These are used when we are running native on bare- |
* | metal, or simply don't know any better. |
* ---------------------------------------------------------
*/
static uint_t
{
}
static const char *
{
}
static uint_t
{
}
static uint_t
{
}
static uint_t
{
}
static uint_t
{
return (hdl->cmih_chipid);
}
static uint_t
{
return (hdl->cmih_procnodeid);
}
static uint_t
{
return (hdl->cmih_procnodes_per_pkg);
}
static uint_t
{
return (hdl->cmih_coreid);
}
static uint_t
{
return (hdl->cmih_strandid);
}
static uint_t
{
}
static uint16_t
{
return (hdl->cmih_smbiosid);
}
static uint_t
{
return (hdl->cmih_smb_chipid);
}
static nvlist_t *
{
return (hdl->cmih_smb_bboard);
}
static uint32_t
{
}
static const char *
{
}
static uint32_t
{
}
static const char *
{
}
static id_t
{
}
/*ARGSUSED*/
static int
{
*rcp = CMI_SUCCESS;
return (0);
}
static ulong_t
{
return (val);
}
/*ARGSUSED*/
static int
{
*rcp = CMI_SUCCESS;
return (0);
}
static void
{
}
volatile uint32_t cmi_trapped_rdmsr;
/*ARGSUSED*/
static int
{
*rcp = CMI_SUCCESS;
else
*rcp = CMIERR_NOTSUP;
} else {
*rcp = CMIERR_MSRGPF;
}
no_trap();
return (0);
}
static cmi_errno_t
{
return (CMIERR_INTERPOSE);
}
volatile uint32_t cmi_trapped_wrmsr;
/*ARGSUSED*/
static int
{
*rcp = CMI_SUCCESS;
else
*rcp = CMIERR_NOTSUP;
} else {
*rcp = CMIERR_MSRGPF;
}
no_trap();
return (0);
}
static cmi_errno_t
{
return (CMI_SUCCESS);
}
static cmi_errno_t
{
return (CMI_SUCCESS);
}
/*ARGSUSED*/
static int
{
int18();
else
int_cmci();
*rcp = CMI_SUCCESS;
return (0);
}
static void
{
}
static int
{
}
#else /* __xpv */
/*
* =======================================================
* | xVM dom0 methods |
* | ---------------- |
* | |
* | These are used when we are running as dom0 in |
* | a Solaris xVM context. |
* ---------------------------------------------------------
*/
extern uint_t _cpuid_vendorstr_to_vendorcode(char *);
static uint_t
{
return (_cpuid_vendorstr_to_vendorcode((char *)xen_physcpu_vendorstr(
}
static const char *
{
}
static uint_t
{
}
static uint_t
{
}
static uint_t
{
}
static uint_t
{
return (hdl->cmih_chipid);
}
static uint_t
{
return (hdl->cmih_procnodeid);
}
static uint_t
{
return (hdl->cmih_procnodes_per_pkg);
}
static uint_t
{
return (hdl->cmih_coreid);
}
static uint_t
{
return (hdl->cmih_strandid);
}
static uint_t
{
}
static uint16_t
{
return (hdl->cmih_smbiosid);
}
static uint_t
{
return (hdl->cmih_smb_chipid);
}
static nvlist_t *
{
return (hdl->cmih_smb_bboard);
}
static uint32_t
{
}
static const char *
{
}
static uint32_t
{
}
static const char *
{
}
static id_t
{
}
static cmi_errno_t
{
switch (msr) {
case IA32_MSR_MCG_CAP:
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_MCG_STATUS)
static cmi_errno_t
{
struct xen_mc_msrinject mci;
return (CMIERR_NOTSUP); /* for injection use only! */
if (!IS_MCA_INJ_MSR(msr))
return (CMIERR_API);
if (panicstr)
return (CMIERR_DEADLOCK);
0 ? CMI_SUCCESS : CMIERR_NOTSUP);
}
static cmi_errno_t
{
}
static cmi_errno_t
{
}
static void
{
struct xen_mc_mceinject mce;
return;
int_no);
}
}
static int
{
new_status &= ~P_FORCED;
switch (new_status) {
case P_STATUS:
break;
case P_FAULTED:
case P_OFFLINE:
break;
case P_ONLINE:
break;
default:
return (-1);
}
rc = 0;
switch (status) {
*old_status = P_OFFLINE;
break;
*old_status = P_FAULTED;
break;
*old_status = P_ONLINE;
break;
default:
return (-1);
}
}
return (-rc);
}
#endif
/*ARGSUSED*/
static void *
{
#ifdef __xpv
return ((void *)cpi);
}
return (NULL);
#else /* __xpv */
do {
return ((void *)cp);
}
return (NULL);
#endif /* __ xpv */
}
static boolean_t
cpu_is_cmt(void *priv)
{
#ifdef __xpv
#else /* __xpv */
return (strands_per_core > 1);
#endif /* __xpv */
}
/*
* Find the handle entry of a given cpu identified by a <chip,core,strand>
* tuple.
*/
static cmi_hdl_ent_t *
{
/*
* Allocate per-chip table which contains a list of handle of
* all strands of the chip.
*/
/* test and set the per-chip table if it is not allocated */
}
}
{
void *priv;
#ifdef __xpv
#else
#endif
if (chipid > CMI_MAX_CHIPID ||
return (NULL);
return (NULL);
#ifdef __xpv
/*
* XXX: need hypervisor support for procnodeid, for now assume
* single-node processors (procnodeid = chipid)
*/
#else /* __xpv */
#endif /* __xpv */
/*
* 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.
*/
"strandid %d handle already allocated!",
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.
*/
}
void
{
int rc = 0;
/* set x86gentopo compatibility */
#ifndef __xpv
#else
#endif
if (!x86gentopo_legacy) {
/*
* If fm_smb_chipinst() or fm_smb_bboard() fails,
* topo reverts to legacy mode
*/
if (rc == 0) {
} else {
#ifdef DEBUG
#endif /* DEBUG */
return;
}
#ifdef DEBUG
"!cmi reads smbios base boards info failed");
#endif /* DEBUG */
}
}
void
{
}
static int
{
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) {
return (1);
}
/*
* Somebody dropped the reference count to 0 after our initial
* check.
*/
return (0);
}
void
{
return;
hdl->cmih_strandid);
}
void
{
}
void *
{
}
void
{
}
const struct cmi_mc_ops *
{
}
void *
{
}
{
if (chipid > CMI_MAX_CHIPID ||
return (NULL);
if (class == CMI_HDL_NEUTRAL)
#ifdef __xpv
#else
#endif
if (!cmi_hdl_canref(ent))
return (NULL);
return (NULL);
}
}
cmi_hdl_any(void)
{
int i, j;
for (i = 0; i < CMI_CHIPID_ARR_SZ; i++) {
if (cmi_chip_tab[i] == NULL)
continue;
j++, ent++) {
if (cmi_hdl_canref(ent))
}
}
return (NULL);
}
void
{
int i, j;
for (i = 0; i < CMI_CHIPID_ARR_SZ; i++) {
if (cmi_chip_tab[i] == NULL)
continue;
j++, ent++) {
if (cmi_hdl_canref(ent)) {
== CMI_HDL_WALK_DONE) {
return;
}
}
}
}
}
void
{
}
void *
{
}
void *
{
}
enum cmi_hdl_class
{
}
type \
{ \
}
CMI_HDL_OPFUNC(vendorstr, const char *)
CMI_HDL_OPFUNC(chiprevstr, const char *)
CMI_HDL_OPFUNC(getsocketstr, const char *)
{
}
void
{
return;
}
int
{
new_status, old_status));
}
#ifndef __xpv
/*
* Return hardware chip instance; cpuid_get_chipid provides this directly.
*/
{
return (cpuid_get_chipid(cp));
}
/*
* Return hardware node instance; cpuid_get_procnodeid provides this directly.
*/
{
return (cpuid_get_procnodeid(cp));
}
/*
* Return core instance within a single chip.
*/
{
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.
*/
{
}
#endif /* __xpv */
void
{
}
void
{
}
{
/*
* 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.
*/
return (CMI_SUCCESS);
return (CMIERR_NOTSUP);
}
{
/* Invalidate any interposed value */
return (CMI_SUCCESS); /* pretend all is ok */
}
void
{
return;
}
void
{
int i;
return;
regs->cmr_msrval);
}
/*ARGSUSED*/
void
{
#ifdef __xpv
int i;
#endif
}
void
cmi_pcird_nohw(void)
{
}
void
cmi_pciwr_nohw(void)
{
}
static uint32_t
{
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)
else
break;
case 2:
if (hdl)
else
break;
case 4:
if (hdl)
else
break;
default:
val = 0;
}
return (val);
}
{
hdl));
}
{
hdl));
}
{
}
void
{
}
void
{
}
void
{
}
static void
{
/*
* If there is an interposed value for this register invalidate it.
*/
if (!(cmi_pcicfg_flags & CMI_PCICFG_FLAG_WR_HWOK))
return;
switch (asz) {
case 1:
if (hdl)
else
break;
case 2:
if (hdl)
else
break;
case 4:
if (hdl)
else
break;
default:
break;
}
}
void
{
}
void
{
}
void
{
}
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
};