17532SSean.Ye@Sun.COM /*
27532SSean.Ye@Sun.COM * CDDL HEADER START
37532SSean.Ye@Sun.COM *
47532SSean.Ye@Sun.COM * The contents of this file are subject to the terms of the
57532SSean.Ye@Sun.COM * Common Development and Distribution License (the "License").
67532SSean.Ye@Sun.COM * You may not use this file except in compliance with the License.
77532SSean.Ye@Sun.COM *
87532SSean.Ye@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97532SSean.Ye@Sun.COM * or http://www.opensolaris.org/os/licensing.
107532SSean.Ye@Sun.COM * See the License for the specific language governing permissions
117532SSean.Ye@Sun.COM * and limitations under the License.
127532SSean.Ye@Sun.COM *
137532SSean.Ye@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
147532SSean.Ye@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157532SSean.Ye@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
167532SSean.Ye@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
177532SSean.Ye@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
187532SSean.Ye@Sun.COM *
197532SSean.Ye@Sun.COM * CDDL HEADER END
207532SSean.Ye@Sun.COM */
217532SSean.Ye@Sun.COM
227532SSean.Ye@Sun.COM /*
2310175SStuart.Maybee@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
247532SSean.Ye@Sun.COM * Use is subject to license terms.
257532SSean.Ye@Sun.COM */
267532SSean.Ye@Sun.COM
277532SSean.Ye@Sun.COM #ifndef __xpv
287532SSean.Ye@Sun.COM #error "This file is for i86xpv only"
297532SSean.Ye@Sun.COM #endif
307532SSean.Ye@Sun.COM
317532SSean.Ye@Sun.COM #include <sys/types.h>
327532SSean.Ye@Sun.COM #include <sys/mca_x86.h>
337532SSean.Ye@Sun.COM #include <sys/archsystm.h>
347532SSean.Ye@Sun.COM #include <sys/hypervisor.h>
357532SSean.Ye@Sun.COM
367532SSean.Ye@Sun.COM #include "../../i86pc/cpu/generic_cpu/gcpu.h"
377532SSean.Ye@Sun.COM
387532SSean.Ye@Sun.COM extern xpv_mca_panic_data_t *xpv_mca_panic_data;
397532SSean.Ye@Sun.COM
407532SSean.Ye@Sun.COM mc_info_t gcpu_mce_data;
417532SSean.Ye@Sun.COM
427532SSean.Ye@Sun.COM enum mctelem_direction {
437532SSean.Ye@Sun.COM MCTELEM_FORWARD,
447532SSean.Ye@Sun.COM MCTELEM_REVERSE
457532SSean.Ye@Sun.COM };
467532SSean.Ye@Sun.COM
477532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_hdl_lookupfails;
487532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_bankhdr_found;
497532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_spechdr_found;
507532SSean.Ye@Sun.COM
517532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_mca_hcall_fails[16];
527532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_globalhdr_found;
537532SSean.Ye@Sun.COM
547532SSean.Ye@Sun.COM static cmi_mca_regs_t *gcpu_xpv_bankregs;
557532SSean.Ye@Sun.COM size_t gcpu_xpv_bankregs_sz;
567532SSean.Ye@Sun.COM
577532SSean.Ye@Sun.COM #define GCPU_XPV_ARCH_NREGS 3
587532SSean.Ye@Sun.COM
597532SSean.Ye@Sun.COM void
gcpu_xpv_mca_init(int nbanks)607532SSean.Ye@Sun.COM gcpu_xpv_mca_init(int nbanks)
617532SSean.Ye@Sun.COM {
627532SSean.Ye@Sun.COM if (gcpu_xpv_bankregs == NULL) {
637532SSean.Ye@Sun.COM gcpu_xpv_bankregs_sz = nbanks * GCPU_XPV_ARCH_NREGS *
647532SSean.Ye@Sun.COM sizeof (cmi_mca_regs_t);
657532SSean.Ye@Sun.COM
667532SSean.Ye@Sun.COM gcpu_xpv_bankregs = kmem_zalloc(gcpu_xpv_bankregs_sz, KM_SLEEP);
677532SSean.Ye@Sun.COM }
687532SSean.Ye@Sun.COM }
697532SSean.Ye@Sun.COM
707532SSean.Ye@Sun.COM static void
gcpu_xpv_proxy_logout(int what,struct mc_info * mi,struct mcinfo_common ** micp,int * idxp,cmi_mca_regs_t * bankregs,size_t bankregs_sz)717532SSean.Ye@Sun.COM gcpu_xpv_proxy_logout(int what, struct mc_info *mi, struct mcinfo_common **micp,
727532SSean.Ye@Sun.COM int *idxp, cmi_mca_regs_t *bankregs, size_t bankregs_sz)
737532SSean.Ye@Sun.COM {
747532SSean.Ye@Sun.COM struct mcinfo_global *mgi = (struct mcinfo_global *)(uintptr_t)*micp;
757532SSean.Ye@Sun.COM struct mcinfo_common *mic;
767532SSean.Ye@Sun.COM struct mcinfo_bank *mib;
777532SSean.Ye@Sun.COM cmi_hdl_t hdl = NULL;
787532SSean.Ye@Sun.COM cmi_mca_regs_t *mcrp;
797532SSean.Ye@Sun.COM gcpu_data_t *gcpu;
807532SSean.Ye@Sun.COM int idx = *idxp;
817532SSean.Ye@Sun.COM int tried = 0;
827532SSean.Ye@Sun.COM int nbanks, j;
837532SSean.Ye@Sun.COM
847532SSean.Ye@Sun.COM /* Skip over the MC_TYPE_GLOBAL record */
857532SSean.Ye@Sun.COM ASSERT(mgi->common.type == MC_TYPE_GLOBAL);
867532SSean.Ye@Sun.COM mic = x86_mcinfo_next((struct mcinfo_common *)(uintptr_t)mgi);
877532SSean.Ye@Sun.COM idx++;
887532SSean.Ye@Sun.COM
897532SSean.Ye@Sun.COM /*
907532SSean.Ye@Sun.COM * Process all MC_TYPE_BANK and MC_TYPE_EXTENDED records that
917532SSean.Ye@Sun.COM * follow the MC_TYPE_GLOBAL record, ending when we reach any
927532SSean.Ye@Sun.COM * other record type or when we're out of record.
937532SSean.Ye@Sun.COM *
947532SSean.Ye@Sun.COM * We skip over MC_TYPE_EXTENDED for now - nothing consumes
957532SSean.Ye@Sun.COM * the extended MSR data even in native Solaris.
967532SSean.Ye@Sun.COM */
977532SSean.Ye@Sun.COM while (idx < x86_mcinfo_nentries(mi) &&
987532SSean.Ye@Sun.COM (mic->type == MC_TYPE_BANK || mic->type == MC_TYPE_EXTENDED)) {
997532SSean.Ye@Sun.COM if (mic->type == MC_TYPE_EXTENDED) {
1007532SSean.Ye@Sun.COM gcpu_xpv_spechdr_found++;
1017532SSean.Ye@Sun.COM goto next_record;
1027532SSean.Ye@Sun.COM } else {
1037532SSean.Ye@Sun.COM gcpu_xpv_bankhdr_found++;
1047532SSean.Ye@Sun.COM }
1057532SSean.Ye@Sun.COM
1067532SSean.Ye@Sun.COM if (hdl == NULL && !tried++) {
1077532SSean.Ye@Sun.COM if ((hdl = cmi_hdl_lookup(CMI_HDL_SOLARIS_xVM_MCA,
1087532SSean.Ye@Sun.COM mgi->mc_socketid, mgi->mc_coreid,
1097532SSean.Ye@Sun.COM mgi->mc_core_threadid)) == NULL) {
1107532SSean.Ye@Sun.COM gcpu_xpv_hdl_lookupfails++;
1117532SSean.Ye@Sun.COM goto next_record;
1127532SSean.Ye@Sun.COM } else {
1137532SSean.Ye@Sun.COM gcpu = cmi_hdl_getcmidata(hdl);
1147532SSean.Ye@Sun.COM nbanks = gcpu->gcpu_mca.gcpu_mca_nbanks;
1157532SSean.Ye@Sun.COM bzero(bankregs, bankregs_sz);
1167532SSean.Ye@Sun.COM mcrp = bankregs;
1177532SSean.Ye@Sun.COM }
1187532SSean.Ye@Sun.COM }
1197532SSean.Ye@Sun.COM
1207532SSean.Ye@Sun.COM mib = (struct mcinfo_bank *)(uintptr_t)mic;
1217532SSean.Ye@Sun.COM
1227532SSean.Ye@Sun.COM mcrp->cmr_msrnum = IA32_MSR_MC(mib->mc_bank, STATUS);
1237532SSean.Ye@Sun.COM mcrp->cmr_msrval = mib->mc_status;
1247532SSean.Ye@Sun.COM mcrp++;
1257532SSean.Ye@Sun.COM
1267532SSean.Ye@Sun.COM mcrp->cmr_msrnum = IA32_MSR_MC(mib->mc_bank, ADDR);
1277532SSean.Ye@Sun.COM mcrp->cmr_msrval = mib->mc_addr;
1287532SSean.Ye@Sun.COM mcrp++;
1297532SSean.Ye@Sun.COM
1307532SSean.Ye@Sun.COM mcrp->cmr_msrnum = IA32_MSR_MC(mib->mc_bank, MISC);
1317532SSean.Ye@Sun.COM mcrp->cmr_msrval = mib->mc_misc;
1327532SSean.Ye@Sun.COM mcrp++;
1337532SSean.Ye@Sun.COM
1347532SSean.Ye@Sun.COM next_record:
1357532SSean.Ye@Sun.COM idx++;
1367532SSean.Ye@Sun.COM mic = x86_mcinfo_next(mic);
1377532SSean.Ye@Sun.COM }
1387532SSean.Ye@Sun.COM
1397532SSean.Ye@Sun.COM /*
1407532SSean.Ye@Sun.COM * If we found some telemetry and a handle to associate it with
1417532SSean.Ye@Sun.COM * then "forward" that telemetry into the MSR interpose layer
1427532SSean.Ye@Sun.COM * and then request logout which will find that interposed
1437532SSean.Ye@Sun.COM * telemetry. Indicate that logout code should clear bank
1447532SSean.Ye@Sun.COM * status registers so that it can invalidate them in the interpose
1457532SSean.Ye@Sun.COM * layer - they won't actually make it as far as real MSR writes.
1467532SSean.Ye@Sun.COM */
1477532SSean.Ye@Sun.COM if (hdl != NULL) {
1487532SSean.Ye@Sun.COM cmi_mca_regs_t gsr;
1497532SSean.Ye@Sun.COM gcpu_mce_status_t mce;
1507532SSean.Ye@Sun.COM
1517532SSean.Ye@Sun.COM gsr.cmr_msrnum = IA32_MSR_MCG_STATUS;
1527532SSean.Ye@Sun.COM gsr.cmr_msrval = mgi->mc_gstatus;
1537532SSean.Ye@Sun.COM cmi_hdl_msrforward(hdl, &gsr, 1);
1547532SSean.Ye@Sun.COM
1557532SSean.Ye@Sun.COM cmi_hdl_msrforward(hdl, bankregs, mcrp - bankregs);
1567532SSean.Ye@Sun.COM gcpu_mca_logout(hdl, NULL, (uint64_t)-1, &mce, B_TRUE, what);
1577532SSean.Ye@Sun.COM cmi_hdl_rele(hdl);
1587532SSean.Ye@Sun.COM }
1597532SSean.Ye@Sun.COM
1607532SSean.Ye@Sun.COM /*
1617532SSean.Ye@Sun.COM * We must move the index on at least one record or our caller
1627532SSean.Ye@Sun.COM * may loop forever; our initial increment over the global
1637532SSean.Ye@Sun.COM * record assures this.
1647532SSean.Ye@Sun.COM */
1657532SSean.Ye@Sun.COM ASSERT(idx > *idxp);
1667532SSean.Ye@Sun.COM *idxp = idx;
1677532SSean.Ye@Sun.COM *micp = mic;
1687532SSean.Ye@Sun.COM }
1697532SSean.Ye@Sun.COM
1707532SSean.Ye@Sun.COM /*
1717532SSean.Ye@Sun.COM * Process a struct mc_info.
1727532SSean.Ye@Sun.COM *
1737532SSean.Ye@Sun.COM * There are x86_mcinfo_nentries(mi) entries. An entry of type
1747532SSean.Ye@Sun.COM * MC_TYPE_GLOBAL precedes a number (potentially zero) of
1757532SSean.Ye@Sun.COM * entries of type MC_TYPE_BANK for telemetry from MCA banks
1767532SSean.Ye@Sun.COM * of the resource identified in the MC_TYPE_GLOBAL entry.
1777532SSean.Ye@Sun.COM * I think there can be multiple MC_TYPE_GLOBAL entries per buffer.
1787532SSean.Ye@Sun.COM */
1797532SSean.Ye@Sun.COM void
gcpu_xpv_mci_process(mc_info_t * mi,int type,cmi_mca_regs_t * bankregs,size_t bankregs_sz)1807532SSean.Ye@Sun.COM gcpu_xpv_mci_process(mc_info_t *mi, int type,
1817532SSean.Ye@Sun.COM cmi_mca_regs_t *bankregs, size_t bankregs_sz)
1827532SSean.Ye@Sun.COM {
1837532SSean.Ye@Sun.COM struct mcinfo_common *mic;
1847532SSean.Ye@Sun.COM int idx;
1857532SSean.Ye@Sun.COM
1867532SSean.Ye@Sun.COM mic = x86_mcinfo_first(mi);
1877532SSean.Ye@Sun.COM
1887532SSean.Ye@Sun.COM idx = 0;
1897532SSean.Ye@Sun.COM while (idx < x86_mcinfo_nentries(mi)) {
1907532SSean.Ye@Sun.COM if (mic->type == MC_TYPE_GLOBAL) {
1917532SSean.Ye@Sun.COM gcpu_xpv_globalhdr_found++;
19210175SStuart.Maybee@Sun.COM gcpu_xpv_proxy_logout(type == XEN_MC_URGENT ?
1937532SSean.Ye@Sun.COM GCPU_MPT_WHAT_MC_ERR : GCPU_MPT_WHAT_XPV_VIRQ,
1947532SSean.Ye@Sun.COM mi, &mic, &idx, bankregs, bankregs_sz);
1957532SSean.Ye@Sun.COM } else {
1967532SSean.Ye@Sun.COM idx++;
1977532SSean.Ye@Sun.COM mic = x86_mcinfo_next(mic);
1987532SSean.Ye@Sun.COM }
1997532SSean.Ye@Sun.COM }
2007532SSean.Ye@Sun.COM }
2017532SSean.Ye@Sun.COM
2027532SSean.Ye@Sun.COM int
gcpu_xpv_telem_read(mc_info_t * mci,int type,uint64_t * idp)2037532SSean.Ye@Sun.COM gcpu_xpv_telem_read(mc_info_t *mci, int type, uint64_t *idp)
2047532SSean.Ye@Sun.COM {
205*11120SMark.Johnson@Sun.COM xen_mc_t xmc;
206*11120SMark.Johnson@Sun.COM xen_mc_fetch_t *mcf = &xmc.u.mc_fetch;
2077532SSean.Ye@Sun.COM long err;
2087532SSean.Ye@Sun.COM
209*11120SMark.Johnson@Sun.COM mcf->flags = type;
210*11120SMark.Johnson@Sun.COM set_xen_guest_handle(mcf->data, mci);
2117532SSean.Ye@Sun.COM
212*11120SMark.Johnson@Sun.COM if ((err = HYPERVISOR_mca(XEN_MC_fetch, &xmc)) != 0) {
2137532SSean.Ye@Sun.COM gcpu_xpv_mca_hcall_fails[err < 16 ? err : 0]++;
2147532SSean.Ye@Sun.COM return (0);
2157532SSean.Ye@Sun.COM }
2167532SSean.Ye@Sun.COM
217*11120SMark.Johnson@Sun.COM if (mcf->flags == XEN_MC_OK) {
218*11120SMark.Johnson@Sun.COM *idp = mcf->fetch_id;
2197532SSean.Ye@Sun.COM return (1);
2207532SSean.Ye@Sun.COM } else {
2217532SSean.Ye@Sun.COM *idp = 0;
2227532SSean.Ye@Sun.COM return (0);
2237532SSean.Ye@Sun.COM }
2247532SSean.Ye@Sun.COM }
2257532SSean.Ye@Sun.COM
2267532SSean.Ye@Sun.COM void
gcpu_xpv_telem_ack(int type,uint64_t fetch_id)2277532SSean.Ye@Sun.COM gcpu_xpv_telem_ack(int type, uint64_t fetch_id)
2287532SSean.Ye@Sun.COM {
229*11120SMark.Johnson@Sun.COM xen_mc_t xmc;
230*11120SMark.Johnson@Sun.COM struct xen_mc_fetch *mcf = &xmc.u.mc_fetch;
2317532SSean.Ye@Sun.COM
232*11120SMark.Johnson@Sun.COM mcf->flags = type | XEN_MC_ACK;
233*11120SMark.Johnson@Sun.COM mcf->fetch_id = fetch_id;
234*11120SMark.Johnson@Sun.COM (void) HYPERVISOR_mca(XEN_MC_fetch, &xmc);
2357532SSean.Ye@Sun.COM }
2367532SSean.Ye@Sun.COM
2377532SSean.Ye@Sun.COM static void
mctelem_traverse(void * head,enum mctelem_direction direction,boolean_t urgent)2387532SSean.Ye@Sun.COM mctelem_traverse(void *head, enum mctelem_direction direction,
2397532SSean.Ye@Sun.COM boolean_t urgent)
2407532SSean.Ye@Sun.COM {
2417532SSean.Ye@Sun.COM char *tep = head, **ntepp;
2427532SSean.Ye@Sun.COM int noff = (direction == MCTELEM_FORWARD) ?
2437532SSean.Ye@Sun.COM xpv_mca_panic_data->mpd_fwdptr_offset :
2447532SSean.Ye@Sun.COM xpv_mca_panic_data->mpd_revptr_offset;
2457532SSean.Ye@Sun.COM
2467532SSean.Ye@Sun.COM
2477532SSean.Ye@Sun.COM while (tep != NULL) {
2487532SSean.Ye@Sun.COM struct mc_info **mcip = (struct mc_info **)
2497532SSean.Ye@Sun.COM (tep + xpv_mca_panic_data->mpd_dataptr_offset);
2507532SSean.Ye@Sun.COM
2517532SSean.Ye@Sun.COM gcpu_xpv_mci_process(*mcip,
25210175SStuart.Maybee@Sun.COM urgent ? XEN_MC_URGENT : XEN_MC_NONURGENT,
2537532SSean.Ye@Sun.COM gcpu_xpv_bankregs, gcpu_xpv_bankregs_sz);
2547532SSean.Ye@Sun.COM
2557532SSean.Ye@Sun.COM ntepp = (char **)(tep + noff);
2567532SSean.Ye@Sun.COM tep = *ntepp;
2577532SSean.Ye@Sun.COM }
2587532SSean.Ye@Sun.COM }
2597532SSean.Ye@Sun.COM
2607532SSean.Ye@Sun.COM /*
2617532SSean.Ye@Sun.COM * Callback made from panicsys. We may have reached panicsys from a
2627532SSean.Ye@Sun.COM * Solaris-initiated panic or a hypervisor-initiated panic; for the
2637532SSean.Ye@Sun.COM * latter we may not perform any hypercalls. Our task is to retrieve
2647532SSean.Ye@Sun.COM * unprocessed MCA telemetry from the hypervisor and shovel it into
2657532SSean.Ye@Sun.COM * errorqs for later processing during panic.
2667532SSean.Ye@Sun.COM */
2677532SSean.Ye@Sun.COM void
gcpu_xpv_panic_callback(void)2687532SSean.Ye@Sun.COM gcpu_xpv_panic_callback(void)
2697532SSean.Ye@Sun.COM {
2707532SSean.Ye@Sun.COM if (IN_XPV_PANIC()) {
2717532SSean.Ye@Sun.COM xpv_mca_panic_data_t *ti = xpv_mca_panic_data;
2727532SSean.Ye@Sun.COM
2737532SSean.Ye@Sun.COM if (ti == NULL ||
2747532SSean.Ye@Sun.COM ti->mpd_magic != MCA_PANICDATA_MAGIC ||
2757532SSean.Ye@Sun.COM ti->mpd_version != MCA_PANICDATA_VERS)
2767532SSean.Ye@Sun.COM return;
2777532SSean.Ye@Sun.COM
2787532SSean.Ye@Sun.COM mctelem_traverse(ti->mpd_urgent_processing, MCTELEM_FORWARD,
2797532SSean.Ye@Sun.COM B_TRUE);
2807532SSean.Ye@Sun.COM mctelem_traverse(ti->mpd_urgent_dangling, MCTELEM_REVERSE,
2817532SSean.Ye@Sun.COM B_TRUE);
2827532SSean.Ye@Sun.COM mctelem_traverse(ti->mpd_urgent_committed, MCTELEM_REVERSE,
2837532SSean.Ye@Sun.COM B_TRUE);
2847532SSean.Ye@Sun.COM
2857532SSean.Ye@Sun.COM mctelem_traverse(ti->mpd_nonurgent_processing, MCTELEM_FORWARD,
2867532SSean.Ye@Sun.COM B_FALSE);
2877532SSean.Ye@Sun.COM mctelem_traverse(ti->mpd_nonurgent_dangling, MCTELEM_REVERSE,
2887532SSean.Ye@Sun.COM B_FALSE);
2897532SSean.Ye@Sun.COM mctelem_traverse(ti->mpd_nonurgent_committed, MCTELEM_REVERSE,
2907532SSean.Ye@Sun.COM B_FALSE);
2917532SSean.Ye@Sun.COM } else {
29210175SStuart.Maybee@Sun.COM int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT };
2937532SSean.Ye@Sun.COM uint64_t fetch_id;
2947532SSean.Ye@Sun.COM int i;
2957532SSean.Ye@Sun.COM
2967532SSean.Ye@Sun.COM for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) {
2977532SSean.Ye@Sun.COM while (gcpu_xpv_telem_read(&gcpu_mce_data,
2987532SSean.Ye@Sun.COM types[i], &fetch_id)) {
2997532SSean.Ye@Sun.COM gcpu_xpv_mci_process(&gcpu_mce_data, types[i],
3007532SSean.Ye@Sun.COM gcpu_xpv_bankregs, gcpu_xpv_bankregs_sz);
3017532SSean.Ye@Sun.COM gcpu_xpv_telem_ack(types[i], fetch_id);
3027532SSean.Ye@Sun.COM }
3037532SSean.Ye@Sun.COM }
3047532SSean.Ye@Sun.COM }
3057532SSean.Ye@Sun.COM }
306