xref: /illumos-gate/usr/src/uts/i86pc/i86hvm/io/xpv/evtchn.c (revision 86ef0a63)
1551bc2a6Smrj /*
2551bc2a6Smrj  * CDDL HEADER START
3551bc2a6Smrj  *
4551bc2a6Smrj  * The contents of this file are subject to the terms of the
5551bc2a6Smrj  * Common Development and Distribution License (the "License").
6551bc2a6Smrj  * You may not use this file except in compliance with the License.
7551bc2a6Smrj  *
8551bc2a6Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9551bc2a6Smrj  * or http://www.opensolaris.org/os/licensing.
10551bc2a6Smrj  * See the License for the specific language governing permissions
11551bc2a6Smrj  * and limitations under the License.
12551bc2a6Smrj  *
13551bc2a6Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14551bc2a6Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15551bc2a6Smrj  * If applicable, add the following below this CDDL HEADER, with the
16551bc2a6Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17551bc2a6Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18551bc2a6Smrj  *
19551bc2a6Smrj  * CDDL HEADER END
20551bc2a6Smrj  */
21551bc2a6Smrj 
22551bc2a6Smrj /*
23349b53ddSStuart Maybee  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24551bc2a6Smrj  * Use is subject to license terms.
25551bc2a6Smrj  */
26551bc2a6Smrj 
27*dde8fce6SJeremy Jones /*
28*dde8fce6SJeremy Jones  * Copyright (c) 2014 by Delphix. All rights reserved.
29*dde8fce6SJeremy Jones  */
30*dde8fce6SJeremy Jones 
31551bc2a6Smrj #include <sys/types.h>
32551bc2a6Smrj #include <sys/xpv_support.h>
33551bc2a6Smrj #include <sys/hypervisor.h>
34551bc2a6Smrj #include <sys/machsystm.h>
35551bc2a6Smrj #include <sys/mutex.h>
36551bc2a6Smrj #include <sys/cmn_err.h>
37551bc2a6Smrj #include <sys/dditypes.h>
38551bc2a6Smrj #include <sys/atomic.h>
39551bc2a6Smrj #include <sys/sysmacros.h>
40551bc2a6Smrj #include <sys/cpu.h>
41551bc2a6Smrj #include <sys/psw.h>
42551bc2a6Smrj #include <sys/psm.h>
43551bc2a6Smrj #include <sys/sdt.h>
44551bc2a6Smrj 
45551bc2a6Smrj extern dev_info_t *xpv_dip;
46551bc2a6Smrj static ddi_intr_handle_t *evtchn_ihp = NULL;
47551bc2a6Smrj static ddi_softint_handle_t evtchn_to_handle[NR_EVENT_CHANNELS];
48ea8190a2Ssmaybe kmutex_t ec_lock;
49551bc2a6Smrj 
50551bc2a6Smrj static int evtchn_callback_irq = -1;
51551bc2a6Smrj 
52c50ea120Srab static volatile ulong_t *pending_events;
53c50ea120Srab static volatile ulong_t *masked_events;
54c50ea120Srab 
55c50ea120Srab /* log2(NBBY * sizeof (ulong)) */
56c50ea120Srab #define	EVTCHN_SHIFT	6
57c50ea120Srab 
58c50ea120Srab /* Atomically get and clear a ulong from memory. */
59c50ea120Srab #define	GET_AND_CLEAR(src, targ) {				\
60551bc2a6Smrj 	membar_enter();						\
61551bc2a6Smrj 	do {							\
62c50ea120Srab 		targ = *src;					\
63c50ea120Srab 	} while (atomic_cas_ulong(src, targ, 0) != targ);	\
64551bc2a6Smrj }
65551bc2a6Smrj 
66551bc2a6Smrj /* Get the first and last bits set in a bitmap */
67c50ea120Srab #define	GET_BOUNDS(bitmap, low, high)	 {	\
68551bc2a6Smrj 	int _i;						\
69551bc2a6Smrj 	low = high = -1;				\
70c50ea120Srab 	for (_i = 0; _i <= sizeof (ulong_t); _i++)			\
71c50ea120Srab 		if (bitmap & (1UL << _i)) {	\
72551bc2a6Smrj 			if (low == -1)			\
73551bc2a6Smrj 				low = _i;		\
74551bc2a6Smrj 			high = _i;			\
75551bc2a6Smrj 		}					\
76551bc2a6Smrj }
77551bc2a6Smrj 
78551bc2a6Smrj void
ec_bind_evtchn_to_handler(int evtchn,pri_t pri,ec_handler_fcn_t handler,void * arg1)79551bc2a6Smrj ec_bind_evtchn_to_handler(int evtchn, pri_t pri, ec_handler_fcn_t handler,
80551bc2a6Smrj     void *arg1)
81551bc2a6Smrj {
82551bc2a6Smrj 	ddi_softint_handle_t hdl;
83551bc2a6Smrj 
84349b53ddSStuart Maybee 	if (evtchn < 0 || evtchn >= NR_EVENT_CHANNELS) {
85551bc2a6Smrj 		cmn_err(CE_WARN, "Binding invalid event channel: %d", evtchn);
86551bc2a6Smrj 		return;
87551bc2a6Smrj 	}
88551bc2a6Smrj 
89551bc2a6Smrj 	(void) ddi_intr_add_softint(xpv_dip, &hdl, pri, handler, (caddr_t)arg1);
90551bc2a6Smrj 	mutex_enter(&ec_lock);
91551bc2a6Smrj 	ASSERT(evtchn_to_handle[evtchn] == NULL);
92551bc2a6Smrj 	evtchn_to_handle[evtchn] = hdl;
93551bc2a6Smrj 	mutex_exit(&ec_lock);
94551bc2a6Smrj 
95551bc2a6Smrj 	/* Let the hypervisor know we're prepared to handle this event */
96551bc2a6Smrj 	hypervisor_unmask_event(evtchn);
97551bc2a6Smrj }
98551bc2a6Smrj 
99551bc2a6Smrj void
ec_unbind_evtchn(int evtchn)100551bc2a6Smrj ec_unbind_evtchn(int evtchn)
101551bc2a6Smrj {
102551bc2a6Smrj 	evtchn_close_t close;
103551bc2a6Smrj 	ddi_softint_handle_t hdl;
104551bc2a6Smrj 
105349b53ddSStuart Maybee 	if (evtchn < 0 || evtchn >= NR_EVENT_CHANNELS) {
106551bc2a6Smrj 		cmn_err(CE_WARN, "Unbinding invalid event channel: %d", evtchn);
107551bc2a6Smrj 		return;
108551bc2a6Smrj 	}
109551bc2a6Smrj 
110551bc2a6Smrj 	/*
111551bc2a6Smrj 	 * Let the hypervisor know we're no longer prepared to handle this
112551bc2a6Smrj 	 * event
113551bc2a6Smrj 	 */
114551bc2a6Smrj 	hypervisor_mask_event(evtchn);
115551bc2a6Smrj 
116551bc2a6Smrj 	/* Cleanup the event handler metadata */
117551bc2a6Smrj 	mutex_enter(&ec_lock);
118551bc2a6Smrj 	hdl = evtchn_to_handle[evtchn];
119551bc2a6Smrj 	evtchn_to_handle[evtchn] = NULL;
120551bc2a6Smrj 	mutex_exit(&ec_lock);
121551bc2a6Smrj 
122551bc2a6Smrj 	close.port = evtchn;
123551bc2a6Smrj 	(void) HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
124551bc2a6Smrj 	(void) ddi_intr_remove_softint(hdl);
125551bc2a6Smrj }
126551bc2a6Smrj 
127551bc2a6Smrj void
ec_notify_via_evtchn(unsigned int port)128551bc2a6Smrj ec_notify_via_evtchn(unsigned int port)
129551bc2a6Smrj {
130551bc2a6Smrj 	evtchn_send_t send;
131551bc2a6Smrj 
132551bc2a6Smrj 	if ((int)port == -1)
133551bc2a6Smrj 		return;
134551bc2a6Smrj 	send.port = port;
135551bc2a6Smrj 	(void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
136551bc2a6Smrj }
137551bc2a6Smrj 
138551bc2a6Smrj void
hypervisor_unmask_event(unsigned int ev)139551bc2a6Smrj hypervisor_unmask_event(unsigned int ev)
140551bc2a6Smrj {
141c50ea120Srab 	int index = ev >> EVTCHN_SHIFT;
142c50ea120Srab 	ulong_t bit = 1UL << (ev & ((1UL << EVTCHN_SHIFT) - 1));
143c50ea120Srab 	volatile ulong_t *maskp;
144551bc2a6Smrj 	evtchn_unmask_t unmask;
145551bc2a6Smrj 
146551bc2a6Smrj 	/*
147c50ea120Srab 	 * index,bit contain the event number as an index into the
148c50ea120Srab 	 * masked-events bitmask. Set it to 0.
149551bc2a6Smrj 	 */
150c50ea120Srab 	maskp = &masked_events[index];
151c50ea120Srab 	atomic_and_ulong(maskp, ~bit);
152551bc2a6Smrj 
153551bc2a6Smrj 	/* Let the hypervisor know the event has been unmasked */
154551bc2a6Smrj 	unmask.port = ev;
155551bc2a6Smrj 	if (HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask) != 0)
156551bc2a6Smrj 		panic("xen_evtchn_unmask() failed");
157551bc2a6Smrj }
158551bc2a6Smrj 
159551bc2a6Smrj /* Set a bit in an evtchan mask word */
160551bc2a6Smrj void
hypervisor_mask_event(uint_t ev)161551bc2a6Smrj hypervisor_mask_event(uint_t ev)
162551bc2a6Smrj {
163c50ea120Srab 	int index = ev >> EVTCHN_SHIFT;
164c50ea120Srab 	ulong_t bit = 1UL << (ev & ((1UL << EVTCHN_SHIFT) - 1));
165c50ea120Srab 	volatile ulong_t *maskp;
166551bc2a6Smrj 
167c50ea120Srab 	maskp = &masked_events[index];
168c50ea120Srab 	atomic_or_ulong(maskp, bit);
169551bc2a6Smrj }
170551bc2a6Smrj 
171551bc2a6Smrj void
hypervisor_clear_event(uint_t ev)172551bc2a6Smrj hypervisor_clear_event(uint_t ev)
173551bc2a6Smrj {
174c50ea120Srab 	int index = ev >> EVTCHN_SHIFT;
175c50ea120Srab 	ulong_t bit = 1UL << (ev & ((1UL << EVTCHN_SHIFT) - 1));
176c50ea120Srab 	volatile ulong_t *maskp;
177551bc2a6Smrj 
178c50ea120Srab 	maskp = &pending_events[index];
179c50ea120Srab 	atomic_and_ulong(maskp, ~bit);
180551bc2a6Smrj }
181551bc2a6Smrj 
182551bc2a6Smrj int
xen_alloc_unbound_evtchn(int domid,int * evtchnp)183551bc2a6Smrj xen_alloc_unbound_evtchn(int domid, int *evtchnp)
184551bc2a6Smrj {
185551bc2a6Smrj 	evtchn_alloc_unbound_t alloc;
186551bc2a6Smrj 	int err;
187551bc2a6Smrj 
188551bc2a6Smrj 	alloc.dom = DOMID_SELF;
189551bc2a6Smrj 	alloc.remote_dom = (domid_t)domid;
190551bc2a6Smrj 
191551bc2a6Smrj 	if ((err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
192551bc2a6Smrj 	    &alloc)) == 0) {
193551bc2a6Smrj 		*evtchnp = alloc.port;
194551bc2a6Smrj 		/* ensure evtchn is masked till we're ready to use it */
195551bc2a6Smrj 		(void) hypervisor_mask_event(*evtchnp);
196551bc2a6Smrj 	} else {
197551bc2a6Smrj 		err = xen_xlate_errcode(err);
198551bc2a6Smrj 	}
199551bc2a6Smrj 
200551bc2a6Smrj 	return (err);
201551bc2a6Smrj }
202551bc2a6Smrj 
203551bc2a6Smrj int
xen_bind_interdomain(int domid,int remote_port,int * port)204551bc2a6Smrj xen_bind_interdomain(int domid, int remote_port, int *port)
205551bc2a6Smrj {
206551bc2a6Smrj 	evtchn_bind_interdomain_t bind;
207551bc2a6Smrj 	int err;
208551bc2a6Smrj 
209551bc2a6Smrj 	bind.remote_dom = (domid_t)domid;
210551bc2a6Smrj 	bind.remote_port = remote_port;
211551bc2a6Smrj 	if ((err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
212551bc2a6Smrj 	    &bind)) == 0)
213551bc2a6Smrj 		*port = bind.local_port;
214551bc2a6Smrj 	else
215551bc2a6Smrj 		err = xen_xlate_errcode(err);
216551bc2a6Smrj 	return (err);
217551bc2a6Smrj }
218551bc2a6Smrj 
219551bc2a6Smrj /*ARGSUSED*/
220551bc2a6Smrj uint_t
evtchn_callback_fcn(caddr_t arg0,caddr_t arg1)221551bc2a6Smrj evtchn_callback_fcn(caddr_t arg0, caddr_t arg1)
222551bc2a6Smrj {
223c50ea120Srab 	ulong_t pending_word;
224551bc2a6Smrj 	int i, j, port;
225551bc2a6Smrj 	volatile struct vcpu_info *vci;
226551bc2a6Smrj 	uint_t rv = DDI_INTR_UNCLAIMED;
227551bc2a6Smrj 	ddi_softint_handle_t hdl;
228551bc2a6Smrj 	int low, high;
229c50ea120Srab 	ulong_t sels;
230551bc2a6Smrj 
231391647d5SJohn Levon 	/*
232391647d5SJohn Levon 	 * Xen hard-codes all notifications to VCPU0, so we bind
233391647d5SJohn Levon 	 * ourselves via xpv.conf.  Note that this also assumes that all
234391647d5SJohn Levon 	 * evtchns are bound to VCPU0, which is true by default.
235391647d5SJohn Levon 	 */
236391647d5SJohn Levon 	ASSERT(CPU->cpu_id == 0);
237391647d5SJohn Levon 
238391647d5SJohn Levon 	vci = &HYPERVISOR_shared_info->vcpu_info[0];
239551bc2a6Smrj 
240551bc2a6Smrj again:
241551bc2a6Smrj 	DTRACE_PROBE2(evtchn__scan__start, int, vci->evtchn_upcall_pending,
242551bc2a6Smrj 	    ulong_t, vci->evtchn_pending_sel);
243551bc2a6Smrj 
244551bc2a6Smrj 	atomic_and_8(&vci->evtchn_upcall_pending, 0);
245551bc2a6Smrj 
246551bc2a6Smrj 	/*
247551bc2a6Smrj 	 * Find the upper and lower bounds in which we need to search for
248551bc2a6Smrj 	 * pending events.
249551bc2a6Smrj 	 */
250c50ea120Srab 	GET_AND_CLEAR(&vci->evtchn_pending_sel, sels);
251551bc2a6Smrj 
252c50ea120Srab 	/* sels == 1 is by far the most common case.  Make it fast */
253c50ea120Srab 	if (sels == 1)
254c50ea120Srab 		low = high = 0;
255c50ea120Srab 	else if (sels == 0)
256c50ea120Srab 		return (rv);
257c50ea120Srab 	else
258c50ea120Srab 		GET_BOUNDS(sels, low, high);
259551bc2a6Smrj 
260551bc2a6Smrj 	/* Scan the port list, looking for words with bits set */
261551bc2a6Smrj 	for (i = low; i <= high; i++) {
262c50ea120Srab 		ulong_t tmp;
263551bc2a6Smrj 
264c50ea120Srab 		GET_AND_CLEAR(&pending_events[i], tmp);
265551bc2a6Smrj 		pending_word = tmp & ~(masked_events[i]);
266551bc2a6Smrj 
267551bc2a6Smrj 		/* Scan the bits in the word, looking for pending events */
268551bc2a6Smrj 		while (pending_word != 0) {
269c50ea120Srab 			j = lowbit(pending_word) - 1;
270551bc2a6Smrj 			port = (i << EVTCHN_SHIFT) + j;
271*dde8fce6SJeremy Jones 			pending_word = pending_word & ~(1UL << j);
272551bc2a6Smrj 
273551bc2a6Smrj 			/*
274551bc2a6Smrj 			 * If there is a handler registered for this event,
275551bc2a6Smrj 			 * schedule a softint of the appropriate priority
276551bc2a6Smrj 			 * to execute it.
277551bc2a6Smrj 			 */
278551bc2a6Smrj 			if ((hdl = evtchn_to_handle[port]) != NULL) {
279551bc2a6Smrj 				(void) ddi_intr_trigger_softint(hdl, NULL);
280551bc2a6Smrj 				rv = DDI_INTR_CLAIMED;
281551bc2a6Smrj 			}
282551bc2a6Smrj 		}
283551bc2a6Smrj 	}
284551bc2a6Smrj 	DTRACE_PROBE2(evtchn__scan__end, int, vci->evtchn_upcall_pending,
285551bc2a6Smrj 	    ulong_t, vci->evtchn_pending_sel);
286551bc2a6Smrj 
287551bc2a6Smrj 	if ((volatile uint8_t)vci->evtchn_upcall_pending ||
288c50ea120Srab 	    ((volatile ulong_t)vci->evtchn_pending_sel))
289551bc2a6Smrj 		goto again;
290551bc2a6Smrj 
291551bc2a6Smrj 	return (rv);
292551bc2a6Smrj }
293551bc2a6Smrj 
294551bc2a6Smrj static int
set_hvm_callback(int irq)295551bc2a6Smrj set_hvm_callback(int irq)
296551bc2a6Smrj {
297551bc2a6Smrj 	struct xen_hvm_param xhp;
298551bc2a6Smrj 
299551bc2a6Smrj 	xhp.domid = DOMID_SELF;
300551bc2a6Smrj 	xhp.index = HVM_PARAM_CALLBACK_IRQ;
301551bc2a6Smrj 	xhp.value = irq;
302551bc2a6Smrj 	return (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp));
303551bc2a6Smrj }
304551bc2a6Smrj 
305551bc2a6Smrj void
ec_fini()306551bc2a6Smrj ec_fini()
307551bc2a6Smrj {
308551bc2a6Smrj 	int i;
309551bc2a6Smrj 
310551bc2a6Smrj 	for (i = 0; i < NR_EVENT_CHANNELS; i++)
311551bc2a6Smrj 		ec_unbind_evtchn(i);
312551bc2a6Smrj 
313551bc2a6Smrj 	evtchn_callback_irq = -1;
314551bc2a6Smrj 	if (evtchn_ihp != NULL) {
315551bc2a6Smrj 		(void) ddi_intr_disable(*evtchn_ihp);
316551bc2a6Smrj 		(void) ddi_intr_remove_handler(*evtchn_ihp);
317551bc2a6Smrj 		(void) ddi_intr_free(*evtchn_ihp);
318551bc2a6Smrj 		kmem_free(evtchn_ihp, sizeof (ddi_intr_handle_t));
319551bc2a6Smrj 		evtchn_ihp = NULL;
320551bc2a6Smrj 	}
321551bc2a6Smrj }
322551bc2a6Smrj 
323551bc2a6Smrj int
ec_init(void)324349b53ddSStuart Maybee ec_init(void)
325551bc2a6Smrj {
326551bc2a6Smrj 	int i;
327551bc2a6Smrj 	int rv, actual;
328551bc2a6Smrj 	ddi_intr_handle_t *ihp;
329551bc2a6Smrj 
330551bc2a6Smrj 	/*
331551bc2a6Smrj 	 * Translate the variable-sized pending and masked event bitmasks
332551bc2a6Smrj 	 * into constant-sized arrays of uint32_t's.
333551bc2a6Smrj 	 */
334c50ea120Srab 	pending_events = &HYPERVISOR_shared_info->evtchn_pending[0];
335c50ea120Srab 	masked_events = &HYPERVISOR_shared_info->evtchn_mask[0];
336551bc2a6Smrj 
337551bc2a6Smrj 	/*
338551bc2a6Smrj 	 * Clear our event handler structures and prevent the hypervisor
339551bc2a6Smrj 	 * from triggering any events.
340551bc2a6Smrj 	 */
341551bc2a6Smrj 	mutex_init(&ec_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL7));
342551bc2a6Smrj 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
343551bc2a6Smrj 		evtchn_to_handle[i] = NULL;
344551bc2a6Smrj 		(void) hypervisor_mask_event(i);
345551bc2a6Smrj 	}
346551bc2a6Smrj 
347551bc2a6Smrj 	/*
348551bc2a6Smrj 	 * Allocate and initialize an interrupt handler to process the
349551bc2a6Smrj 	 * hypervisor's "hey you have events pending!" interrupt.
350551bc2a6Smrj 	 */
351551bc2a6Smrj 	ihp = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
352349b53ddSStuart Maybee 	rv = ddi_intr_alloc(xpv_dip, ihp, DDI_INTR_TYPE_FIXED, 0, 1, &actual,
353551bc2a6Smrj 	    DDI_INTR_ALLOC_NORMAL);
354551bc2a6Smrj 	if (rv < 0 || actual != 1) {
355551bc2a6Smrj 		cmn_err(CE_WARN, "Could not allocate evtchn interrupt: %d",
356551bc2a6Smrj 		    rv);
357551bc2a6Smrj 		return (-1);
358551bc2a6Smrj 	}
359551bc2a6Smrj 
360551bc2a6Smrj 	rv = ddi_intr_add_handler(*ihp, evtchn_callback_fcn, NULL, NULL);
361551bc2a6Smrj 	if (rv < 0) {
362551bc2a6Smrj 		(void) ddi_intr_free(*ihp);
363551bc2a6Smrj 		cmn_err(CE_WARN, "Could not attach evtchn handler");
364551bc2a6Smrj 		return (-1);
365551bc2a6Smrj 	}
366551bc2a6Smrj 	evtchn_ihp = ihp;
367551bc2a6Smrj 
368551bc2a6Smrj 	if (ddi_intr_enable(*ihp) != DDI_SUCCESS) {
369551bc2a6Smrj 		cmn_err(CE_WARN, "Could not enable evtchn interrupts\n");
370551bc2a6Smrj 		return (-1);
371551bc2a6Smrj 	}
372551bc2a6Smrj 
373551bc2a6Smrj 	/* Tell the hypervisor which interrupt we're waiting on. */
374551bc2a6Smrj 	evtchn_callback_irq = ((ddi_intr_handle_impl_t *)*ihp)->ih_vector;
375551bc2a6Smrj 
376551bc2a6Smrj 	if (set_hvm_callback(evtchn_callback_irq) != 0) {
377551bc2a6Smrj 		cmn_err(CE_WARN, "Couldn't register evtchn callback");
378551bc2a6Smrj 		return (-1);
379551bc2a6Smrj 	}
380551bc2a6Smrj 	return (0);
381551bc2a6Smrj }
382ea8190a2Ssmaybe 
383ea8190a2Ssmaybe void
ec_resume(void)384ea8190a2Ssmaybe ec_resume(void)
385ea8190a2Ssmaybe {
386ea8190a2Ssmaybe 	int i;
387ea8190a2Ssmaybe 
388ea8190a2Ssmaybe 	/* New event-channel space is not 'live' yet. */
389ea8190a2Ssmaybe 	for (i = 0; i < NR_EVENT_CHANNELS; i++)
390ea8190a2Ssmaybe 		(void) hypervisor_mask_event(i);
391ea8190a2Ssmaybe 	if (set_hvm_callback(evtchn_callback_irq) != 0)
392ea8190a2Ssmaybe 		cmn_err(CE_WARN, "Couldn't register evtchn callback");
393ea8190a2Ssmaybe 
394ea8190a2Ssmaybe }
395