1 /******************************************************************************
2 *
3 * Copyright 2007-2008 Samuel Thibault <samuel.thibault@eu.citrix.com>.
4 * All rights reserved.
5 * Use is subject to license terms.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation;
10 * version 2.1 of the License.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Split off from xc_minios.c
21 */
22
23 #include "xen_list.h"
24 #include <mini-os/types.h>
25 #include <mini-os/os.h>
26 #include <mini-os/lib.h>
27 #include <mini-os/events.h>
28 #include <mini-os/wait.h>
29
30 #include <assert.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <unistd.h>
36 #include <inttypes.h>
37 #include <malloc.h>
38
39 #include "private.h"
40
41 XEN_LIST_HEAD(port_list, struct port_info);
42
43 struct ports {
44 struct port_list list;
45 };
46
47 struct port_info {
48 XEN_LIST_ENTRY(struct port_info) list;
49 evtchn_port_t port;
50 bool pending;
51 bool bound;
52 };
53
port_alloc(xenevtchn_handle * xce)54 static struct port_info *port_alloc(xenevtchn_handle *xce)
55 {
56 struct port_info *port_info;
57 struct file *file = get_file_from_fd(xce->fd);
58 struct ports *ports = file->dev;
59 unsigned long flags;
60
61 port_info = malloc(sizeof(struct port_info));
62 if ( port_info == NULL )
63 return NULL;
64
65 port_info->pending = false;
66 port_info->port = -1;
67 port_info->bound = false;
68
69 local_irq_save(flags);
70 XEN_LIST_INSERT_HEAD(&ports->list, port_info, list);
71 local_irq_restore(flags);
72
73 return port_info;
74 }
75
port_dealloc(struct port_info * port_info)76 static void port_dealloc(struct port_info *port_info)
77 {
78 unsigned long flags;
79
80 if ( port_info->bound )
81 unbind_evtchn(port_info->port);
82
83 local_irq_save(flags);
84 XEN_LIST_REMOVE(port_info, list);
85 local_irq_restore(flags);
86
87 free(port_info);
88 }
89
evtchn_close_fd(struct file * file)90 static int evtchn_close_fd(struct file *file)
91 {
92 struct port_info *port_info, *tmp;
93 struct ports *ports = file->dev;
94
95 XEN_LIST_FOREACH_SAFE(port_info, &ports->list, list, tmp)
96 port_dealloc(port_info);
97
98 free(ports);
99
100 return 0;
101 }
102
103 static const struct file_ops evtchn_ops = {
104 .name = "evtchn",
105 .close = evtchn_close_fd,
106 .select_rd = select_read_flag,
107 };
108
109 static unsigned int ftype_evtchn;
110
111 __attribute__((constructor))
evtchn_initialize(void)112 static void evtchn_initialize(void)
113 {
114 ftype_evtchn = alloc_file_type(&evtchn_ops);
115 }
116
osdep_evtchn_open(xenevtchn_handle * xce,unsigned int flags)117 int osdep_evtchn_open(xenevtchn_handle *xce, unsigned int flags)
118 {
119 int fd;
120 struct file *file;
121 struct ports *ports;
122
123 ports = malloc(sizeof(*ports));
124 if ( !ports )
125 return -1;
126
127 fd = alloc_fd(ftype_evtchn);
128 file = get_file_from_fd(fd);
129
130 if ( !file )
131 {
132 free(ports);
133 return -1;
134 }
135
136 if ( !(flags & XENEVTCHN_NO_CLOEXEC) )
137 file->cloexec = true;
138 file->dev = ports;
139 XEN_LIST_INIT(&ports->list);
140 xce->fd = fd;
141 printf("evtchn_open() -> %d\n", fd);
142
143 return 0;
144 }
145
osdep_evtchn_close(xenevtchn_handle * xce)146 int osdep_evtchn_close(xenevtchn_handle *xce)
147 {
148 if ( xce->fd == -1 )
149 return 0;
150
151 return close(xce->fd);
152 }
153
osdep_evtchn_restrict(xenevtchn_handle * xce,domid_t domid)154 int osdep_evtchn_restrict(xenevtchn_handle *xce, domid_t domid)
155 {
156 errno = EOPNOTSUPP;
157
158 return -1;
159 }
160
xenevtchn_notify(xenevtchn_handle * xce,evtchn_port_t port)161 int xenevtchn_notify(xenevtchn_handle *xce, evtchn_port_t port)
162 {
163 int ret;
164
165 ret = notify_remote_via_evtchn(port);
166
167 if ( ret < 0 )
168 {
169 errno = -ret;
170 ret = -1;
171 }
172
173 return ret;
174 }
175
evtchn_handler(evtchn_port_t port,struct pt_regs * regs,void * data)176 static void evtchn_handler(evtchn_port_t port, struct pt_regs *regs, void *data)
177 {
178 xenevtchn_handle *xce = data;
179 struct file *file = get_file_from_fd(xce->fd);
180 struct port_info *port_info;
181 struct ports *ports;
182
183 assert(file);
184 ports = file->dev;
185 mask_evtchn(port);
186
187 XEN_LIST_FOREACH(port_info, &ports->list, list)
188 {
189 if ( port_info->port == port )
190 goto found;
191 }
192
193 printk("Unknown port %d for handle %d\n", port, xce->fd);
194 return;
195
196 found:
197 port_info->pending = true;
198 file->read = true;
199 wake_up(&event_queue);
200 }
201
xenevtchn_bind_unbound_port(xenevtchn_handle * xce,uint32_t domid)202 xenevtchn_port_or_error_t xenevtchn_bind_unbound_port(xenevtchn_handle *xce,
203 uint32_t domid)
204 {
205 struct port_info *port_info;
206 int ret;
207 evtchn_port_t port;
208
209 port_info = port_alloc(xce);
210 if ( port_info == NULL )
211 return -1;
212
213 printf("xenevtchn_bind_unbound_port(%d)", domid);
214 ret = evtchn_alloc_unbound(domid, evtchn_handler, xce, &port);
215 printf(" = %d\n", ret);
216
217 if ( ret < 0 )
218 {
219 port_dealloc(port_info);
220 errno = -ret;
221 return -1;
222 }
223
224 port_info->bound = true;
225 port_info->port = port;
226 unmask_evtchn(port);
227
228 return port;
229 }
230
xenevtchn_bind_interdomain(xenevtchn_handle * xce,uint32_t domid,evtchn_port_t remote_port)231 xenevtchn_port_or_error_t xenevtchn_bind_interdomain(xenevtchn_handle *xce,
232 uint32_t domid,
233 evtchn_port_t remote_port)
234 {
235 struct port_info *port_info;
236 evtchn_port_t local_port;
237 int ret;
238
239 port_info = port_alloc(xce);
240 if ( port_info == NULL )
241 return -1;
242
243 printf("xenevtchn_bind_interdomain(%d, %"PRId32")", domid, remote_port);
244 ret = evtchn_bind_interdomain(domid, remote_port, evtchn_handler,
245 xce, &local_port);
246 printf(" = %d\n", ret);
247
248 if ( ret < 0 )
249 {
250 port_dealloc(port_info);
251 errno = -ret;
252 return -1;
253 }
254
255 port_info->bound = true;
256 port_info->port = local_port;
257 unmask_evtchn(local_port);
258
259 return local_port;
260 }
261
xenevtchn_bind(xenevtchn_handle * xce,evtchn_port_t port)262 int xenevtchn_bind(xenevtchn_handle *xce, evtchn_port_t port)
263 {
264 struct port_info *port_info;
265 port_info = port_alloc(xce);
266 if ( port_info == NULL )
267 return -1;
268
269 printf("xenevtchn_bind(%"PRId32")\n", port);
270 bind_evtchn(port, evtchn_handler, xce);
271
272 port_info->bound = true;
273 port_info->port = port;
274 unmask_evtchn(port);
275
276 return 0;
277 }
278
xenevtchn_unbind(xenevtchn_handle * xce,evtchn_port_t port)279 int xenevtchn_unbind(xenevtchn_handle *xce, evtchn_port_t port)
280 {
281 int fd = xce->fd;
282 struct file *file = get_file_from_fd(fd);
283 struct port_info *port_info;
284 struct ports *ports = file->dev;
285 unsigned long flags;
286
287 local_irq_save(flags);
288 XEN_LIST_FOREACH(port_info, &ports->list, list)
289 {
290 if ( port_info->port == port )
291 {
292 port_dealloc(port_info);
293 local_irq_restore(flags);
294 return 0;
295 }
296 }
297 local_irq_restore(flags);
298
299 printf("Warning: couldn't find port %"PRId32" for xc handle %x\n",
300 port, fd);
301 errno = EINVAL;
302
303 return -1;
304 }
305
xenevtchn_bind_virq(xenevtchn_handle * xce,unsigned int virq)306 xenevtchn_port_or_error_t xenevtchn_bind_virq(xenevtchn_handle *xce,
307 unsigned int virq)
308 {
309 struct port_info *port_info;
310 evtchn_port_t port;
311
312 port_info = port_alloc(xce);
313 if ( port_info == NULL )
314 return -1;
315
316 printf("xenevtchn_bind_virq(%d)", virq);
317 port = bind_virq(virq, evtchn_handler, xce);
318 printf(" = %d\n", port);
319
320 if ( port < 0 )
321 {
322 port_dealloc(port_info);
323 errno = -port;
324 return -1;
325 }
326
327 port_info->bound = true;
328 port_info->port = port;
329 unmask_evtchn(port);
330
331 return port;
332 }
333
xenevtchn_pending(xenevtchn_handle * xce)334 xenevtchn_port_or_error_t xenevtchn_pending(xenevtchn_handle *xce)
335 {
336 struct file *file = get_file_from_fd(xce->fd);
337 struct port_info *port_info;
338 struct ports *ports = file->dev;
339 unsigned long flags;
340 evtchn_port_t ret = -1;
341
342 local_irq_save(flags);
343
344 file->read = false;
345
346 XEN_LIST_FOREACH(port_info, &ports->list, list)
347 {
348 if ( port_info->port != -1 && port_info->pending )
349 {
350 if ( ret == -1 )
351 {
352 ret = port_info->port;
353 port_info->pending = false;
354 }
355 else
356 {
357 file->read = true;
358 break;
359 }
360 }
361 }
362
363 local_irq_restore(flags);
364
365 return ret;
366 }
367
xenevtchn_unmask(xenevtchn_handle * xce,evtchn_port_t port)368 int xenevtchn_unmask(xenevtchn_handle *xce, evtchn_port_t port)
369 {
370 unmask_evtchn(port);
371
372 return 0;
373 }
374
375 /*
376 * Local variables:
377 * mode: C
378 * c-file-style: "BSD"
379 * c-basic-offset: 4
380 * tab-width: 4
381 * indent-tabs-mode: nil
382 * End:
383 */
384