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 2008 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_F_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_fetch_t mcf;
206 	long err;
207 
208 	mcf.flags = type;
209 	mcf.data_sz = sizeof (mc_info_t);
210 	set_xen_guest_handle(mcf.data, mci);
211 
212 	if ((err = HYPERVISOR_mca(XEN_MC_CMD_fetch,
213 	    (xen_mc_arg_t *)&mcf)) != XEN_MC_HCALL_SUCCESS) {
214 		gcpu_xpv_mca_hcall_fails[err < 16 ? err : 0]++;
215 		return (0);
216 	}
217 
218 	if (mcf.flags == XEN_MC_F_OK) {
219 		*idp = mcf.fetch_id;
220 		return (1);
221 	} else {
222 		*idp = 0;
223 		return (0);
224 	}
225 }
226 
227 void
228 gcpu_xpv_telem_ack(int type, uint64_t fetch_id)
229 {
230 	struct xen_mc_fetch mcf;
231 
232 	mcf.flags = type | XEN_MC_F_ACK;
233 	mcf.fetch_id = fetch_id;
234 	(void) HYPERVISOR_mca(XEN_MC_CMD_fetch, (xen_mc_arg_t *)&mcf);
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_F_URGENT : XEN_MC_F_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_F_URGENT, XEN_MC_F_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