1181624Skmacy/******************************************************************************
2181624Skmacy * evtchn.c
3181624Skmacy *
4181624Skmacy * Xenolinux driver for receiving and demuxing event-channel signals.
5181624Skmacy *
6181624Skmacy * Copyright (c) 2004, K A Fraser
7181624Skmacy */
8181624Skmacy
9181624Skmacy#include <sys/cdefs.h>
10181624Skmacy__FBSDID("$FreeBSD$");
11181624Skmacy
12181624Skmacy#include <sys/param.h>
13181624Skmacy#include <sys/systm.h>
14181624Skmacy#include <sys/uio.h>
15181624Skmacy#include <sys/bus.h>
16181624Skmacy#include <sys/malloc.h>
17181624Skmacy#include <sys/kernel.h>
18181624Skmacy#include <sys/lock.h>
19181624Skmacy#include <sys/mutex.h>
20181624Skmacy#include <sys/selinfo.h>
21181624Skmacy#include <sys/poll.h>
22181624Skmacy#include <sys/conf.h>
23181624Skmacy#include <sys/fcntl.h>
24181624Skmacy#include <sys/ioccom.h>
25255040Sgibbs#include <sys/rman.h>
26181624Skmacy
27255040Sgibbs#include <xen/xen-os.h>
28255040Sgibbs#include <xen/evtchn.h>
29186557Skmacy#include <xen/xen_intr.h>
30255040Sgibbs
31181624Skmacy#include <machine/bus.h>
32181624Skmacy#include <machine/resource.h>
33181624Skmacy#include <machine/xen/synch_bitops.h>
34181624Skmacy
35255040Sgibbs#include <xen/evtchn/evtchnvar.h>
36181624Skmacy
37181624Skmacytypedef struct evtchn_sotfc {
38181624Skmacy
39181624Skmacy	struct selinfo  ev_rsel;
40181624Skmacy} evtchn_softc_t;
41181624Skmacy
42181624Skmacy/* Only one process may open /dev/xen/evtchn at any time. */
43181624Skmacystatic unsigned long evtchn_dev_inuse;
44181624Skmacy
45181624Skmacy/* Notification ring, accessed via /dev/xen/evtchn. */
46181624Skmacy
47181624Skmacy#define EVTCHN_RING_SIZE     2048  /* 2048 16-bit entries */
48181624Skmacy
49181624Skmacy#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
50181624Skmacystatic uint16_t *ring;
51181624Skmacystatic unsigned int ring_cons, ring_prod, ring_overflow;
52181624Skmacy
53181624Skmacy/* Which ports is user-space bound to? */
54181624Skmacystatic uint32_t bound_ports[32];
55181624Skmacy
56181624Skmacy/* Unique address for processes to sleep on */
57181624Skmacystatic void *evtchn_waddr = &ring;
58181624Skmacy
59181624Skmacystatic struct mtx lock, upcall_lock;
60181624Skmacy
61181624Skmacystatic d_read_t      evtchn_read;
62181624Skmacystatic d_write_t     evtchn_write;
63181624Skmacystatic d_ioctl_t     evtchn_ioctl;
64181624Skmacystatic d_poll_t      evtchn_poll;
65181624Skmacystatic d_open_t      evtchn_open;
66181624Skmacystatic d_close_t     evtchn_close;
67181624Skmacy
68181624Skmacy
69181624Skmacyvoid
70255040Sgibbsevtchn_device_upcall(evtchn_port_t port)
71181624Skmacy{
72181624Skmacy	mtx_lock(&upcall_lock);
73181624Skmacy
74255040Sgibbs	evtchn_mask_port(port);
75255040Sgibbs	evtchn_clear_port(port);
76181624Skmacy
77181624Skmacy	if ( ring != NULL ) {
78181624Skmacy		if ( (ring_prod - ring_cons) < EVTCHN_RING_SIZE ) {
79181624Skmacy			ring[EVTCHN_RING_MASK(ring_prod)] = (uint16_t)port;
80181624Skmacy			if ( ring_cons == ring_prod++ ) {
81181624Skmacy				wakeup(evtchn_waddr);
82181624Skmacy			}
83181624Skmacy		}
84181624Skmacy		else {
85181624Skmacy			ring_overflow = 1;
86181624Skmacy		}
87181624Skmacy	}
88181624Skmacy
89181624Skmacy	mtx_unlock(&upcall_lock);
90181624Skmacy}
91181624Skmacy
92181624Skmacystatic void
93181624Skmacy__evtchn_reset_buffer_ring(void)
94181624Skmacy{
95181624Skmacy	/* Initialise the ring to empty. Clear errors. */
96181624Skmacy	ring_cons = ring_prod = ring_overflow = 0;
97181624Skmacy}
98181624Skmacy
99181624Skmacystatic int
100181624Skmacyevtchn_read(struct cdev *dev, struct uio *uio, int ioflag)
101181624Skmacy{
102181624Skmacy	int rc;
103181624Skmacy	unsigned int count, c, p, sst = 0, bytes1 = 0, bytes2 = 0;
104181624Skmacy	count = uio->uio_resid;
105181624Skmacy
106181624Skmacy	count &= ~1; /* even number of bytes */
107181624Skmacy
108181624Skmacy	if ( count == 0 )
109181624Skmacy	{
110181624Skmacy		rc = 0;
111181624Skmacy		goto out;
112181624Skmacy	}
113181624Skmacy
114181624Skmacy	if ( count > PAGE_SIZE )
115181624Skmacy		count = PAGE_SIZE;
116181624Skmacy
117181624Skmacy	for ( ; ; ) {
118181624Skmacy		if ( (c = ring_cons) != (p = ring_prod) )
119181624Skmacy			break;
120181624Skmacy
121181624Skmacy		if ( ring_overflow ) {
122181624Skmacy			rc = EFBIG;
123181624Skmacy			goto out;
124181624Skmacy		}
125181624Skmacy
126181624Skmacy		if (sst != 0) {
127181624Skmacy			rc = EINTR;
128181624Skmacy			goto out;
129181624Skmacy		}
130181624Skmacy
131181624Skmacy		/* PCATCH == check for signals before and after sleeping
132181624Skmacy		 * PWAIT == priority of waiting on resource
133181624Skmacy		 */
134181624Skmacy		sst = tsleep(evtchn_waddr, PWAIT|PCATCH, "evchwt", 10);
135181624Skmacy	}
136181624Skmacy
137181624Skmacy	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
138181624Skmacy	if ( ((c ^ p) & EVTCHN_RING_SIZE) != 0 ) {
139181624Skmacy		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) * sizeof(uint16_t);
140181624Skmacy		bytes2 = EVTCHN_RING_MASK(p) * sizeof(uint16_t);
141181624Skmacy	}
142181624Skmacy	else {
143181624Skmacy		bytes1 = (p - c) * sizeof(uint16_t);
144181624Skmacy		bytes2 = 0;
145181624Skmacy	}
146181624Skmacy
147181624Skmacy	/* Truncate chunks according to caller's maximum byte count. */
148181624Skmacy	if ( bytes1 > count ) {
149181624Skmacy		bytes1 = count;
150181624Skmacy		bytes2 = 0;
151181624Skmacy	}
152181624Skmacy	else if ( (bytes1 + bytes2) > count ) {
153181624Skmacy		bytes2 = count - bytes1;
154181624Skmacy	}
155181624Skmacy
156181624Skmacy	if ( uiomove(&ring[EVTCHN_RING_MASK(c)], bytes1, uio) ||
157181624Skmacy	     ((bytes2 != 0) && uiomove(&ring[0], bytes2, uio)))
158181624Skmacy		/* keeping this around as its replacement is not equivalent
159181624Skmacy		 * copyout(&ring[0], &buf[bytes1], bytes2)
160181624Skmacy		 */
161181624Skmacy	{
162181624Skmacy		rc = EFAULT;
163181624Skmacy		goto out;
164181624Skmacy	}
165181624Skmacy
166181624Skmacy	ring_cons += (bytes1 + bytes2) / sizeof(uint16_t);
167181624Skmacy
168181624Skmacy	rc = bytes1 + bytes2;
169181624Skmacy
170181624Skmacy out:
171181624Skmacy
172181624Skmacy	return rc;
173181624Skmacy}
174181624Skmacy
175181624Skmacystatic int
176181624Skmacyevtchn_write(struct cdev *dev, struct uio *uio, int ioflag)
177181624Skmacy{
178181624Skmacy	int  rc, i, count;
179181624Skmacy
180181624Skmacy	count = uio->uio_resid;
181181624Skmacy
182181624Skmacy	uint16_t *kbuf = (uint16_t *)malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
183181624Skmacy
184181624Skmacy
185181624Skmacy	if ( kbuf == NULL )
186181624Skmacy		return ENOMEM;
187181624Skmacy
188181624Skmacy	count &= ~1; /* even number of bytes */
189181624Skmacy
190181624Skmacy	if ( count == 0 ) {
191181624Skmacy		rc = 0;
192181624Skmacy		goto out;
193181624Skmacy	}
194181624Skmacy
195181624Skmacy	if ( count > PAGE_SIZE )
196181624Skmacy		count = PAGE_SIZE;
197181624Skmacy
198181624Skmacy	if ( uiomove(kbuf, count, uio) != 0 ) {
199181624Skmacy		rc = EFAULT;
200181624Skmacy		goto out;
201181624Skmacy	}
202181624Skmacy
203181624Skmacy	mtx_lock_spin(&lock);
204181624Skmacy	for ( i = 0; i < (count/2); i++ )
205181624Skmacy		if ( test_bit(kbuf[i], &bound_ports[0]) )
206255040Sgibbs			evtchn_unmask_port(kbuf[i]);
207181624Skmacy	mtx_unlock_spin(&lock);
208181624Skmacy
209181624Skmacy	rc = count;
210181624Skmacy
211181624Skmacy out:
212181624Skmacy	free(kbuf, M_DEVBUF);
213181624Skmacy	return rc;
214181624Skmacy}
215181624Skmacy
216181624Skmacystatic int
217181624Skmacyevtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
218181624Skmacy	     int mode, struct thread *td __unused)
219181624Skmacy{
220181624Skmacy	int rc = 0;
221181624Skmacy
222255040Sgibbs#ifdef NOTYET
223181624Skmacy	mtx_lock_spin(&lock);
224181624Skmacy
225181624Skmacy	switch ( cmd )
226181624Skmacy	{
227181624Skmacy	case EVTCHN_RESET:
228181624Skmacy		__evtchn_reset_buffer_ring();
229181624Skmacy		break;
230181624Skmacy	case EVTCHN_BIND:
231189699Sdfr		if ( !synch_test_and_set_bit((uintptr_t)arg, &bound_ports[0]) )
232189699Sdfr			unmask_evtchn((uintptr_t)arg);
233181624Skmacy		else
234181624Skmacy			rc = EINVAL;
235181624Skmacy		break;
236181624Skmacy	case EVTCHN_UNBIND:
237189699Sdfr		if ( synch_test_and_clear_bit((uintptr_t)arg, &bound_ports[0]) )
238189699Sdfr			mask_evtchn((uintptr_t)arg);
239181624Skmacy		else
240181624Skmacy			rc = EINVAL;
241181624Skmacy		break;
242181624Skmacy	default:
243181624Skmacy		rc = ENOSYS;
244181624Skmacy		break;
245181624Skmacy	}
246181624Skmacy
247181624Skmacy	mtx_unlock_spin(&lock);
248255040Sgibbs#endif
249181624Skmacy
250181624Skmacy	return rc;
251181624Skmacy}
252181624Skmacy
253181624Skmacystatic int
254181624Skmacyevtchn_poll(struct cdev *dev, int poll_events, struct thread *td)
255181624Skmacy{
256181624Skmacy
257181624Skmacy	evtchn_softc_t *sc;
258181624Skmacy	unsigned int mask = POLLOUT | POLLWRNORM;
259181624Skmacy
260181624Skmacy	sc = dev->si_drv1;
261181624Skmacy
262181624Skmacy	if ( ring_cons != ring_prod )
263181624Skmacy		mask |= POLLIN | POLLRDNORM;
264181624Skmacy	else if ( ring_overflow )
265181624Skmacy		mask = POLLERR;
266181624Skmacy	else
267181624Skmacy		selrecord(td, &sc->ev_rsel);
268181624Skmacy
269181624Skmacy
270181624Skmacy	return mask;
271181624Skmacy}
272181624Skmacy
273181624Skmacy
274181624Skmacystatic int
275181624Skmacyevtchn_open(struct cdev *dev, int flag, int otyp, struct thread *td)
276181624Skmacy{
277181624Skmacy	uint16_t *_ring;
278181624Skmacy
279181624Skmacy	if (flag & O_NONBLOCK)
280181624Skmacy		return EBUSY;
281181624Skmacy
282181624Skmacy	if ( synch_test_and_set_bit(0, &evtchn_dev_inuse) )
283181624Skmacy		return EBUSY;
284181624Skmacy
285181624Skmacy	if ( (_ring = (uint16_t *)malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK)) == NULL )
286181624Skmacy		return ENOMEM;
287181624Skmacy
288181624Skmacy	mtx_lock_spin(&lock);
289181624Skmacy	ring = _ring;
290181624Skmacy	__evtchn_reset_buffer_ring();
291181624Skmacy	mtx_unlock_spin(&lock);
292181624Skmacy
293181624Skmacy
294181624Skmacy	return 0;
295181624Skmacy}
296181624Skmacy
297181624Skmacystatic int
298181624Skmacyevtchn_close(struct cdev *dev, int flag, int otyp, struct thread *td __unused)
299181624Skmacy{
300181624Skmacy	int i;
301181624Skmacy
302181624Skmacy	if (ring != NULL) {
303181624Skmacy		free(ring, M_DEVBUF);
304181624Skmacy		ring = NULL;
305181624Skmacy	}
306204159Skmacy	mtx_lock_spin(&lock);
307181624Skmacy	for ( i = 0; i < NR_EVENT_CHANNELS; i++ )
308181624Skmacy		if ( synch_test_and_clear_bit(i, &bound_ports[0]) )
309255040Sgibbs			evtchn_mask_port(i);
310181624Skmacy	mtx_unlock_spin(&lock);
311181624Skmacy
312181624Skmacy	evtchn_dev_inuse = 0;
313181624Skmacy
314181624Skmacy	return 0;
315181624Skmacy}
316181624Skmacy
317181624Skmacystatic struct cdevsw evtchn_devsw = {
318228162Sjhb	.d_version =	D_VERSION,
319228162Sjhb	.d_open =	evtchn_open,
320228162Sjhb	.d_close =	evtchn_close,
321228162Sjhb	.d_read =	evtchn_read,
322228162Sjhb	.d_write =	evtchn_write,
323228162Sjhb	.d_ioctl =	evtchn_ioctl,
324228162Sjhb	.d_poll =	evtchn_poll,
325228162Sjhb	.d_name =	"evtchn",
326181624Skmacy};
327181624Skmacy
328181624Skmacy
329181624Skmacy/* XXX  - if this device is ever supposed to support use by more than one process
330181624Skmacy * this global static will have to go away
331181624Skmacy */
332181624Skmacystatic struct cdev *evtchn_dev;
333181624Skmacy
334181624Skmacy
335181624Skmacy
336181624Skmacystatic int
337181865Skmacyevtchn_dev_init(void *dummy __unused)
338181624Skmacy{
339181624Skmacy	/* XXX I believe we don't need these leaving them here for now until we
340181624Skmacy	 * have some semblance of it working
341181624Skmacy	 */
342181624Skmacy	mtx_init(&upcall_lock, "evtchup", NULL, MTX_DEF);
343181624Skmacy
344181624Skmacy	/* (DEVFS) create '/dev/misc/evtchn'. */
345181624Skmacy	evtchn_dev = make_dev(&evtchn_devsw, 0, UID_ROOT, GID_WHEEL, 0600, "xen/evtchn");
346181624Skmacy
347181624Skmacy	mtx_init(&lock, "evch", NULL, MTX_SPIN | MTX_NOWITNESS);
348181624Skmacy
349181624Skmacy	evtchn_dev->si_drv1 = malloc(sizeof(evtchn_softc_t), M_DEVBUF, M_WAITOK);
350181624Skmacy	bzero(evtchn_dev->si_drv1, sizeof(evtchn_softc_t));
351181624Skmacy
352189699Sdfr	if (bootverbose)
353189699Sdfr		printf("Event-channel device installed.\n");
354181624Skmacy
355181624Skmacy	return 0;
356181624Skmacy}
357181624Skmacy
358181865SkmacySYSINIT(evtchn_dev_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, evtchn_dev_init, NULL);
359