xref: /optee_os/core/kernel/interrupt.c (revision f932e355993798f38a051d9ec4e06d208c969668)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016-2019, Linaro Limited
4  */
5 
6 #include <kernel/dt.h>
7 #include <kernel/interrupt.h>
8 #include <kernel/panic.h>
9 #include <libfdt.h>
10 #include <mm/core_memprot.h>
11 #include <stdlib.h>
12 #include <trace.h>
13 #include <assert.h>
14 
15 /*
16  * NOTE!
17  *
18  * We're assuming that there's no concurrent use of this interface, except
19  * delivery of interrupts in parallel. Synchronization will be needed when
20  * we begin to modify settings after boot initialization.
21  */
22 
23 static struct itr_chip *itr_main_chip __nex_bss;
24 static SLIST_HEAD(, itr_handler) handlers __nex_data =
25 	SLIST_HEAD_INITIALIZER(handlers);
26 
27 TEE_Result itr_chip_init(struct itr_chip *chip)
28 {
29 	if (!itr_chip_is_valid(chip))
30 		return TEE_ERROR_BAD_PARAMETERS;
31 
32 	SLIST_INIT(&chip->handlers);
33 
34 	return TEE_SUCCESS;
35 }
36 
37 void interrupt_main_init(struct itr_chip *chip)
38 {
39 	assert(itr_chip_is_valid(chip));
40 	itr_main_chip = chip;
41 }
42 
43 struct itr_chip *interrupt_get_main_chip(void)
44 {
45 	assert(itr_main_chip);
46 	return itr_main_chip;
47 }
48 
49 #ifdef CFG_DT
50 int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type,
51 			 uint32_t *prio)
52 {
53 	const uint32_t *prop = NULL;
54 	int count = 0;
55 	int it_num = DT_INFO_INVALID_INTERRUPT;
56 
57 	if (!itr_main_chip || !itr_main_chip->dt_get_irq)
58 		return it_num;
59 
60 	prop = fdt_getprop(fdt, node, "interrupts", &count);
61 	if (!prop)
62 		return it_num;
63 
64 	return itr_main_chip->dt_get_irq(prop, count, type, prio);
65 }
66 #endif
67 
68 void itr_handle(size_t it)
69 {
70 	struct itr_handler *h = NULL;
71 	bool was_handled = false;
72 
73 	SLIST_FOREACH(h, &handlers, link) {
74 		if (h->it == it) {
75 			if (h->handler(h) == ITRR_HANDLED)
76 				was_handled = true;
77 			else if (!(h->flags & ITRF_SHARED))
78 				break;
79 		}
80 	}
81 
82 	if (!was_handled) {
83 		EMSG("Disabling unhandled interrupt %zu", it);
84 		itr_main_chip->ops->disable(itr_main_chip, it);
85 	}
86 }
87 
88 struct itr_handler *itr_alloc_add_type_prio(size_t it, itr_handler_t handler,
89 					    uint32_t flags, void *data,
90 					    uint32_t type, uint32_t prio)
91 {
92 	struct itr_handler *hdl = calloc(1, sizeof(*hdl));
93 
94 	if (hdl) {
95 		hdl->it = it;
96 		hdl->handler = handler;
97 		hdl->flags = flags;
98 		hdl->data = data;
99 		itr_add_type_prio(hdl, type, prio);
100 	}
101 
102 	return hdl;
103 }
104 
105 void itr_free(struct itr_handler *hdl)
106 {
107 	if (!hdl)
108 		return;
109 
110 	itr_main_chip->ops->disable(itr_main_chip, hdl->it);
111 
112 	SLIST_REMOVE(&handlers, hdl, itr_handler, link);
113 	free(hdl);
114 }
115 
116 void itr_add_type_prio(struct itr_handler *h, uint32_t type, uint32_t prio)
117 {
118 	struct itr_handler __maybe_unused *hdl = NULL;
119 
120 	SLIST_FOREACH(hdl, &handlers, link)
121 		if (hdl->it == h->it)
122 			assert((hdl->flags & ITRF_SHARED) &&
123 			       (h->flags & ITRF_SHARED));
124 
125 	itr_main_chip->ops->add(itr_main_chip, h->it, type, prio);
126 	SLIST_INSERT_HEAD(&handlers, h, link);
127 }
128 
129 void itr_enable(size_t it)
130 {
131 	itr_main_chip->ops->enable(itr_main_chip, it);
132 }
133 
134 void itr_disable(size_t it)
135 {
136 	itr_main_chip->ops->disable(itr_main_chip, it);
137 }
138 
139 void itr_raise_pi(size_t it)
140 {
141 	itr_main_chip->ops->raise_pi(itr_main_chip, it);
142 }
143 
144 void itr_raise_sgi(size_t it, uint8_t cpu_mask)
145 {
146 	itr_main_chip->ops->raise_sgi(itr_main_chip, it, cpu_mask);
147 }
148 
149 void itr_set_affinity(size_t it, uint8_t cpu_mask)
150 {
151 	itr_main_chip->ops->set_affinity(itr_main_chip, it, cpu_mask);
152 }
153 
154 /* This function is supposed to be overridden in platform specific code */
155 void __weak __noreturn interrupt_main_handler(void)
156 {
157 	panic("Secure interrupt handler not defined");
158 }
159 
160 /*
161  * Interrupt controller chip support
162  */
163 void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num)
164 {
165 	struct itr_handler *h = NULL;
166 	bool was_handled = false;
167 
168 	assert(chip);
169 
170 	SLIST_FOREACH(h, &chip->handlers, link) {
171 		if (h->it == itr_num) {
172 			if (h->handler(h) == ITRR_HANDLED)
173 				was_handled = true;
174 			else if (!(h->flags & ITRF_SHARED))
175 				break;
176 		}
177 	}
178 
179 	if (!was_handled) {
180 		EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num);
181 		interrupt_mask(chip, itr_num);
182 	}
183 }
184 
185 TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num,
186 			       uint32_t type, uint32_t prio)
187 {
188 	chip->ops->add(chip, itr_num, type, prio);
189 
190 	return TEE_SUCCESS;
191 }
192 
193 TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
194 					   uint32_t type, uint32_t prio)
195 {
196 	struct itr_handler *h = NULL;
197 
198 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
199 	       hdl->handler && is_unpaged(hdl->handler));
200 
201 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
202 		if (h->it == hdl->it &&
203 		    (!(hdl->flags & ITRF_SHARED) ||
204 		     !(h->flags & ITRF_SHARED))) {
205 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
206 			     hdl->chip->name, hdl->it);
207 			return TEE_ERROR_GENERIC;
208 		}
209 	}
210 
211 	interrupt_configure(hdl->chip, hdl->it, type, prio);
212 
213 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
214 
215 	return TEE_SUCCESS;
216 }
217 
218 void interrupt_remove_handler(struct itr_handler *hdl)
219 {
220 	struct itr_handler *h = NULL;
221 	bool disable_itr = true;
222 
223 	if (!hdl)
224 		return;
225 
226 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
227 		if (h == hdl)
228 			break;
229 	if (!h) {
230 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
231 		assert(false);
232 		return;
233 	}
234 
235 	if (hdl->flags & ITRF_SHARED) {
236 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
237 			if (h != hdl && h->it == hdl->it) {
238 				disable_itr = false;
239 				break;
240 			}
241 		}
242 	}
243 
244 	if (disable_itr)
245 		interrupt_disable(hdl->chip, hdl->it);
246 
247 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
248 }
249 
250 TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip, size_t itr_num,
251 				       itr_handler_t handler, uint32_t flags,
252 				       void *data, struct itr_handler **out_hdl)
253 {
254 	TEE_Result res = TEE_ERROR_GENERIC;
255 	struct itr_handler *hdl = NULL;
256 
257 	hdl = calloc(1, sizeof(*hdl));
258 	if (!hdl)
259 		return TEE_ERROR_OUT_OF_MEMORY;
260 
261 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
262 
263 	res = interrupt_add_handler(hdl);
264 	if (res) {
265 		free(hdl);
266 		return res;
267 	}
268 
269 	if (out_hdl)
270 		*out_hdl = hdl;
271 
272 	return TEE_SUCCESS;
273 }
274 
275 void interrupt_remove_free_handler(struct itr_handler *hdl)
276 {
277 	if (hdl) {
278 		interrupt_remove_handler(hdl);
279 		free(hdl);
280 	}
281 }
282