xref: /illumos-gate/usr/src/uts/i86xpv/cpu/generic_cpu/gcpu_poll_xpv.c (revision 349b53dd4e695e3d833b5380540385145b2d3ae8)
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 /*
23*349b53ddSStuart 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 /*
28e4b86885SCheng Sean Ye  * "Polled" MCA events in an i86xpv dom0.  A timeout runs in the hypervisor
29e4b86885SCheng Sean Ye  * and checks MCA state.  If it observes valid MCA state in a bank and if
30e4b86885SCheng Sean Ye  * it sees that dom0 has registered a handler for the VIRQ_MCA then it
31e4b86885SCheng Sean Ye  * raises that VIRQ to dom0.  The interrupt handler performs a
32e4b86885SCheng Sean Ye  * hypercall to retrieve the polled telemetry and then pushes that telemetry
33e4b86885SCheng Sean Ye  * into the MSR interpose hash and calls the generic logout code which
34e4b86885SCheng Sean Ye  * will then find the provided interposed MSR values when it performs
35e4b86885SCheng Sean Ye  * cmi_hdl_rdmsr so logout code works unchanged for native or i86xpv dom0.
36e4b86885SCheng Sean Ye  */
37e4b86885SCheng Sean Ye 
38e4b86885SCheng Sean Ye #include <sys/types.h>
39e4b86885SCheng Sean Ye #include <sys/conf.h>
40e4b86885SCheng Sean Ye #include <sys/x86_archext.h>
41e4b86885SCheng Sean Ye #include <sys/mca_x86.h>
42e4b86885SCheng Sean Ye #include <sys/ddi.h>
43e4b86885SCheng Sean Ye #include <sys/spl.h>
44e4b86885SCheng Sean Ye #include <sys/sunddi.h>
45e4b86885SCheng Sean Ye #include <sys/evtchn_impl.h>
46e4b86885SCheng Sean Ye #include <sys/hypervisor.h>
47e4b86885SCheng Sean Ye 
48e4b86885SCheng Sean Ye #include "../../i86pc/cpu/generic_cpu/gcpu.h"
49e4b86885SCheng Sean Ye 
50e4b86885SCheng Sean Ye extern int *gcpu_xpv_telem_read(mc_info_t *, int, uint64_t *);
51e4b86885SCheng Sean Ye extern void gcpu_xpv_telem_ack(int, uint64_t);
52e4b86885SCheng Sean Ye extern void gcpu_xpv_mci_process(mc_info_t *, int, cmi_mca_regs_t *, size_t);
53e4b86885SCheng Sean Ye 
54e4b86885SCheng Sean Ye int gcpu_xpv_mch_poll_interval_secs = 10;
55e4b86885SCheng Sean Ye int gcpu_xpv_virq_level = 3;
56e4b86885SCheng Sean Ye 
57e4b86885SCheng Sean Ye static timeout_id_t gcpu_xpv_mch_poll_timeoutid;
58e4b86885SCheng Sean Ye 
59e4b86885SCheng Sean Ye static int gcpu_xpv_virq_vect = -1;
60e4b86885SCheng Sean Ye 
61e4b86885SCheng Sean Ye static mc_info_t gcpu_xpv_polldata;
62e4b86885SCheng Sean Ye static kmutex_t gcpu_xpv_polldata_lock;
63e4b86885SCheng Sean Ye 
64e4b86885SCheng Sean Ye static cmi_mca_regs_t *gcpu_xpv_poll_bankregs;
65e4b86885SCheng Sean Ye static size_t gcpu_xpv_poll_bankregs_sz;
66e4b86885SCheng Sean Ye 
67e4b86885SCheng Sean Ye static uint32_t gcpu_xpv_intr_unclaimed;
68e4b86885SCheng Sean Ye static uint32_t gcpu_xpv_mca_hcall_busy;
69e4b86885SCheng Sean Ye 
70e4b86885SCheng Sean Ye static gcpu_poll_trace_ctl_t gcpu_xpv_poll_trace_ctl;
71e4b86885SCheng Sean Ye 
72e4b86885SCheng Sean Ye #define	GCPU_XPV_ARCH_NREGS		3
73e4b86885SCheng Sean Ye #define	GCPU_XPV_MCH_POLL_REARM		((void *)1)
74e4b86885SCheng Sean Ye #define	GCPU_XPV_MCH_POLL_NO_REARM	NULL
75e4b86885SCheng Sean Ye 
76e4b86885SCheng Sean Ye static uint_t
77e4b86885SCheng Sean Ye gcpu_xpv_virq_intr(void)
78e4b86885SCheng Sean Ye {
79*349b53ddSStuart Maybee 	int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT };
80e4b86885SCheng Sean Ye 	uint64_t fetch_id;
81e4b86885SCheng Sean Ye 	int count = 0;
82e4b86885SCheng Sean Ye 	int i;
83e4b86885SCheng Sean Ye 
84e4b86885SCheng Sean Ye 	if (gcpu_xpv_virq_vect == -1 || gcpu_xpv_poll_bankregs_sz == 0) {
85e4b86885SCheng Sean Ye 		gcpu_xpv_intr_unclaimed++;
86e4b86885SCheng Sean Ye 		return (DDI_INTR_UNCLAIMED);
87e4b86885SCheng Sean Ye 	}
88e4b86885SCheng Sean Ye 
89e4b86885SCheng Sean Ye 	if (!mutex_tryenter(&gcpu_xpv_polldata_lock)) {
90e4b86885SCheng Sean Ye 		gcpu_xpv_mca_hcall_busy++;
91e4b86885SCheng Sean Ye 		return (DDI_INTR_CLAIMED);
92e4b86885SCheng Sean Ye 	}
93e4b86885SCheng Sean Ye 
94e4b86885SCheng Sean Ye 	for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) {
95e4b86885SCheng Sean Ye 		while (gcpu_xpv_telem_read(&gcpu_xpv_polldata, types[i],
96e4b86885SCheng Sean Ye 		    &fetch_id)) {
97e4b86885SCheng Sean Ye 			gcpu_poll_trace(&gcpu_xpv_poll_trace_ctl,
98e4b86885SCheng Sean Ye 			    GCPU_MPT_WHAT_XPV_VIRQ,
99e4b86885SCheng Sean Ye 			    x86_mcinfo_nentries(&gcpu_xpv_polldata));
100e4b86885SCheng Sean Ye 			gcpu_xpv_mci_process(&gcpu_xpv_polldata, types[i],
101e4b86885SCheng Sean Ye 			    gcpu_xpv_poll_bankregs, gcpu_xpv_poll_bankregs_sz);
102e4b86885SCheng Sean Ye 			gcpu_xpv_telem_ack(types[i], fetch_id);
103e4b86885SCheng Sean Ye 			count++;
104e4b86885SCheng Sean Ye 		}
105e4b86885SCheng Sean Ye 	}
106e4b86885SCheng Sean Ye 
107e4b86885SCheng Sean Ye 	mutex_exit(&gcpu_xpv_polldata_lock);
108e4b86885SCheng Sean Ye 
109e4b86885SCheng Sean Ye 	return (DDI_INTR_CLAIMED);
110e4b86885SCheng Sean Ye }
111e4b86885SCheng Sean Ye 
112e4b86885SCheng Sean Ye static void
113e4b86885SCheng Sean Ye gcpu_xpv_mch_poll(void *arg)
114e4b86885SCheng Sean Ye {
115e4b86885SCheng Sean Ye 	cmi_hdl_t hdl = cmi_hdl_any();
116e4b86885SCheng Sean Ye 
117e4b86885SCheng Sean Ye 	if (hdl != NULL) {
118e4b86885SCheng Sean Ye 		cmi_mc_logout(hdl, 0, 0);
119e4b86885SCheng Sean Ye 		cmi_hdl_rele(hdl);
120e4b86885SCheng Sean Ye 	}
121e4b86885SCheng Sean Ye 
122e4b86885SCheng Sean Ye 	if (arg == GCPU_XPV_MCH_POLL_REARM &&
123e4b86885SCheng Sean Ye 	    gcpu_xpv_mch_poll_interval_secs != 0) {
124e4b86885SCheng Sean Ye 		gcpu_xpv_mch_poll_timeoutid = timeout(gcpu_xpv_mch_poll,
125e4b86885SCheng Sean Ye 		    GCPU_XPV_MCH_POLL_REARM,
126e4b86885SCheng Sean Ye 		    drv_usectohz(gcpu_xpv_mch_poll_interval_secs * MICROSEC));
127e4b86885SCheng Sean Ye 	}
128e4b86885SCheng Sean Ye }
129e4b86885SCheng Sean Ye 
130e4b86885SCheng Sean Ye /*
131e4b86885SCheng Sean Ye  * gcpu_mca_poll_init is called from gcpu_mca_init for each cpu handle
132e4b86885SCheng Sean Ye  * that we initialize for.  It should prepare for polling by allocating
133e4b86885SCheng Sean Ye  * control structures and the like, but must not kick polling off yet.
134e4b86885SCheng Sean Ye  *
135e4b86885SCheng Sean Ye  * Since we initialize all cpus in a serialized loop there is no race
136e4b86885SCheng Sean Ye  * on allocating the bankregs structure, nor in free'ing and enlarging
137e4b86885SCheng Sean Ye  * it if we find the number of MCA banks is not uniform in the system
138e4b86885SCheng Sean Ye  * (unlikely) since polling is only started post mp startup.
139e4b86885SCheng Sean Ye  */
140e4b86885SCheng Sean Ye 
141e4b86885SCheng Sean Ye void
142e4b86885SCheng Sean Ye gcpu_mca_poll_init(cmi_hdl_t hdl)
143e4b86885SCheng Sean Ye {
144e4b86885SCheng Sean Ye 	gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl);
145e4b86885SCheng Sean Ye 	int nbanks = gcpu->gcpu_mca.gcpu_mca_nbanks;
146e4b86885SCheng Sean Ye 	size_t sz = nbanks * GCPU_XPV_ARCH_NREGS * sizeof (cmi_mca_regs_t);
147e4b86885SCheng Sean Ye 
148e4b86885SCheng Sean Ye 	ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA);
149e4b86885SCheng Sean Ye 
150e4b86885SCheng Sean Ye 	if (gcpu_xpv_poll_bankregs == NULL || sz > gcpu_xpv_poll_bankregs_sz) {
151e4b86885SCheng Sean Ye 		if (gcpu_xpv_poll_bankregs != NULL) {
152e4b86885SCheng Sean Ye 			kmem_free(gcpu_xpv_poll_bankregs,
153e4b86885SCheng Sean Ye 			    gcpu_xpv_poll_bankregs_sz);
154e4b86885SCheng Sean Ye 		} else {
155e4b86885SCheng Sean Ye 			gcpu_poll_trace_init(&gcpu_xpv_poll_trace_ctl);
156e4b86885SCheng Sean Ye 		}
157e4b86885SCheng Sean Ye 
158e4b86885SCheng Sean Ye 		gcpu_xpv_poll_bankregs_sz = sz;
159e4b86885SCheng Sean Ye 		gcpu_xpv_poll_bankregs = kmem_zalloc(sz, KM_SLEEP);
160e4b86885SCheng Sean Ye 
161e4b86885SCheng Sean Ye 	}
162e4b86885SCheng Sean Ye }
163e4b86885SCheng Sean Ye 
164e4b86885SCheng Sean Ye void
165e4b86885SCheng Sean Ye gcpu_mca_poll_start(cmi_hdl_t hdl)
166e4b86885SCheng Sean Ye {
167e4b86885SCheng Sean Ye 	ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA);
168e4b86885SCheng Sean Ye 	/*
169e4b86885SCheng Sean Ye 	 * We are on the boot cpu (cpu 0), called at the end of its
170e4b86885SCheng Sean Ye 	 * multiprocessor startup.
171e4b86885SCheng Sean Ye 	 */
172e4b86885SCheng Sean Ye 	if (gcpu_xpv_poll_bankregs_sz != 0 && gcpu_xpv_virq_vect == -1) {
173e4b86885SCheng Sean Ye 		/*
174e4b86885SCheng Sean Ye 		 * The hypervisor will poll MCA state for us, but it cannot
175e4b86885SCheng Sean Ye 		 * poll MCH state so we do that via a timeout.
176e4b86885SCheng Sean Ye 		 */
177e4b86885SCheng Sean Ye 		if (gcpu_xpv_mch_poll_interval_secs != 0) {
178e4b86885SCheng Sean Ye 			gcpu_xpv_mch_poll_timeoutid =
179e4b86885SCheng Sean Ye 			    timeout(gcpu_xpv_mch_poll, GCPU_XPV_MCH_POLL_REARM,
180e4b86885SCheng Sean Ye 			    drv_usectohz(gcpu_xpv_mch_poll_interval_secs *
181e4b86885SCheng Sean Ye 			    MICROSEC));
182e4b86885SCheng Sean Ye 		}
183e4b86885SCheng Sean Ye 
184e4b86885SCheng Sean Ye 		/*
185e4b86885SCheng Sean Ye 		 * Register handler for VIRQ_MCA; once this is in place
186e4b86885SCheng Sean Ye 		 * the hypervisor will begin to forward polled MCA observations
187e4b86885SCheng Sean Ye 		 * to us.
188e4b86885SCheng Sean Ye 		 */
189e4b86885SCheng Sean Ye 		gcpu_xpv_virq_vect = ec_bind_virq_to_irq(VIRQ_MCA, 0);
190e4b86885SCheng Sean Ye 		(void) add_avintr(NULL, gcpu_xpv_virq_level,
191e4b86885SCheng Sean Ye 		    (avfunc)gcpu_xpv_virq_intr, "MCA", gcpu_xpv_virq_vect,
192e4b86885SCheng Sean Ye 		    NULL, NULL, NULL, NULL);
193e4b86885SCheng Sean Ye 	}
194e4b86885SCheng Sean Ye }
195