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 #ifndef __xpv 28 #error "This file is for i86xpv only" 29 #endif 30 31 #include <sys/types.h> 32 #include <sys/mca_x86.h> 33 #include <sys/archsystm.h> 34 #include <sys/hypervisor.h> 35 36 #include "../../i86pc/cpu/generic_cpu/gcpu.h" 37 38 extern xpv_mca_panic_data_t *xpv_mca_panic_data; 39 40 mc_info_t gcpu_mce_data; 41 42 enum mctelem_direction { 43 MCTELEM_FORWARD, 44 MCTELEM_REVERSE 45 }; 46 47 static uint32_t gcpu_xpv_hdl_lookupfails; 48 static uint32_t gcpu_xpv_bankhdr_found; 49 static uint32_t gcpu_xpv_spechdr_found; 50 51 static uint32_t gcpu_xpv_mca_hcall_fails[16]; 52 static uint32_t gcpu_xpv_globalhdr_found; 53 54 static cmi_mca_regs_t *gcpu_xpv_bankregs; 55 size_t gcpu_xpv_bankregs_sz; 56 57 #define GCPU_XPV_ARCH_NREGS 3 58 59 void 60 gcpu_xpv_mca_init(int nbanks) 61 { 62 if (gcpu_xpv_bankregs == NULL) { 63 gcpu_xpv_bankregs_sz = nbanks * GCPU_XPV_ARCH_NREGS * 64 sizeof (cmi_mca_regs_t); 65 66 gcpu_xpv_bankregs = kmem_zalloc(gcpu_xpv_bankregs_sz, KM_SLEEP); 67 } 68 } 69 70 static void 71 gcpu_xpv_proxy_logout(int what, struct mc_info *mi, struct mcinfo_common **micp, 72 int *idxp, cmi_mca_regs_t *bankregs, size_t bankregs_sz) 73 { 74 struct mcinfo_global *mgi = (struct mcinfo_global *)(uintptr_t)*micp; 75 struct mcinfo_common *mic; 76 struct mcinfo_bank *mib; 77 cmi_hdl_t hdl = NULL; 78 cmi_mca_regs_t *mcrp; 79 gcpu_data_t *gcpu; 80 int idx = *idxp; 81 int tried = 0; 82 int nbanks, j; 83 84 /* Skip over the MC_TYPE_GLOBAL record */ 85 ASSERT(mgi->common.type == MC_TYPE_GLOBAL); 86 mic = x86_mcinfo_next((struct mcinfo_common *)(uintptr_t)mgi); 87 idx++; 88 89 /* 90 * Process all MC_TYPE_BANK and MC_TYPE_EXTENDED records that 91 * follow the MC_TYPE_GLOBAL record, ending when we reach any 92 * other record type or when we're out of record. 93 * 94 * We skip over MC_TYPE_EXTENDED for now - nothing consumes 95 * the extended MSR data even in native Solaris. 96 */ 97 while (idx < x86_mcinfo_nentries(mi) && 98 (mic->type == MC_TYPE_BANK || mic->type == MC_TYPE_EXTENDED)) { 99 if (mic->type == MC_TYPE_EXTENDED) { 100 gcpu_xpv_spechdr_found++; 101 goto next_record; 102 } else { 103 gcpu_xpv_bankhdr_found++; 104 } 105 106 if (hdl == NULL && !tried++) { 107 if ((hdl = cmi_hdl_lookup(CMI_HDL_SOLARIS_xVM_MCA, 108 mgi->mc_socketid, mgi->mc_coreid, 109 mgi->mc_core_threadid)) == NULL) { 110 gcpu_xpv_hdl_lookupfails++; 111 goto next_record; 112 } else { 113 gcpu = cmi_hdl_getcmidata(hdl); 114 nbanks = gcpu->gcpu_mca.gcpu_mca_nbanks; 115 bzero(bankregs, bankregs_sz); 116 mcrp = bankregs; 117 } 118 } 119 120 mib = (struct mcinfo_bank *)(uintptr_t)mic; 121 122 mcrp->cmr_msrnum = IA32_MSR_MC(mib->mc_bank, STATUS); 123 mcrp->cmr_msrval = mib->mc_status; 124 mcrp++; 125 126 mcrp->cmr_msrnum = IA32_MSR_MC(mib->mc_bank, ADDR); 127 mcrp->cmr_msrval = mib->mc_addr; 128 mcrp++; 129 130 mcrp->cmr_msrnum = IA32_MSR_MC(mib->mc_bank, MISC); 131 mcrp->cmr_msrval = mib->mc_misc; 132 mcrp++; 133 134 next_record: 135 idx++; 136 mic = x86_mcinfo_next(mic); 137 } 138 139 /* 140 * If we found some telemetry and a handle to associate it with 141 * then "forward" that telemetry into the MSR interpose layer 142 * and then request logout which will find that interposed 143 * telemetry. Indicate that logout code should clear bank 144 * status registers so that it can invalidate them in the interpose 145 * layer - they won't actually make it as far as real MSR writes. 146 */ 147 if (hdl != NULL) { 148 cmi_mca_regs_t gsr; 149 gcpu_mce_status_t mce; 150 151 gsr.cmr_msrnum = IA32_MSR_MCG_STATUS; 152 gsr.cmr_msrval = mgi->mc_gstatus; 153 cmi_hdl_msrforward(hdl, &gsr, 1); 154 155 cmi_hdl_msrforward(hdl, bankregs, mcrp - bankregs); 156 gcpu_mca_logout(hdl, NULL, (uint64_t)-1, &mce, B_TRUE, what); 157 cmi_hdl_rele(hdl); 158 } 159 160 /* 161 * We must move the index on at least one record or our caller 162 * may loop forever; our initial increment over the global 163 * record assures this. 164 */ 165 ASSERT(idx > *idxp); 166 *idxp = idx; 167 *micp = mic; 168 } 169 170 /* 171 * Process a struct mc_info. 172 * 173 * There are x86_mcinfo_nentries(mi) entries. An entry of type 174 * MC_TYPE_GLOBAL precedes a number (potentially zero) of 175 * entries of type MC_TYPE_BANK for telemetry from MCA banks 176 * of the resource identified in the MC_TYPE_GLOBAL entry. 177 * I think there can be multiple MC_TYPE_GLOBAL entries per buffer. 178 */ 179 void 180 gcpu_xpv_mci_process(mc_info_t *mi, int type, 181 cmi_mca_regs_t *bankregs, size_t bankregs_sz) 182 { 183 struct mcinfo_common *mic; 184 int idx; 185 186 mic = x86_mcinfo_first(mi); 187 188 idx = 0; 189 while (idx < x86_mcinfo_nentries(mi)) { 190 if (mic->type == MC_TYPE_GLOBAL) { 191 gcpu_xpv_globalhdr_found++; 192 gcpu_xpv_proxy_logout(type == XEN_MC_URGENT ? 193 GCPU_MPT_WHAT_MC_ERR : GCPU_MPT_WHAT_XPV_VIRQ, 194 mi, &mic, &idx, bankregs, bankregs_sz); 195 } else { 196 idx++; 197 mic = x86_mcinfo_next(mic); 198 } 199 } 200 } 201 202 int 203 gcpu_xpv_telem_read(mc_info_t *mci, int type, uint64_t *idp) 204 { 205 xen_mc_t xmc; 206 xen_mc_fetch_t *mcf = &xmc.u.mc_fetch; 207 long err; 208 209 mcf->flags = type; 210 set_xen_guest_handle(mcf->data, mci); 211 212 if ((err = HYPERVISOR_mca(XEN_MC_fetch, &xmc)) != 0) { 213 gcpu_xpv_mca_hcall_fails[err < 16 ? err : 0]++; 214 return (0); 215 } 216 217 if (mcf->flags == XEN_MC_OK) { 218 *idp = mcf->fetch_id; 219 return (1); 220 } else { 221 *idp = 0; 222 return (0); 223 } 224 } 225 226 void 227 gcpu_xpv_telem_ack(int type, uint64_t fetch_id) 228 { 229 xen_mc_t xmc; 230 struct xen_mc_fetch *mcf = &xmc.u.mc_fetch; 231 232 mcf->flags = type | XEN_MC_ACK; 233 mcf->fetch_id = fetch_id; 234 (void) HYPERVISOR_mca(XEN_MC_fetch, &xmc); 235 } 236 237 static void 238 mctelem_traverse(void *head, enum mctelem_direction direction, 239 boolean_t urgent) 240 { 241 char *tep = head, **ntepp; 242 int noff = (direction == MCTELEM_FORWARD) ? 243 xpv_mca_panic_data->mpd_fwdptr_offset : 244 xpv_mca_panic_data->mpd_revptr_offset; 245 246 247 while (tep != NULL) { 248 struct mc_info **mcip = (struct mc_info **) 249 (tep + xpv_mca_panic_data->mpd_dataptr_offset); 250 251 gcpu_xpv_mci_process(*mcip, 252 urgent ? XEN_MC_URGENT : XEN_MC_NONURGENT, 253 gcpu_xpv_bankregs, gcpu_xpv_bankregs_sz); 254 255 ntepp = (char **)(tep + noff); 256 tep = *ntepp; 257 } 258 } 259 260 /* 261 * Callback made from panicsys. We may have reached panicsys from a 262 * Solaris-initiated panic or a hypervisor-initiated panic; for the 263 * latter we may not perform any hypercalls. Our task is to retrieve 264 * unprocessed MCA telemetry from the hypervisor and shovel it into 265 * errorqs for later processing during panic. 266 */ 267 void 268 gcpu_xpv_panic_callback(void) 269 { 270 if (IN_XPV_PANIC()) { 271 xpv_mca_panic_data_t *ti = xpv_mca_panic_data; 272 273 if (ti == NULL || 274 ti->mpd_magic != MCA_PANICDATA_MAGIC || 275 ti->mpd_version != MCA_PANICDATA_VERS) 276 return; 277 278 mctelem_traverse(ti->mpd_urgent_processing, MCTELEM_FORWARD, 279 B_TRUE); 280 mctelem_traverse(ti->mpd_urgent_dangling, MCTELEM_REVERSE, 281 B_TRUE); 282 mctelem_traverse(ti->mpd_urgent_committed, MCTELEM_REVERSE, 283 B_TRUE); 284 285 mctelem_traverse(ti->mpd_nonurgent_processing, MCTELEM_FORWARD, 286 B_FALSE); 287 mctelem_traverse(ti->mpd_nonurgent_dangling, MCTELEM_REVERSE, 288 B_FALSE); 289 mctelem_traverse(ti->mpd_nonurgent_committed, MCTELEM_REVERSE, 290 B_FALSE); 291 } else { 292 int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT }; 293 uint64_t fetch_id; 294 int i; 295 296 for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) { 297 while (gcpu_xpv_telem_read(&gcpu_mce_data, 298 types[i], &fetch_id)) { 299 gcpu_xpv_mci_process(&gcpu_mce_data, types[i], 300 gcpu_xpv_bankregs, gcpu_xpv_bankregs_sz); 301 gcpu_xpv_telem_ack(types[i], fetch_id); 302 } 303 } 304 } 305 } 306