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