1e4b86885SCheng Sean Ye /* 2e4b86885SCheng Sean Ye * CDDL HEADER START 3e4b86885SCheng Sean Ye * 4e4b86885SCheng Sean Ye * The contents of this file are subject to the terms of the 5e4b86885SCheng Sean Ye * Common Development and Distribution License (the "License"). 6e4b86885SCheng Sean Ye * You may not use this file except in compliance with the License. 7e4b86885SCheng Sean Ye * 8e4b86885SCheng Sean Ye * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9e4b86885SCheng Sean Ye * or http://www.opensolaris.org/os/licensing. 10e4b86885SCheng Sean Ye * See the License for the specific language governing permissions 11e4b86885SCheng Sean Ye * and limitations under the License. 12e4b86885SCheng Sean Ye * 13e4b86885SCheng Sean Ye * When distributing Covered Code, include this CDDL HEADER in each 14e4b86885SCheng Sean Ye * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15e4b86885SCheng Sean Ye * If applicable, add the following below this CDDL HEADER, with the 16e4b86885SCheng Sean Ye * fields enclosed by brackets "[]" replaced with your own identifying 17e4b86885SCheng Sean Ye * information: Portions Copyright [yyyy] [name of copyright owner] 18e4b86885SCheng Sean Ye * 19e4b86885SCheng Sean Ye * CDDL HEADER END 20e4b86885SCheng Sean Ye */ 21e4b86885SCheng Sean Ye 22e4b86885SCheng Sean Ye /* 23*349b53ddSStuart Maybee * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24e4b86885SCheng Sean Ye * Use is subject to license terms. 25e4b86885SCheng Sean Ye */ 26e4b86885SCheng Sean Ye 27e4b86885SCheng Sean Ye /* 28e4b86885SCheng Sean Ye * "Polled" MCA events in an i86xpv dom0. A timeout runs in the hypervisor 29e4b86885SCheng Sean Ye * and checks MCA state. If it observes valid MCA state in a bank and if 30e4b86885SCheng Sean Ye * it sees that dom0 has registered a handler for the VIRQ_MCA then it 31e4b86885SCheng Sean Ye * raises that VIRQ to dom0. The interrupt handler performs a 32e4b86885SCheng Sean Ye * hypercall to retrieve the polled telemetry and then pushes that telemetry 33e4b86885SCheng Sean Ye * into the MSR interpose hash and calls the generic logout code which 34e4b86885SCheng Sean Ye * will then find the provided interposed MSR values when it performs 35e4b86885SCheng Sean Ye * cmi_hdl_rdmsr so logout code works unchanged for native or i86xpv dom0. 36e4b86885SCheng Sean Ye */ 37e4b86885SCheng Sean Ye 38e4b86885SCheng Sean Ye #include <sys/types.h> 39e4b86885SCheng Sean Ye #include <sys/conf.h> 40e4b86885SCheng Sean Ye #include <sys/x86_archext.h> 41e4b86885SCheng Sean Ye #include <sys/mca_x86.h> 42e4b86885SCheng Sean Ye #include <sys/ddi.h> 43e4b86885SCheng Sean Ye #include <sys/spl.h> 44e4b86885SCheng Sean Ye #include <sys/sunddi.h> 45e4b86885SCheng Sean Ye #include <sys/evtchn_impl.h> 46e4b86885SCheng Sean Ye #include <sys/hypervisor.h> 47e4b86885SCheng Sean Ye 48e4b86885SCheng Sean Ye #include "../../i86pc/cpu/generic_cpu/gcpu.h" 49e4b86885SCheng Sean Ye 50e4b86885SCheng Sean Ye extern int *gcpu_xpv_telem_read(mc_info_t *, int, uint64_t *); 51e4b86885SCheng Sean Ye extern void gcpu_xpv_telem_ack(int, uint64_t); 52e4b86885SCheng Sean Ye extern void gcpu_xpv_mci_process(mc_info_t *, int, cmi_mca_regs_t *, size_t); 53e4b86885SCheng Sean Ye 54e4b86885SCheng Sean Ye int gcpu_xpv_mch_poll_interval_secs = 10; 55e4b86885SCheng Sean Ye int gcpu_xpv_virq_level = 3; 56e4b86885SCheng Sean Ye 57e4b86885SCheng Sean Ye static timeout_id_t gcpu_xpv_mch_poll_timeoutid; 58e4b86885SCheng Sean Ye 59e4b86885SCheng Sean Ye static int gcpu_xpv_virq_vect = -1; 60e4b86885SCheng Sean Ye 61e4b86885SCheng Sean Ye static mc_info_t gcpu_xpv_polldata; 62e4b86885SCheng Sean Ye static kmutex_t gcpu_xpv_polldata_lock; 63e4b86885SCheng Sean Ye 64e4b86885SCheng Sean Ye static cmi_mca_regs_t *gcpu_xpv_poll_bankregs; 65e4b86885SCheng Sean Ye static size_t gcpu_xpv_poll_bankregs_sz; 66e4b86885SCheng Sean Ye 67e4b86885SCheng Sean Ye static uint32_t gcpu_xpv_intr_unclaimed; 68e4b86885SCheng Sean Ye static uint32_t gcpu_xpv_mca_hcall_busy; 69e4b86885SCheng Sean Ye 70e4b86885SCheng Sean Ye static gcpu_poll_trace_ctl_t gcpu_xpv_poll_trace_ctl; 71e4b86885SCheng Sean Ye 72e4b86885SCheng Sean Ye #define GCPU_XPV_ARCH_NREGS 3 73e4b86885SCheng Sean Ye #define GCPU_XPV_MCH_POLL_REARM ((void *)1) 74e4b86885SCheng Sean Ye #define GCPU_XPV_MCH_POLL_NO_REARM NULL 75e4b86885SCheng Sean Ye 76e4b86885SCheng Sean Ye static uint_t 77e4b86885SCheng Sean Ye gcpu_xpv_virq_intr(void) 78e4b86885SCheng Sean Ye { 79*349b53ddSStuart Maybee int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT }; 80e4b86885SCheng Sean Ye uint64_t fetch_id; 81e4b86885SCheng Sean Ye int count = 0; 82e4b86885SCheng Sean Ye int i; 83e4b86885SCheng Sean Ye 84e4b86885SCheng Sean Ye if (gcpu_xpv_virq_vect == -1 || gcpu_xpv_poll_bankregs_sz == 0) { 85e4b86885SCheng Sean Ye gcpu_xpv_intr_unclaimed++; 86e4b86885SCheng Sean Ye return (DDI_INTR_UNCLAIMED); 87e4b86885SCheng Sean Ye } 88e4b86885SCheng Sean Ye 89e4b86885SCheng Sean Ye if (!mutex_tryenter(&gcpu_xpv_polldata_lock)) { 90e4b86885SCheng Sean Ye gcpu_xpv_mca_hcall_busy++; 91e4b86885SCheng Sean Ye return (DDI_INTR_CLAIMED); 92e4b86885SCheng Sean Ye } 93e4b86885SCheng Sean Ye 94e4b86885SCheng Sean Ye for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) { 95e4b86885SCheng Sean Ye while (gcpu_xpv_telem_read(&gcpu_xpv_polldata, types[i], 96e4b86885SCheng Sean Ye &fetch_id)) { 97e4b86885SCheng Sean Ye gcpu_poll_trace(&gcpu_xpv_poll_trace_ctl, 98e4b86885SCheng Sean Ye GCPU_MPT_WHAT_XPV_VIRQ, 99e4b86885SCheng Sean Ye x86_mcinfo_nentries(&gcpu_xpv_polldata)); 100e4b86885SCheng Sean Ye gcpu_xpv_mci_process(&gcpu_xpv_polldata, types[i], 101e4b86885SCheng Sean Ye gcpu_xpv_poll_bankregs, gcpu_xpv_poll_bankregs_sz); 102e4b86885SCheng Sean Ye gcpu_xpv_telem_ack(types[i], fetch_id); 103e4b86885SCheng Sean Ye count++; 104e4b86885SCheng Sean Ye } 105e4b86885SCheng Sean Ye } 106e4b86885SCheng Sean Ye 107e4b86885SCheng Sean Ye mutex_exit(&gcpu_xpv_polldata_lock); 108e4b86885SCheng Sean Ye 109e4b86885SCheng Sean Ye return (DDI_INTR_CLAIMED); 110e4b86885SCheng Sean Ye } 111e4b86885SCheng Sean Ye 112e4b86885SCheng Sean Ye static void 113e4b86885SCheng Sean Ye gcpu_xpv_mch_poll(void *arg) 114e4b86885SCheng Sean Ye { 115e4b86885SCheng Sean Ye cmi_hdl_t hdl = cmi_hdl_any(); 116e4b86885SCheng Sean Ye 117e4b86885SCheng Sean Ye if (hdl != NULL) { 118e4b86885SCheng Sean Ye cmi_mc_logout(hdl, 0, 0); 119e4b86885SCheng Sean Ye cmi_hdl_rele(hdl); 120e4b86885SCheng Sean Ye } 121e4b86885SCheng Sean Ye 122e4b86885SCheng Sean Ye if (arg == GCPU_XPV_MCH_POLL_REARM && 123e4b86885SCheng Sean Ye gcpu_xpv_mch_poll_interval_secs != 0) { 124e4b86885SCheng Sean Ye gcpu_xpv_mch_poll_timeoutid = timeout(gcpu_xpv_mch_poll, 125e4b86885SCheng Sean Ye GCPU_XPV_MCH_POLL_REARM, 126e4b86885SCheng Sean Ye drv_usectohz(gcpu_xpv_mch_poll_interval_secs * MICROSEC)); 127e4b86885SCheng Sean Ye } 128e4b86885SCheng Sean Ye } 129e4b86885SCheng Sean Ye 130e4b86885SCheng Sean Ye /* 131e4b86885SCheng Sean Ye * gcpu_mca_poll_init is called from gcpu_mca_init for each cpu handle 132e4b86885SCheng Sean Ye * that we initialize for. It should prepare for polling by allocating 133e4b86885SCheng Sean Ye * control structures and the like, but must not kick polling off yet. 134e4b86885SCheng Sean Ye * 135e4b86885SCheng Sean Ye * Since we initialize all cpus in a serialized loop there is no race 136e4b86885SCheng Sean Ye * on allocating the bankregs structure, nor in free'ing and enlarging 137e4b86885SCheng Sean Ye * it if we find the number of MCA banks is not uniform in the system 138e4b86885SCheng Sean Ye * (unlikely) since polling is only started post mp startup. 139e4b86885SCheng Sean Ye */ 140e4b86885SCheng Sean Ye 141e4b86885SCheng Sean Ye void 142e4b86885SCheng Sean Ye gcpu_mca_poll_init(cmi_hdl_t hdl) 143e4b86885SCheng Sean Ye { 144e4b86885SCheng Sean Ye gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); 145e4b86885SCheng Sean Ye int nbanks = gcpu->gcpu_mca.gcpu_mca_nbanks; 146e4b86885SCheng Sean Ye size_t sz = nbanks * GCPU_XPV_ARCH_NREGS * sizeof (cmi_mca_regs_t); 147e4b86885SCheng Sean Ye 148e4b86885SCheng Sean Ye ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA); 149e4b86885SCheng Sean Ye 150e4b86885SCheng Sean Ye if (gcpu_xpv_poll_bankregs == NULL || sz > gcpu_xpv_poll_bankregs_sz) { 151e4b86885SCheng Sean Ye if (gcpu_xpv_poll_bankregs != NULL) { 152e4b86885SCheng Sean Ye kmem_free(gcpu_xpv_poll_bankregs, 153e4b86885SCheng Sean Ye gcpu_xpv_poll_bankregs_sz); 154e4b86885SCheng Sean Ye } else { 155e4b86885SCheng Sean Ye gcpu_poll_trace_init(&gcpu_xpv_poll_trace_ctl); 156e4b86885SCheng Sean Ye } 157e4b86885SCheng Sean Ye 158e4b86885SCheng Sean Ye gcpu_xpv_poll_bankregs_sz = sz; 159e4b86885SCheng Sean Ye gcpu_xpv_poll_bankregs = kmem_zalloc(sz, KM_SLEEP); 160e4b86885SCheng Sean Ye 161e4b86885SCheng Sean Ye } 162e4b86885SCheng Sean Ye } 163e4b86885SCheng Sean Ye 164e4b86885SCheng Sean Ye void 165e4b86885SCheng Sean Ye gcpu_mca_poll_start(cmi_hdl_t hdl) 166e4b86885SCheng Sean Ye { 167e4b86885SCheng Sean Ye ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA); 168e4b86885SCheng Sean Ye /* 169e4b86885SCheng Sean Ye * We are on the boot cpu (cpu 0), called at the end of its 170e4b86885SCheng Sean Ye * multiprocessor startup. 171e4b86885SCheng Sean Ye */ 172e4b86885SCheng Sean Ye if (gcpu_xpv_poll_bankregs_sz != 0 && gcpu_xpv_virq_vect == -1) { 173e4b86885SCheng Sean Ye /* 174e4b86885SCheng Sean Ye * The hypervisor will poll MCA state for us, but it cannot 175e4b86885SCheng Sean Ye * poll MCH state so we do that via a timeout. 176e4b86885SCheng Sean Ye */ 177e4b86885SCheng Sean Ye if (gcpu_xpv_mch_poll_interval_secs != 0) { 178e4b86885SCheng Sean Ye gcpu_xpv_mch_poll_timeoutid = 179e4b86885SCheng Sean Ye timeout(gcpu_xpv_mch_poll, GCPU_XPV_MCH_POLL_REARM, 180e4b86885SCheng Sean Ye drv_usectohz(gcpu_xpv_mch_poll_interval_secs * 181e4b86885SCheng Sean Ye MICROSEC)); 182e4b86885SCheng Sean Ye } 183e4b86885SCheng Sean Ye 184e4b86885SCheng Sean Ye /* 185e4b86885SCheng Sean Ye * Register handler for VIRQ_MCA; once this is in place 186e4b86885SCheng Sean Ye * the hypervisor will begin to forward polled MCA observations 187e4b86885SCheng Sean Ye * to us. 188e4b86885SCheng Sean Ye */ 189e4b86885SCheng Sean Ye gcpu_xpv_virq_vect = ec_bind_virq_to_irq(VIRQ_MCA, 0); 190e4b86885SCheng Sean Ye (void) add_avintr(NULL, gcpu_xpv_virq_level, 191e4b86885SCheng Sean Ye (avfunc)gcpu_xpv_virq_intr, "MCA", gcpu_xpv_virq_vect, 192e4b86885SCheng Sean Ye NULL, NULL, NULL, NULL); 193e4b86885SCheng Sean Ye } 194e4b86885SCheng Sean Ye } 195