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
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
gcpu_xpv_mca_init(int nbanks)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
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)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
gcpu_xpv_mci_process(mc_info_t * mi,int type,cmi_mca_regs_t * bankregs,size_t bankregs_sz)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_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
gcpu_xpv_telem_read(mc_info_t * mci,int type,uint64_t * idp)203 gcpu_xpv_telem_read(mc_info_t *mci, int type, uint64_t *idp)
204 {
205 xen_mc_t xmc;
206 xen_mc_fetch_t *mcf = &xmc.u.mc_fetch;
207 long err;
208
209 mcf->flags = type;
210 set_xen_guest_handle(mcf->data, mci);
211
212 if ((err = HYPERVISOR_mca(XEN_MC_fetch, &xmc)) != 0) {
213 gcpu_xpv_mca_hcall_fails[err < 16 ? err : 0]++;
214 return (0);
215 }
216
217 if (mcf->flags == XEN_MC_OK) {
218 *idp = mcf->fetch_id;
219 return (1);
220 } else {
221 *idp = 0;
222 return (0);
223 }
224 }
225
226 void
gcpu_xpv_telem_ack(int type,uint64_t fetch_id)227 gcpu_xpv_telem_ack(int type, uint64_t fetch_id)
228 {
229 xen_mc_t xmc;
230 struct xen_mc_fetch *mcf = &xmc.u.mc_fetch;
231
232 mcf->flags = type | XEN_MC_ACK;
233 mcf->fetch_id = fetch_id;
234 (void) HYPERVISOR_mca(XEN_MC_fetch, &xmc);
235 }
236
237 static void
mctelem_traverse(void * head,enum mctelem_direction direction,boolean_t urgent)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_URGENT : XEN_MC_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
gcpu_xpv_panic_callback(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_URGENT, XEN_MC_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