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