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