1/* -*-  Mode:C; c-basic-offset:4; tab-width:4 -*-
2 ****************************************************************************
3 * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
4 * (C) 2005 - Grzegorz Milos - Intel Research Cambridge
5 ****************************************************************************
6 *
7 *        File: events.c
8 *      Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
9 *     Changes: Grzegorz Milos (gm281@cam.ac.uk)
10 *
11 *        Date: Jul 2003, changes Jun 2005
12 *
13 * Environment: Xen Minimal OS
14 * Description: Deals with events recieved on event channels
15 *
16 ****************************************************************************
17 */
18
19#include <mini-os/os.h>
20#include <mini-os/mm.h>
21#include <mini-os/hypervisor.h>
22#include <mini-os/events.h>
23#include <mini-os/lib.h>
24#include <mini-os/wait.h>
25
26#define NR_EVS 1024
27
28/* this represents a event handler. Chaining or sharing is not allowed */
29typedef struct _ev_action_t {
30    spinlock_t lock;
31    evtchn_handler_t handler;
32    void *data;
33    uint32_t count;
34} ev_action_t;
35
36static ev_action_t ev_actions[NR_EVS];
37void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data);
38
39static unsigned long bound_ports[NR_EVS/(8*sizeof(unsigned long))];
40
41static void (*rump_evtdev_callback)(u_int port);
42
43void minios_events_register_rump_callback(void (*cb)(u_int))
44{
45    BUG_ON(rump_evtdev_callback != NULL);
46    rump_evtdev_callback = cb;
47}
48
49/* interrupt handler queues events here */
50DECLARE_WAIT_QUEUE_HEAD(minios_events_waitq);
51static DEFINE_SPINLOCK(evt_handler_lock);
52void minios_evtdev_handler(evtchn_port_t port, struct pt_regs * regs,
53                           void *data)
54{
55    unsigned long flags;
56
57    minios_mask_evtchn(port);
58
59    local_irq_save(flags);
60    spin_lock(&evt_handler_lock);
61
62    rump_evtdev_callback(port);
63
64    spin_unlock(&evt_handler_lock);
65    local_irq_restore(flags);
66}
67
68void unbind_all_ports(void)
69{
70    int i;
71    int cpu = 0;
72    shared_info_t *s = HYPERVISOR_shared_info;
73    vcpu_info_t   *vcpu_info = &s->vcpu_info[cpu];
74    int rc;
75
76    for (i = 0; i < NR_EVS; i++)
77    {
78        if (i == start_info.console.domU.evtchn ||
79            i == start_info.store_evtchn)
80            continue;
81
82        if (test_and_clear_bit(i, bound_ports))
83        {
84            struct evtchn_close close;
85            minios_printk("port %d still bound!\n", i);
86            minios_mask_evtchn(i);
87
88            spin_lock(&ev_actions[i].lock);
89            ev_actions[i].handler = default_handler;
90            wmb();
91            ev_actions[i].data = NULL;
92            spin_unlock(&ev_actions[i].lock);
93
94            close.port = i;
95            rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
96            if (rc)
97                minios_printk("WARN: close_port %s failed rc=%d. ignored\n", i, rc);
98            minios_clear_evtchn(i);
99        }
100    }
101    vcpu_info->evtchn_upcall_pending = 0;
102    vcpu_info->evtchn_pending_sel = 0;
103}
104
105/*
106 * Demux events to different handlers.
107 */
108int do_event(evtchn_port_t port, struct pt_regs *regs)
109{
110    ev_action_t  *action;
111
112    minios_clear_evtchn(port);
113
114    if (port >= NR_EVS)
115    {
116        minios_printk("WARN: do_event(): Port number too large: %d\n", port);
117        return 1;
118    }
119
120    spin_lock(&ev_actions[port].lock);
121    action = &ev_actions[port];
122    action->count++;
123
124    /* call the handler */
125    action->handler(port, regs, action->data);
126    spin_unlock(&ev_actions[port].lock);
127
128    return 1;
129}
130
131evtchn_port_t minios_bind_evtchn(evtchn_port_t port, evtchn_handler_t handler,
132                                 void *data)
133{
134    spin_lock(&ev_actions[port].lock);
135    if (ev_actions[port].handler != default_handler)
136        minios_printk("WARN: Handler for port %d already registered, replacing\n",
137                      port);
138
139    ev_actions[port].data = data;
140    wmb();
141    ev_actions[port].handler = handler;
142    spin_unlock(&ev_actions[port].lock);
143
144    set_bit(port, bound_ports);
145
146    return port;
147}
148
149void minios_unbind_evtchn(evtchn_port_t port)
150{
151    struct evtchn_close close;
152    int rc;
153
154    spin_lock(&ev_actions[port].lock);
155    if (ev_actions[port].handler == default_handler)
156        minios_printk("WARN: No handler for port %d when unbinding\n", port);
157    minios_mask_evtchn(port);
158    minios_clear_evtchn(port);
159
160    ev_actions[port].handler = default_handler;
161    wmb();
162    ev_actions[port].data = NULL;
163    spin_unlock(&ev_actions[port].lock);
164
165    clear_bit(port, bound_ports);
166
167    close.port = port;
168    rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
169    if (rc)
170        minios_printk("WARN: close_port %s failed rc=%d. ignored\n", port, rc);
171}
172
173evtchn_port_t minios_bind_virq(uint32_t virq, evtchn_handler_t handler,
174                               void *data)
175{
176    evtchn_bind_virq_t op;
177    int rc;
178
179    /* Try to bind the virq to a port */
180    op.virq = virq;
181    op.vcpu = smp_processor_id();
182
183    if ((rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &op)) != 0)
184    {
185        minios_printk("Failed to bind virtual IRQ %d with rc=%d\n", virq, rc);
186        return -1;
187    }
188    minios_bind_evtchn(op.port, handler, data);
189    return op.port;
190}
191
192evtchn_port_t minios_bind_pirq(uint32_t pirq, int will_share,
193                               evtchn_handler_t handler, void *data)
194{
195    evtchn_bind_pirq_t op;
196    int rc;
197
198    /* Try to bind the pirq to a port */
199    op.pirq = pirq;
200    op.flags = will_share ? BIND_PIRQ__WILL_SHARE : 0;
201
202    if ((rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &op)) != 0)
203    {
204        minios_printk("Failed to bind physical IRQ %d with rc=%d\n", pirq, rc);
205        return -1;
206    }
207    minios_bind_evtchn(op.port, handler, data);
208    return op.port;
209}
210
211#if defined(__x86_64__)
212static char irqstack[2 * STACK_SIZE];
213
214static struct pda
215{
216    int irqcount;       /* offset 0 (used in x86_64.S) */
217    char *irqstackptr;  /*        8 */
218} cpu0_pda;
219#endif
220
221/* Just a simple wrapper for event channel hypercall. */
222int minios_event_channel_op(int cmd, void *op)
223{
224    return HYPERVISOR_event_channel_op(cmd, op);
225}
226
227/*
228 * Initially all events are without a handler and disabled
229 */
230void init_events(void)
231{
232    int i;
233#if defined(__x86_64__)
234    asm volatile("movl %0,%%fs ; movl %0,%%gs" :: "r" (0));
235    wrmsrl(0xc0000101, &cpu0_pda); /* 0xc0000101 is MSR_GS_BASE */
236    cpu0_pda.irqcount = -1;
237    cpu0_pda.irqstackptr = (void*) (((unsigned long)irqstack + 2 * STACK_SIZE)
238                                    & ~(STACK_SIZE - 1));
239#endif
240    /* initialize event handler */
241    for ( i = 0; i < NR_EVS; i++ )
242    {
243        ev_actions[i].handler = default_handler;
244        spin_lock_init(&ev_actions[i].lock);
245        minios_mask_evtchn(i);
246    }
247}
248
249void fini_events(void)
250{
251    /* Dealloc all events */
252    unbind_all_ports();
253#if defined(__x86_64__)
254    wrmsrl(0xc0000101, NULL); /* 0xc0000101 is MSR_GS_BASE */
255#endif
256}
257
258void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore)
259{
260    minios_printk("[Port %d] - event received\n", port);
261}
262
263/* Create a port available to the pal for exchanging notifications.
264   Returns the result of the hypervisor call. */
265
266/* Unfortunate confusion of terminology: the port is unbound as far
267   as Xen is concerned, but we automatically bind a handler to it
268   from inside mini-os. */
269
270int minios_evtchn_alloc_unbound(domid_t pal, evtchn_handler_t handler,
271                                void *data, evtchn_port_t *port)
272{
273    int rc;
274
275    evtchn_alloc_unbound_t op;
276    op.dom = DOMID_SELF;
277    op.remote_dom = pal;
278    rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op);
279    if (rc)
280    {
281        minios_printk("ERROR: alloc_unbound failed with rc=%d", rc);
282        return rc;
283    }
284    *port = minios_bind_evtchn(op.port, handler, data);
285    return rc;
286}
287
288/* Connect to a port so as to allow the exchange of notifications with
289   the pal. Returns the result of the hypervisor call. */
290
291int minios_evtchn_bind_interdomain(domid_t pal, evtchn_port_t remote_port,
292                                   evtchn_handler_t handler, void *data,
293                                   evtchn_port_t *local_port)
294{
295    int rc;
296    evtchn_port_t port;
297    evtchn_bind_interdomain_t op;
298    op.remote_dom = pal;
299    op.remote_port = remote_port;
300    rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, &op);
301    if (rc)
302    {
303        minios_printk("ERROR: bind_interdomain failed with rc=%d", rc);
304        return rc;
305    }
306    port = op.local_port;
307    *local_port = minios_bind_evtchn(port, handler, data);
308    return rc;
309}
310
311int minios_notify_remote_via_evtchn(evtchn_port_t port)
312{
313    evtchn_send_t op;
314    op.port = port;
315    return HYPERVISOR_event_channel_op(EVTCHNOP_send, &op);
316}
317
318/*
319 * Local variables:
320 * mode: C
321 * c-set-style: "BSD"
322 * c-basic-offset: 4
323 * tab-width: 4
324 * indent-tabs-mode: nil
325 * End:
326 */
327