]>
Commit | Line | Data |
---|---|---|
673fd82c OA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge | |
4 | * (C) 2005 - Grzegorz Milos - Intel Research Cambridge | |
5 | * (C) 2020 - EPAM Systems Inc. | |
6 | * | |
7 | * File: events.c [1] | |
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 | * Description: Deals with events received on event channels | |
14 | * | |
15 | * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary | |
16 | */ | |
d678a59d | 17 | #include <common.h> |
673fd82c OA |
18 | #include <log.h> |
19 | ||
20 | #include <asm/io.h> | |
21 | #include <asm/xen/system.h> | |
22 | ||
23 | #include <xen/events.h> | |
24 | #include <xen/hvm.h> | |
25 | ||
2413fe67 | 26 | #if IS_ENABLED(CONFIG_XEN_SERIAL) |
384d5cfe | 27 | extern u32 console_evtchn; |
2413fe67 | 28 | #endif /* IS_ENABLED(CONFIG_XEN_SERIAL) */ |
384d5cfe | 29 | |
673fd82c OA |
30 | #define NR_EVS 1024 |
31 | ||
32 | /** | |
33 | * struct _ev_action - represents a event handler. | |
34 | * | |
35 | * Chaining or sharing is not allowed | |
36 | */ | |
37 | struct _ev_action { | |
38 | void (*handler)(evtchn_port_t port, struct pt_regs *regs, void *data); | |
39 | void *data; | |
40 | u32 count; | |
41 | }; | |
42 | ||
43 | static struct _ev_action ev_actions[NR_EVS]; | |
44 | void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data); | |
45 | ||
46 | static unsigned long bound_ports[NR_EVS / (8 * sizeof(unsigned long))]; | |
47 | ||
48 | void unbind_all_ports(void) | |
49 | { | |
50 | int i; | |
51 | int cpu = 0; | |
52 | struct shared_info *s = HYPERVISOR_shared_info; | |
53 | struct vcpu_info *vcpu_info = &s->vcpu_info[cpu]; | |
54 | ||
55 | for (i = 0; i < NR_EVS; i++) { | |
2413fe67 | 56 | #if IS_ENABLED(CONFIG_XEN_SERIAL) |
384d5cfe PF |
57 | if (i == console_evtchn) |
58 | continue; | |
2413fe67 | 59 | #endif /* IS_ENABLED(CONFIG_XEN_SERIAL) */ |
c588ca87 | 60 | |
673fd82c OA |
61 | if (test_and_clear_bit(i, bound_ports)) { |
62 | printf("port %d still bound!\n", i); | |
63 | unbind_evtchn(i); | |
64 | } | |
65 | } | |
66 | vcpu_info->evtchn_upcall_pending = 0; | |
67 | vcpu_info->evtchn_pending_sel = 0; | |
68 | } | |
69 | ||
70 | int do_event(evtchn_port_t port, struct pt_regs *regs) | |
71 | { | |
72 | struct _ev_action *action; | |
73 | ||
74 | clear_evtchn(port); | |
75 | ||
76 | if (port >= NR_EVS) { | |
77 | printk("WARN: do_event(): Port number too large: %d\n", port); | |
78 | return 1; | |
79 | } | |
80 | ||
81 | action = &ev_actions[port]; | |
82 | action->count++; | |
83 | ||
84 | /* call the handler */ | |
85 | action->handler(port, regs, action->data); | |
86 | ||
87 | return 1; | |
88 | } | |
89 | ||
90 | evtchn_port_t bind_evtchn(evtchn_port_t port, | |
91 | void (*handler)(evtchn_port_t, struct pt_regs *, void *), | |
92 | void *data) | |
93 | { | |
94 | if (ev_actions[port].handler != default_handler) | |
95 | printf("WARN: Handler for port %d already registered, replacing\n", | |
96 | port); | |
97 | ||
98 | ev_actions[port].data = data; | |
99 | wmb(); | |
100 | ev_actions[port].handler = handler; | |
101 | synch_set_bit(port, bound_ports); | |
102 | ||
103 | return port; | |
104 | } | |
105 | ||
106 | /** | |
107 | * unbind_evtchn() - Unbind event channel for selected port | |
108 | */ | |
109 | void unbind_evtchn(evtchn_port_t port) | |
110 | { | |
111 | struct evtchn_close close; | |
112 | int rc; | |
113 | ||
114 | if (ev_actions[port].handler == default_handler) | |
3a739cc6 | 115 | debug("Default handler for port %d when unbinding\n", port); |
673fd82c OA |
116 | mask_evtchn(port); |
117 | clear_evtchn(port); | |
118 | ||
119 | ev_actions[port].handler = default_handler; | |
120 | wmb(); | |
121 | ev_actions[port].data = NULL; | |
122 | synch_clear_bit(port, bound_ports); | |
123 | ||
124 | close.port = port; | |
125 | rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); | |
126 | if (rc) | |
127 | printf("WARN: close_port %d failed rc=%d. ignored\n", port, rc); | |
128 | } | |
129 | ||
130 | void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore) | |
131 | { | |
132 | debug("[Port %d] - event received\n", port); | |
133 | } | |
134 | ||
135 | /** | |
136 | * evtchn_alloc_unbound() - Create a port available to the pal for | |
137 | * exchanging notifications. | |
138 | * | |
139 | * Unfortunate confusion of terminology: the port is unbound as far | |
140 | * as Xen is concerned, but we automatically bind a handler to it. | |
141 | * | |
142 | * Return: The result of the hypervisor call. | |
143 | */ | |
144 | int evtchn_alloc_unbound(domid_t pal, | |
145 | void (*handler)(evtchn_port_t, struct pt_regs *, void *), | |
146 | void *data, evtchn_port_t *port) | |
147 | { | |
148 | int rc; | |
149 | ||
150 | struct evtchn_alloc_unbound op; | |
151 | ||
152 | op.dom = DOMID_SELF; | |
153 | op.remote_dom = pal; | |
154 | rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); | |
155 | if (rc) { | |
156 | printf("ERROR: alloc_unbound failed with rc=%d", rc); | |
157 | return rc; | |
158 | } | |
159 | if (!handler) | |
160 | handler = default_handler; | |
161 | *port = bind_evtchn(op.port, handler, data); | |
162 | return rc; | |
163 | } | |
164 | ||
165 | /** | |
166 | * eventchn_poll() - Event channel polling function | |
167 | * | |
168 | * Check and process any pending events | |
169 | */ | |
170 | void eventchn_poll(void) | |
171 | { | |
172 | do_hypervisor_callback(NULL); | |
173 | } | |
174 | ||
175 | /** | |
176 | * init_events() - Initialize event handler | |
177 | * | |
178 | * Initially all events are without a handler and disabled. | |
179 | */ | |
180 | void init_events(void) | |
181 | { | |
182 | int i; | |
183 | ||
184 | debug("%s\n", __func__); | |
185 | ||
186 | for (i = 0; i < NR_EVS; i++) { | |
187 | ev_actions[i].handler = default_handler; | |
188 | mask_evtchn(i); | |
189 | } | |
190 | } | |
191 | ||
192 | /** | |
193 | * fini_events() - Close all ports | |
194 | * | |
195 | * Mask and clear event channels. Close port using EVTCHNOP_close | |
196 | * hypercall. | |
197 | */ | |
198 | void fini_events(void) | |
199 | { | |
200 | debug("%s\n", __func__); | |
201 | /* Dealloc all events */ | |
202 | unbind_all_ports(); | |
203 | } |