xref: /optee_os/core/kernel/interrupt.c (revision bc12b0e95e3c63f46850c1e69c79cd6879c68543)
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 void itr_handle(size_t it)
69 {
70 	interrupt_call_handlers(itr_main_chip, it);
71 }
72 
73 struct itr_handler *itr_alloc_add_type_prio(size_t it, itr_handler_t handler,
74 					    uint32_t flags, void *data,
75 					    uint32_t type, uint32_t prio)
76 {
77 	struct itr_handler *hdl = calloc(1, sizeof(*hdl));
78 
79 	if (hdl) {
80 		hdl->it = it;
81 		hdl->handler = handler;
82 		hdl->flags = flags;
83 		hdl->data = data;
84 		itr_add_type_prio(hdl, type, prio);
85 	}
86 
87 	return hdl;
88 }
89 
90 void itr_free(struct itr_handler *hdl)
91 {
92 	if (!hdl)
93 		return;
94 
95 	itr_main_chip->ops->disable(itr_main_chip, hdl->it);
96 
97 	SLIST_REMOVE(&itr_main_chip->handlers, hdl, itr_handler, link);
98 	free(hdl);
99 }
100 
101 void itr_add_type_prio(struct itr_handler *h, uint32_t type, uint32_t prio)
102 {
103 	struct itr_handler __maybe_unused *hdl = NULL;
104 
105 	SLIST_FOREACH(hdl, &itr_main_chip->handlers, link)
106 		if (hdl->it == h->it)
107 			assert((hdl->flags & ITRF_SHARED) &&
108 			       (h->flags & ITRF_SHARED));
109 
110 	itr_main_chip->ops->add(itr_main_chip, h->it, type, prio);
111 	SLIST_INSERT_HEAD(&itr_main_chip->handlers, h, link);
112 }
113 
114 void itr_enable(size_t it)
115 {
116 	itr_main_chip->ops->enable(itr_main_chip, it);
117 }
118 
119 void itr_disable(size_t it)
120 {
121 	itr_main_chip->ops->disable(itr_main_chip, it);
122 }
123 
124 void itr_raise_pi(size_t it)
125 {
126 	itr_main_chip->ops->raise_pi(itr_main_chip, it);
127 }
128 
129 void itr_raise_sgi(size_t it, uint8_t cpu_mask)
130 {
131 	itr_main_chip->ops->raise_sgi(itr_main_chip, it, cpu_mask);
132 }
133 
134 void itr_set_affinity(size_t it, uint8_t cpu_mask)
135 {
136 	itr_main_chip->ops->set_affinity(itr_main_chip, it, cpu_mask);
137 }
138 
139 /* This function is supposed to be overridden in platform specific code */
140 void __weak __noreturn interrupt_main_handler(void)
141 {
142 	panic("Secure interrupt handler not defined");
143 }
144 
145 /*
146  * Interrupt controller chip support
147  */
148 void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num)
149 {
150 	struct itr_handler *h = NULL;
151 	bool was_handled = false;
152 
153 	assert(chip);
154 
155 	SLIST_FOREACH(h, &chip->handlers, link) {
156 		if (h->it == itr_num) {
157 			if (h->handler(h) == ITRR_HANDLED)
158 				was_handled = true;
159 			else if (!(h->flags & ITRF_SHARED))
160 				break;
161 		}
162 	}
163 
164 	if (!was_handled) {
165 		EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num);
166 		interrupt_mask(chip, itr_num);
167 	}
168 }
169 
170 TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num,
171 			       uint32_t type, uint32_t prio)
172 {
173 	chip->ops->add(chip, itr_num, type, prio);
174 
175 	return TEE_SUCCESS;
176 }
177 
178 TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
179 					   uint32_t type, uint32_t prio)
180 {
181 	struct itr_handler *h = NULL;
182 
183 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
184 	       hdl->handler && is_unpaged(hdl->handler));
185 
186 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
187 		if (h->it == hdl->it &&
188 		    (!(hdl->flags & ITRF_SHARED) ||
189 		     !(h->flags & ITRF_SHARED))) {
190 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
191 			     hdl->chip->name, hdl->it);
192 			return TEE_ERROR_GENERIC;
193 		}
194 	}
195 
196 	interrupt_configure(hdl->chip, hdl->it, type, prio);
197 
198 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
199 
200 	return TEE_SUCCESS;
201 }
202 
203 void interrupt_remove_handler(struct itr_handler *hdl)
204 {
205 	struct itr_handler *h = NULL;
206 	bool disable_itr = true;
207 
208 	if (!hdl)
209 		return;
210 
211 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
212 		if (h == hdl)
213 			break;
214 	if (!h) {
215 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
216 		assert(false);
217 		return;
218 	}
219 
220 	if (hdl->flags & ITRF_SHARED) {
221 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
222 			if (h != hdl && h->it == hdl->it) {
223 				disable_itr = false;
224 				break;
225 			}
226 		}
227 	}
228 
229 	if (disable_itr)
230 		interrupt_disable(hdl->chip, hdl->it);
231 
232 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
233 }
234 
235 TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip, size_t itr_num,
236 				       itr_handler_t handler, uint32_t flags,
237 				       void *data, struct itr_handler **out_hdl)
238 {
239 	TEE_Result res = TEE_ERROR_GENERIC;
240 	struct itr_handler *hdl = NULL;
241 
242 	hdl = calloc(1, sizeof(*hdl));
243 	if (!hdl)
244 		return TEE_ERROR_OUT_OF_MEMORY;
245 
246 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
247 
248 	res = interrupt_add_handler(hdl);
249 	if (res) {
250 		free(hdl);
251 		return res;
252 	}
253 
254 	if (out_hdl)
255 		*out_hdl = hdl;
256 
257 	return TEE_SUCCESS;
258 }
259 
260 void interrupt_remove_free_handler(struct itr_handler *hdl)
261 {
262 	if (hdl) {
263 		interrupt_remove_handler(hdl);
264 		free(hdl);
265 	}
266 }
267