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 
140 	event_channels[port].priv = data;
141 	event_channels[port].cb = cb;
142 
143 	return 0;
144 }
145 
unbind_event_channel(evtchn_port_t port)146 int unbind_event_channel(evtchn_port_t port)
147 {
148 	__ASSERT(port < EVTCHN_2L_NR_CHANNELS,
149 		"%s: trying to unbind invalid evtchn #%u\n",
150 		__func__, port);
151 
152 	event_channels[port].cb = empty_callback;
153 	event_channels[port].priv = &event_channels[port];
154 	events_missed[port] = false;
155 
156 	return 0;
157 }
158 
get_missed_events(evtchn_port_t port)159 int get_missed_events(evtchn_port_t port)
160 {
161 	__ASSERT(port < EVTCHN_2L_NR_CHANNELS,
162 		"%s: trying to get missed event from invalid port #%u\n",
163 		__func__, port);
164 
165 	if (events_missed[port]) {
166 		events_missed[port] = false;
167 		return 1;
168 	}
169 
170 	return 0;
171 }
172 
mask_event_channel(evtchn_port_t port)173 int mask_event_channel(evtchn_port_t port)
174 {
175 	shared_info_t *s = HYPERVISOR_shared_info;
176 
177 	__ASSERT(port < EVTCHN_2L_NR_CHANNELS,
178 		"%s: trying to mask invalid evtchn #%u\n",
179 		__func__, port);
180 
181 
182 	sys_bitfield_set_bit((mem_addr_t) s->evtchn_mask, port);
183 
184 	return 0;
185 }
186 
unmask_event_channel(evtchn_port_t port)187 int unmask_event_channel(evtchn_port_t port)
188 {
189 	shared_info_t *s = HYPERVISOR_shared_info;
190 
191 	__ASSERT(port < EVTCHN_2L_NR_CHANNELS,
192 		"%s: trying to unmask invalid evtchn #%u\n",
193 		__func__, port);
194 
195 	sys_bitfield_clear_bit((mem_addr_t) s->evtchn_mask, port);
196 
197 	return 0;
198 }
199 
clear_event_channel(evtchn_port_t port)200 static void clear_event_channel(evtchn_port_t port)
201 {
202 	shared_info_t *s = HYPERVISOR_shared_info;
203 
204 	sys_bitfield_clear_bit((mem_addr_t) s->evtchn_pending, port);
205 }
206 
get_pending_events(xen_ulong_t pos)207 static inline xen_ulong_t get_pending_events(xen_ulong_t pos)
208 {
209 	shared_info_t *s = HYPERVISOR_shared_info;
210 
211 	return (s->evtchn_pending[pos] & ~(s->evtchn_mask[pos]));
212 }
213 
process_event(evtchn_port_t port)214 static void process_event(evtchn_port_t port)
215 {
216 	evtchn_handle_t channel = event_channels[port];
217 
218 	clear_event_channel(port);
219 	channel.cb(channel.priv);
220 }
221 
events_isr(void * data)222 static void events_isr(void *data)
223 {
224 	ARG_UNUSED(data);
225 
226 	/* Needed for 2-level unwrapping */
227 	xen_ulong_t pos_selector;   /* bits are positions in pending array */
228 	xen_ulong_t events_pending; /* bits - events in pos_selector element */
229 	uint32_t pos_index, event_index; /* bit indexes */
230 
231 	evtchn_port_t port; /* absolute event index */
232 
233 	/* TODO: SMP? XEN_LEGACY_MAX_VCPUS == 1*/
234 	vcpu_info_t *vcpu = &HYPERVISOR_shared_info->vcpu_info[0];
235 
236 	/*
237 	 * Need to set it to 0 /before/ checking for pending work, thus
238 	 * avoiding a set-and-check race (check struct vcpu_info_t)
239 	 */
240 	vcpu->evtchn_upcall_pending = 0;
241 
242 	barrier_dmem_fence_full();
243 
244 	/* Can not use system atomic_t/atomic_set() due to 32-bit casting */
245 	pos_selector = __atomic_exchange_n(&vcpu->evtchn_pending_sel,
246 					0, __ATOMIC_SEQ_CST);
247 
248 	while (pos_selector) {
249 		/* Find first position, clear it in selector and process */
250 		pos_index = __builtin_ffsl(pos_selector) - 1;
251 		pos_selector &= ~(((xen_ulong_t) 1) << pos_index);
252 
253 		/* Find all active evtchn on selected position */
254 		while ((events_pending = get_pending_events(pos_index)) != 0) {
255 			event_index =  __builtin_ffsl(events_pending) - 1;
256 			events_pending &= (((xen_ulong_t) 1) << event_index);
257 
258 			port = (pos_index * 8 * sizeof(xen_ulong_t))
259 					+ event_index;
260 			process_event(port);
261 		}
262 	}
263 }
264 
xen_events_init(void)265 int xen_events_init(void)
266 {
267 	int i;
268 
269 	if (!HYPERVISOR_shared_info) {
270 		/* shared info was not mapped */
271 		LOG_ERR("%s: shared_info - NULL, can't setup events\n", __func__);
272 		return -EINVAL;
273 	}
274 
275 	/* bind all ports with default callback */
276 	for (i = 0; i < EVTCHN_2L_NR_CHANNELS; i++) {
277 		event_channels[i].cb = empty_callback;
278 		event_channels[i].priv = &event_channels[i];
279 		events_missed[i] = false;
280 	}
281 
282 	IRQ_CONNECT(DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, irq),
283 		DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, priority), events_isr,
284 		NULL, DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, flags));
285 
286 	irq_enable(DT_IRQ_BY_IDX(DT_INST(0, xen_xen), 0, irq));
287 
288 	LOG_INF("%s: events inited\n", __func__);
289 	return 0;
290 }
291