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