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