1 /*
2 * Copyright (c) 2021 EPAM Systems
3 * Copyright (c) 2022 Arm Limited (or its affiliates). All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/arch/arm64/hypercall.h>
9 #include <zephyr/xen/public/xen.h>
10 #include <zephyr/xen/public/event_channel.h>
11 #include <zephyr/xen/events.h>
12 #include <zephyr/sys/barrier.h>
13
14 #include <errno.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/irq.h>
18
19 LOG_MODULE_REGISTER(xen_events);
20
21 extern shared_info_t *HYPERVISOR_shared_info;
22
23 static evtchn_handle_t event_channels[EVTCHN_2L_NR_CHANNELS];
24 static bool events_missed[EVTCHN_2L_NR_CHANNELS];
25
empty_callback(void * data)26 static void empty_callback(void *data)
27 {
28 /* data is the event_channels entry, subtracting the base, it's the port */
29 unsigned int port = (((evtchn_handle_t *)data) - event_channels);
30
31 events_missed[port] = true;
32 }
33
alloc_unbound_event_channel(domid_t remote_dom)34 int alloc_unbound_event_channel(domid_t remote_dom)
35 {
36 int rc;
37 struct evtchn_alloc_unbound alloc = {
38 .dom = DOMID_SELF,
39 .remote_dom = remote_dom,
40 };
41
42 rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &alloc);
43 if (rc == 0) {
44 rc = alloc.port;
45 }
46
47 return rc;
48 }
49
50 #ifdef CONFIG_XEN_DOM0
alloc_unbound_event_channel_dom0(domid_t dom,domid_t remote_dom)51 int alloc_unbound_event_channel_dom0(domid_t dom, domid_t remote_dom)
52 {
53 int rc;
54 struct evtchn_alloc_unbound alloc = {
55 .dom = dom,
56 .remote_dom = remote_dom,
57 };
58
59 rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &alloc);
60 if (rc == 0) {
61 rc = alloc.port;
62 }
63
64 return rc;
65 }
66 #endif /* CONFIG_XEN_DOM0 */
67
bind_interdomain_event_channel(domid_t remote_dom,evtchn_port_t remote_port,evtchn_cb_t cb,void * data)68 int bind_interdomain_event_channel(domid_t remote_dom, evtchn_port_t remote_port,
69 evtchn_cb_t cb, void *data)
70 {
71 int rc;
72 struct evtchn_bind_interdomain bind = {
73 .remote_dom = remote_dom,
74 .remote_port = remote_port,
75 };
76
77 rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, &bind);
78 if (rc < 0) {
79 return rc;
80 }
81
82 rc = bind_event_channel(bind.local_port, cb, data);
83 if (rc < 0) {
84 return rc;
85 }
86
87 return bind.local_port;
88 }
89
evtchn_status(evtchn_status_t * status)90 int evtchn_status(evtchn_status_t *status)
91 {
92 return HYPERVISOR_event_channel_op(EVTCHNOP_status, status);
93 }
94
evtchn_close(evtchn_port_t port)95 int evtchn_close(evtchn_port_t port)
96 {
97 struct evtchn_close close = {
98 .port = port,
99 };
100
101 return HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
102 }
103
evtchn_set_priority(evtchn_port_t port,uint32_t priority)104 int evtchn_set_priority(evtchn_port_t port, uint32_t priority)
105 {
106 struct evtchn_set_priority set = {
107 .port = port,
108 .priority = priority,
109 };
110
111 return HYPERVISOR_event_channel_op(EVTCHNOP_set_priority, &set);
112 }
113
notify_evtchn(evtchn_port_t port)114 void notify_evtchn(evtchn_port_t port)
115 {
116 struct evtchn_send send;
117
118 __ASSERT(port < EVTCHN_2L_NR_CHANNELS,
119 "%s: trying to send notify for invalid evtchn #%u\n",
120 __func__, port);
121
122 send.port = port;
123
124 HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
125 }
126
bind_event_channel(evtchn_port_t port,evtchn_cb_t cb,void * data)127 int bind_event_channel(evtchn_port_t port, evtchn_cb_t cb, void *data)
128 {
129 __ASSERT(port < EVTCHN_2L_NR_CHANNELS,
130 "%s: trying to bind invalid evtchn #%u\n",
131 __func__, port);
132 __ASSERT(cb != NULL, "%s: NULL callback for evtchn #%u\n",
133 __func__, port);
134
135 if (event_channels[port].cb != empty_callback)
136 LOG_WRN("%s: re-bind callback for evtchn #%u\n",
137 __func__, port);
138
139 event_channels[port].priv = data;
140 event_channels[port].cb = cb;
141
142 return 0;
143 }
144
unbind_event_channel(evtchn_port_t port)145 int unbind_event_channel(evtchn_port_t port)
146 {
147 __ASSERT(port < EVTCHN_2L_NR_CHANNELS,
148 "%s: trying to unbind invalid evtchn #%u\n",
149 __func__, port);
150
151 event_channels[port].cb = empty_callback;
152 event_channels[port].priv = &event_channels[port];
153 events_missed[port] = false;
154
155 return 0;
156 }
157
get_missed_events(evtchn_port_t port)158 int get_missed_events(evtchn_port_t port)
159 {
160 __ASSERT(port < EVTCHN_2L_NR_CHANNELS,
161 "%s: trying to get missed event from invalid port #%u\n",
162 __func__, port);
163
164 if (events_missed[port]) {
165 events_missed[port] = false;
166 return 1;
167 }
168
169 return 0;
170 }
171
mask_event_channel(evtchn_port_t port)172 int mask_event_channel(evtchn_port_t port)
173 {
174 shared_info_t *s = HYPERVISOR_shared_info;
175
176 __ASSERT(port < EVTCHN_2L_NR_CHANNELS,
177 "%s: trying to mask invalid evtchn #%u\n",
178 __func__, port);
179
180
181 sys_bitfield_set_bit((mem_addr_t) s->evtchn_mask, port);
182
183 return 0;
184 }
185
unmask_event_channel(evtchn_port_t port)186 int unmask_event_channel(evtchn_port_t port)
187 {
188 shared_info_t *s = HYPERVISOR_shared_info;
189
190 __ASSERT(port < EVTCHN_2L_NR_CHANNELS,
191 "%s: trying to unmask invalid evtchn #%u\n",
192 __func__, port);
193
194 sys_bitfield_clear_bit((mem_addr_t) s->evtchn_mask, port);
195
196 return 0;
197 }
198
clear_event_channel(evtchn_port_t port)199 static void clear_event_channel(evtchn_port_t port)
200 {
201 shared_info_t *s = HYPERVISOR_shared_info;
202
203 sys_bitfield_clear_bit((mem_addr_t) s->evtchn_pending, port);
204 }
205
get_pending_events(xen_ulong_t pos)206 static inline xen_ulong_t get_pending_events(xen_ulong_t pos)
207 {
208 shared_info_t *s = HYPERVISOR_shared_info;
209
210 return (s->evtchn_pending[pos] & ~(s->evtchn_mask[pos]));
211 }
212
process_event(evtchn_port_t port)213 static void process_event(evtchn_port_t port)
214 {
215 evtchn_handle_t channel = event_channels[port];
216
217 clear_event_channel(port);
218 channel.cb(channel.priv);
219 }
220
events_isr(void * data)221 static void events_isr(void *data)
222 {
223 ARG_UNUSED(data);
224
225 /* Needed for 2-level unwrapping */
226 xen_ulong_t pos_selector; /* bits are positions in pending array */
227 xen_ulong_t events_pending; /* bits - events in pos_selector element */
228 uint32_t pos_index, event_index; /* bit indexes */
229
230 evtchn_port_t port; /* absolute event index */
231
232 /* TODO: SMP? XEN_LEGACY_MAX_VCPUS == 1*/
233 vcpu_info_t *vcpu = &HYPERVISOR_shared_info->vcpu_info[0];
234
235 /*
236 * Need to set it to 0 /before/ checking for pending work, thus
237 * avoiding a set-and-check race (check struct vcpu_info_t)
238 */
239 vcpu->evtchn_upcall_pending = 0;
240
241 barrier_dmem_fence_full();
242
243 /* Can not use system atomic_t/atomic_set() due to 32-bit casting */
244 pos_selector = __atomic_exchange_n(&vcpu->evtchn_pending_sel,
245 0, __ATOMIC_SEQ_CST);
246
247 while (pos_selector) {
248 /* Find first position, clear it in selector and process */
249 pos_index = __builtin_ffsl(pos_selector) - 1;
250 pos_selector &= ~(((xen_ulong_t) 1) << pos_index);
251
252 /* Find all active evtchn on selected position */
253 while ((events_pending = get_pending_events(pos_index)) != 0) {
254 event_index = __builtin_ffsl(events_pending) - 1;
255 events_pending &= (((xen_ulong_t) 1) << event_index);
256
257 port = (pos_index * 8 * sizeof(xen_ulong_t))
258 + event_index;
259 process_event(port);
260 }
261 }
262 }
263
xen_events_init(void)264 int xen_events_init(void)
265 {
266 int i;
267
268 if (!HYPERVISOR_shared_info) {
269 /* shared info was not mapped */
270 LOG_ERR("%s: shared_info - NULL, can't setup events\n", __func__);
271 return -EINVAL;
272 }
273
274 /* bind all ports with default callback */
275 for (i = 0; i < EVTCHN_2L_NR_CHANNELS; i++) {
276 event_channels[i].cb = empty_callback;
277 event_channels[i].priv = &event_channels[i];
278 events_missed[i] = false;
279 }
280
281 IRQ_CONNECT(DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, irq),
282 DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, priority), events_isr,
283 NULL, DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, flags));
284
285 irq_enable(DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, irq));
286
287 LOG_INF("%s: events inited\n", __func__);
288 return 0;
289 }
290