xref: /optee_os/core/kernel/interrupt.c (revision 79f8990d9d28539864d8f97f9f1cb32e289e595f)
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 
25 TEE_Result itr_chip_init(struct itr_chip *chip)
26 {
27 	if (!itr_chip_is_valid(chip))
28 		return TEE_ERROR_BAD_PARAMETERS;
29 
30 	SLIST_INIT(&chip->handlers);
31 
32 	return TEE_SUCCESS;
33 }
34 
35 void interrupt_main_init(struct itr_chip *chip)
36 {
37 	if (itr_chip_init(chip))
38 		panic();
39 
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 struct itr_chip *interrupt_get_main_chip_may_fail(void)
50 {
51 	return itr_main_chip;
52 }
53 
54 #ifdef CFG_DT
55 int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type,
56 			 uint32_t *prio)
57 {
58 	const uint32_t *prop = NULL;
59 	int count = 0;
60 	int it_num = DT_INFO_INVALID_INTERRUPT;
61 
62 	if (!itr_main_chip || !itr_main_chip->dt_get_irq)
63 		return it_num;
64 
65 	prop = fdt_getprop(fdt, node, "interrupts", &count);
66 	if (!prop)
67 		return it_num;
68 
69 	return itr_main_chip->dt_get_irq(prop, count, type, prio);
70 }
71 #endif
72 
73 /* This function is supposed to be overridden in platform specific code */
74 void __weak __noreturn interrupt_main_handler(void)
75 {
76 	panic("Secure interrupt handler not defined");
77 }
78 
79 /*
80  * Interrupt controller chip support
81  */
82 void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num)
83 {
84 	struct itr_handler *h = NULL;
85 	bool was_handled = false;
86 
87 	assert(chip);
88 
89 	SLIST_FOREACH(h, &chip->handlers, link) {
90 		if (h->it == itr_num) {
91 			if (h->handler(h) == ITRR_HANDLED)
92 				was_handled = true;
93 			else if (!(h->flags & ITRF_SHARED))
94 				break;
95 		}
96 	}
97 
98 	if (!was_handled) {
99 		EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num);
100 		interrupt_mask(chip, itr_num);
101 	}
102 }
103 
104 TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num,
105 			       uint32_t type, uint32_t prio)
106 {
107 	chip->ops->add(chip, itr_num, type, prio);
108 
109 	return TEE_SUCCESS;
110 }
111 
112 static TEE_Result add_configure_handler(struct itr_handler *hdl,
113 					uint32_t type, uint32_t prio,
114 					bool configure)
115 {
116 	struct itr_handler *h = NULL;
117 
118 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
119 	       hdl->handler && is_unpaged(hdl->handler));
120 
121 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
122 		if (h->it == hdl->it &&
123 		    (!(hdl->flags & ITRF_SHARED) ||
124 		     !(h->flags & ITRF_SHARED))) {
125 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
126 			     hdl->chip->name, hdl->it);
127 			return TEE_ERROR_GENERIC;
128 		}
129 	}
130 
131 	if (configure)
132 		interrupt_configure(hdl->chip, hdl->it, type, prio);
133 
134 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
135 
136 	return TEE_SUCCESS;
137 }
138 
139 TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
140 					   uint32_t type, uint32_t prio)
141 {
142 	return add_configure_handler(hdl, type, prio, true /* configure */);
143 }
144 
145 TEE_Result interrupt_create_handler(struct itr_chip *itr_chip, size_t itr_num,
146 				    itr_handler_t callback, void *priv,
147 				    uint32_t flags,
148 				    struct itr_handler **out_hdl)
149 {
150 	TEE_Result res = TEE_ERROR_GENERIC;
151 	struct itr_handler *itr_hdl = NULL;
152 
153 	itr_hdl = calloc(1, sizeof(*itr_hdl));
154 	if (!itr_hdl)
155 		return TEE_ERROR_OUT_OF_MEMORY;
156 
157 	*itr_hdl = (struct itr_handler){
158 		.chip = itr_chip,
159 		.it = itr_num,
160 		.flags = flags,
161 		.handler = callback,
162 		.data = priv,
163 	};
164 
165 	res = add_configure_handler(itr_hdl, 0, 0, false /* configure */);
166 	if (res) {
167 		free(itr_hdl);
168 		return res;
169 	}
170 
171 	if (out_hdl)
172 		*out_hdl = itr_hdl;
173 
174 	return TEE_SUCCESS;
175 }
176 
177 void interrupt_remove_handler(struct itr_handler *hdl)
178 {
179 	struct itr_handler *h = NULL;
180 	bool disable_itr = true;
181 
182 	if (!hdl)
183 		return;
184 
185 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
186 		if (h == hdl)
187 			break;
188 	if (!h) {
189 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
190 		assert(false);
191 		return;
192 	}
193 
194 	if (hdl->flags & ITRF_SHARED) {
195 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
196 			if (h != hdl && h->it == hdl->it) {
197 				disable_itr = false;
198 				break;
199 			}
200 		}
201 	}
202 
203 	if (disable_itr)
204 		interrupt_disable(hdl->chip, hdl->it);
205 
206 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
207 }
208 
209 TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip,
210 					    size_t itr_num,
211 					    itr_handler_t handler,
212 					    uint32_t flags, void *data,
213 					    uint32_t type, uint32_t prio,
214 					    struct itr_handler **out_hdl)
215 {
216 	TEE_Result res = TEE_ERROR_GENERIC;
217 	struct itr_handler *hdl = NULL;
218 
219 	hdl = calloc(1, sizeof(*hdl));
220 	if (!hdl)
221 		return TEE_ERROR_OUT_OF_MEMORY;
222 
223 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
224 
225 	res = interrupt_add_configure_handler(hdl, type, prio);
226 	if (res) {
227 		free(hdl);
228 		return res;
229 	}
230 
231 	if (out_hdl)
232 		*out_hdl = hdl;
233 
234 	return TEE_SUCCESS;
235 }
236 
237 void interrupt_remove_free_handler(struct itr_handler *hdl)
238 {
239 	if (hdl) {
240 		interrupt_remove_handler(hdl);
241 		free(hdl);
242 	}
243 }
244 
245 #ifdef CFG_DT
246 TEE_Result interrupt_register_provider(const void *fdt, int node,
247 				       itr_dt_get_func dt_get_itr, void *data)
248 {
249 	return dt_driver_register_provider(fdt, node,
250 					   (get_of_device_func)dt_get_itr,
251 					   data, DT_DRIVER_INTERRUPT);
252 }
253 
254 /*
255  * Fills an itr_desc reference based on "interrupts" property bindings.
256  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but
257  * not yet initialized.
258  */
259 static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node,
260 						unsigned int index,
261 						struct itr_desc *itr_desc)
262 {
263 	const uint32_t *prop = NULL;
264 	uint32_t phandle = 0;
265 	int pnode = 0;
266 	int len = 0;
267 
268 	prop = fdt_getprop(fdt, node, "interrupts", &len);
269 	if (!prop)
270 		return TEE_ERROR_ITEM_NOT_FOUND;
271 
272 	/* Find "interrupt-parent" in node or its parents */
273 	pnode = node;
274 	prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
275 
276 	while (!prop) {
277 		pnode = fdt_parent_offset(fdt, pnode);
278 		if (pnode < 0)
279 			break;
280 
281 		prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
282 		if (!prop && len != -FDT_ERR_NOTFOUND)
283 			break;
284 	}
285 	if (!prop) {
286 		DMSG("No interrupt parent for node %s",
287 		     fdt_get_name(fdt, node, NULL));
288 		return TEE_ERROR_GENERIC;
289 	}
290 
291 	/* "interrupt-parent" provides interrupt controller phandle */
292 	phandle = fdt32_to_cpu(prop[0]);
293 
294 	/* Get interrupt chip/number from phandle and "interrupts" property */
295 	return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt,
296 							   node, index,
297 							   DT_DRIVER_INTERRUPT,
298 							   phandle,
299 							   itr_desc);
300 }
301 
302 /*
303  * Fills an itr_desc based on "interrupts-extended" property bindings.
304  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found
305  * but not yet initialized.
306  */
307 static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node,
308 						  unsigned int index,
309 						  struct itr_desc *itr_desc)
310 {
311 	return dt_driver_device_from_node_idx_prop("interrupts-extended",
312 						   fdt, node, index,
313 						   DT_DRIVER_INTERRUPT,
314 						   itr_desc);
315 }
316 
317 TEE_Result interrupt_dt_get_by_index(const void *fdt, int node,
318 				     unsigned int index, struct itr_chip **chip,
319 				     size_t *itr_num)
320 {
321 	TEE_Result res = TEE_ERROR_GENERIC;
322 	struct itr_desc desc = { };
323 
324 	assert(chip && itr_num);
325 
326 	/* "interrupts-extended" takes precedence over "interrupts" */
327 	if (fdt_getprop(fdt, node, "interrupts-extended", NULL))
328 		res = get_extended_interrupt_by_index(fdt, node, index, &desc);
329 	else
330 		res = get_legacy_interrupt_by_index(fdt, node, index, &desc);
331 
332 	if (!res) {
333 		assert(desc.chip);
334 		*chip = desc.chip;
335 		*itr_num = desc.itr_num;
336 	}
337 
338 	return res;
339 }
340 
341 TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name,
342 				    struct itr_chip **chip, size_t *itr_num)
343 {
344 	int idx = 0;
345 
346 	idx = fdt_stringlist_search(fdt, node, "interrupt-names", name);
347 	if (idx < 0)
348 		return TEE_ERROR_GENERIC;
349 
350 	return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num);
351 }
352 #endif /*CFG_DT*/
353