gcpu_mca_xpv.c revision 10175:dd9708d1f561
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
38extern xpv_mca_panic_data_t *xpv_mca_panic_data;
39
40mc_info_t gcpu_mce_data;
41
42enum mctelem_direction {
43	MCTELEM_FORWARD,
44	MCTELEM_REVERSE
45};
46
47static uint32_t gcpu_xpv_hdl_lookupfails;
48static uint32_t gcpu_xpv_bankhdr_found;
49static uint32_t gcpu_xpv_spechdr_found;
50
51static uint32_t gcpu_xpv_mca_hcall_fails[16];
52static uint32_t gcpu_xpv_globalhdr_found;
53
54static cmi_mca_regs_t *gcpu_xpv_bankregs;
55size_t gcpu_xpv_bankregs_sz;
56
57#define	GCPU_XPV_ARCH_NREGS	3
58
59void
60gcpu_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
70static void
71gcpu_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
134next_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 */
179void
180gcpu_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
202int
203gcpu_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	set_xen_guest_handle(mcf.data, mci);
210
211	if ((err = HYPERVISOR_mca(XEN_MC_fetch, (xen_mc_arg_t *)&mcf)) != 0) {
212		gcpu_xpv_mca_hcall_fails[err < 16 ? err : 0]++;
213		return (0);
214	}
215
216	if (mcf.flags == XEN_MC_OK) {
217		*idp = mcf.fetch_id;
218		return (1);
219	} else {
220		*idp = 0;
221		return (0);
222	}
223}
224
225void
226gcpu_xpv_telem_ack(int type, uint64_t fetch_id)
227{
228	struct xen_mc_fetch mcf;
229
230	mcf.flags = type | XEN_MC_ACK;
231	mcf.fetch_id = fetch_id;
232	(void) HYPERVISOR_mca(XEN_MC_fetch, (xen_mc_arg_t *)&mcf);
233}
234
235static void
236mctelem_traverse(void *head, enum mctelem_direction direction,
237    boolean_t urgent)
238{
239	char *tep = head, **ntepp;
240	int noff = (direction == MCTELEM_FORWARD) ?
241	    xpv_mca_panic_data->mpd_fwdptr_offset :
242	    xpv_mca_panic_data->mpd_revptr_offset;
243
244
245	while (tep != NULL) {
246		struct mc_info **mcip = (struct mc_info **)
247		    (tep + xpv_mca_panic_data->mpd_dataptr_offset);
248
249		gcpu_xpv_mci_process(*mcip,
250		    urgent ? XEN_MC_URGENT : XEN_MC_NONURGENT,
251		    gcpu_xpv_bankregs, gcpu_xpv_bankregs_sz);
252
253		ntepp = (char **)(tep + noff);
254		tep = *ntepp;
255	}
256}
257
258/*
259 * Callback made from panicsys.  We may have reached panicsys from a
260 * Solaris-initiated panic or a hypervisor-initiated panic;  for the
261 * latter we may not perform any hypercalls.  Our task is to retrieve
262 * unprocessed MCA telemetry from the hypervisor and shovel it into
263 * errorqs for later processing during panic.
264 */
265void
266gcpu_xpv_panic_callback(void)
267{
268	if (IN_XPV_PANIC()) {
269		xpv_mca_panic_data_t *ti = xpv_mca_panic_data;
270
271		if (ti == NULL ||
272		    ti->mpd_magic != MCA_PANICDATA_MAGIC ||
273		    ti->mpd_version != MCA_PANICDATA_VERS)
274			return;
275
276		mctelem_traverse(ti->mpd_urgent_processing, MCTELEM_FORWARD,
277		    B_TRUE);
278		mctelem_traverse(ti->mpd_urgent_dangling, MCTELEM_REVERSE,
279		    B_TRUE);
280		mctelem_traverse(ti->mpd_urgent_committed, MCTELEM_REVERSE,
281		    B_TRUE);
282
283		mctelem_traverse(ti->mpd_nonurgent_processing, MCTELEM_FORWARD,
284		    B_FALSE);
285		mctelem_traverse(ti->mpd_nonurgent_dangling, MCTELEM_REVERSE,
286		    B_FALSE);
287		mctelem_traverse(ti->mpd_nonurgent_committed, MCTELEM_REVERSE,
288		    B_FALSE);
289	} else {
290		int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT };
291		uint64_t fetch_id;
292		int i;
293
294		for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) {
295			while (gcpu_xpv_telem_read(&gcpu_mce_data,
296			    types[i], &fetch_id)) {
297				gcpu_xpv_mci_process(&gcpu_mce_data, types[i],
298				    gcpu_xpv_bankregs, gcpu_xpv_bankregs_sz);
299				gcpu_xpv_telem_ack(types[i], fetch_id);
300			}
301		}
302	}
303}
304