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