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