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 <os.h> |
---|
20 | #include <mm.h> |
---|
21 | #include <hypervisor.h> |
---|
22 | #include <events.h> |
---|
23 | #include <lib.h> |
---|
24 | |
---|
25 | #define NR_EVS 1024 |
---|
26 | |
---|
27 | /* this represents a event handler. Chaining or sharing is not allowed */ |
---|
28 | typedef struct _ev_action_t { |
---|
29 | evtchn_handler_t handler; |
---|
30 | void *data; |
---|
31 | u32 count; |
---|
32 | } ev_action_t; |
---|
33 | |
---|
34 | static ev_action_t ev_actions[NR_EVS]; |
---|
35 | void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data); |
---|
36 | |
---|
37 | static unsigned long bound_ports[NR_EVS/(8*sizeof(unsigned long))]; |
---|
38 | |
---|
39 | void unbind_all_ports(void) |
---|
40 | { |
---|
41 | int i; |
---|
42 | |
---|
43 | for (i = 0; i < NR_EVS; i++) |
---|
44 | { |
---|
45 | if (test_and_clear_bit(i, bound_ports)) |
---|
46 | { |
---|
47 | struct evtchn_close close; |
---|
48 | mask_evtchn(i); |
---|
49 | close.port = i; |
---|
50 | HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); |
---|
51 | } |
---|
52 | } |
---|
53 | } |
---|
54 | |
---|
55 | /* |
---|
56 | * Demux events to different handlers. |
---|
57 | */ |
---|
58 | int do_event(evtchn_port_t port, struct pt_regs *regs) |
---|
59 | { |
---|
60 | ev_action_t *action; |
---|
61 | if (port >= NR_EVS) { |
---|
62 | printk("Port number too large: %d\n", port); |
---|
63 | goto out; |
---|
64 | } |
---|
65 | |
---|
66 | action = &ev_actions[port]; |
---|
67 | action->count++; |
---|
68 | |
---|
69 | /* call the handler */ |
---|
70 | action->handler(port, regs, action->data); |
---|
71 | |
---|
72 | out: |
---|
73 | clear_evtchn(port); |
---|
74 | |
---|
75 | return 1; |
---|
76 | |
---|
77 | } |
---|
78 | |
---|
79 | evtchn_port_t bind_evtchn(evtchn_port_t port, evtchn_handler_t handler, |
---|
80 | void *data) |
---|
81 | { |
---|
82 | if(ev_actions[port].handler != default_handler) |
---|
83 | printk("WARN: Handler for port %d already registered, replacing\n", |
---|
84 | port); |
---|
85 | |
---|
86 | ev_actions[port].data = data; |
---|
87 | wmb(); |
---|
88 | ev_actions[port].handler = handler; |
---|
89 | |
---|
90 | /* Finally unmask the port */ |
---|
91 | unmask_evtchn(port); |
---|
92 | |
---|
93 | return port; |
---|
94 | } |
---|
95 | |
---|
96 | void unbind_evtchn(evtchn_port_t port ) |
---|
97 | { |
---|
98 | if (ev_actions[port].handler == default_handler) |
---|
99 | printk("WARN: No handler for port %d when unbinding\n", port); |
---|
100 | ev_actions[port].handler = default_handler; |
---|
101 | wmb(); |
---|
102 | ev_actions[port].data = NULL; |
---|
103 | } |
---|
104 | |
---|
105 | int bind_virq(uint32_t virq, evtchn_handler_t handler, void *data) |
---|
106 | { |
---|
107 | evtchn_bind_virq_t op; |
---|
108 | |
---|
109 | /* Try to bind the virq to a port */ |
---|
110 | op.virq = virq; |
---|
111 | op.vcpu = smp_processor_id(); |
---|
112 | |
---|
113 | if ( HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &op) != 0 ) |
---|
114 | { |
---|
115 | printk("Failed to bind virtual IRQ %d\n", virq); |
---|
116 | return 1; |
---|
117 | } |
---|
118 | set_bit(op.port,bound_ports); |
---|
119 | bind_evtchn(op.port, handler, data); |
---|
120 | return 0; |
---|
121 | } |
---|
122 | |
---|
123 | #if defined(__x86_64__) |
---|
124 | /* Allocate 4 pages for the irqstack */ |
---|
125 | #define STACK_PAGES 4 |
---|
126 | char irqstack[1024 * 4 * STACK_PAGES]; |
---|
127 | |
---|
128 | static struct pda |
---|
129 | { |
---|
130 | int irqcount; /* offset 0 (used in x86_64.S) */ |
---|
131 | char *irqstackptr; /* 8 */ |
---|
132 | } cpu0_pda; |
---|
133 | #endif |
---|
134 | |
---|
135 | /* |
---|
136 | * Initially all events are without a handler and disabled |
---|
137 | */ |
---|
138 | void init_events(void) |
---|
139 | { |
---|
140 | int i; |
---|
141 | #if defined(__x86_64__) |
---|
142 | asm volatile("movl %0,%%fs ; movl %0,%%gs" :: "r" (0)); |
---|
143 | wrmsrl(0xc0000101, &cpu0_pda); /* 0xc0000101 is MSR_GS_BASE */ |
---|
144 | cpu0_pda.irqcount = -1; |
---|
145 | cpu0_pda.irqstackptr = irqstack + 1024 * 4 * STACK_PAGES; |
---|
146 | #endif |
---|
147 | /* inintialise event handler */ |
---|
148 | for ( i = 0; i < NR_EVS; i++ ) |
---|
149 | { |
---|
150 | ev_actions[i].handler = default_handler; |
---|
151 | mask_evtchn(i); |
---|
152 | } |
---|
153 | } |
---|
154 | |
---|
155 | void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore) |
---|
156 | { |
---|
157 | printk("[Port %d] - event received\n", port); |
---|
158 | } |
---|
159 | |
---|
160 | /* Create a port available to the pal for exchanging notifications. |
---|
161 | Returns the result of the hypervisor call. */ |
---|
162 | |
---|
163 | /* Unfortunate confusion of terminology: the port is unbound as far |
---|
164 | as Xen is concerned, but we automatically bind a handler to it |
---|
165 | from inside mini-os. */ |
---|
166 | |
---|
167 | int evtchn_alloc_unbound(domid_t pal, evtchn_handler_t handler, |
---|
168 | void *data, evtchn_port_t *port) |
---|
169 | { |
---|
170 | evtchn_alloc_unbound_t op; |
---|
171 | op.dom = DOMID_SELF; |
---|
172 | op.remote_dom = pal; |
---|
173 | int err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); |
---|
174 | if (err) |
---|
175 | return err; |
---|
176 | *port = bind_evtchn(op.port, handler, data); |
---|
177 | return err; |
---|
178 | } |
---|
179 | |
---|
180 | /* Connect to a port so as to allow the exchange of notifications with |
---|
181 | the pal. Returns the result of the hypervisor call. */ |
---|
182 | |
---|
183 | int evtchn_bind_interdomain(domid_t pal, evtchn_port_t remote_port, |
---|
184 | evtchn_handler_t handler, void *data, |
---|
185 | evtchn_port_t *local_port) |
---|
186 | { |
---|
187 | evtchn_bind_interdomain_t op; |
---|
188 | op.remote_dom = pal; |
---|
189 | op.remote_port = remote_port; |
---|
190 | int err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, &op); |
---|
191 | if (err) |
---|
192 | return err; |
---|
193 | set_bit(op.local_port,bound_ports); |
---|
194 | evtchn_port_t port = op.local_port; |
---|
195 | clear_evtchn(port); /* Without, handler gets invoked now! */ |
---|
196 | *local_port = bind_evtchn(port, handler, data); |
---|
197 | return err; |
---|
198 | } |
---|