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
itr_chip_is_valid(struct itr_chip * chip)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
__itr_chip_init(struct itr_chip * chip)34 static void __itr_chip_init(struct itr_chip *chip)
35 {
36 SLIST_INIT(&chip->handlers);
37 }
38
itr_chip_init(struct itr_chip * chip)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
itr_chip_dt_only_init(struct itr_chip * chip)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
interrupt_main_init(struct itr_chip * chip)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
interrupt_get_main_chip(void)71 struct itr_chip *interrupt_get_main_chip(void)
72 {
73 assert(itr_main_chip);
74 return itr_main_chip;
75 }
76
interrupt_get_main_chip_may_fail(void)77 struct itr_chip *interrupt_get_main_chip_may_fail(void)
78 {
79 return itr_main_chip;
80 }
81
82 #ifdef CFG_DT
dt_get_irq_type_prio(const void * fdt,int node,uint32_t * type,uint32_t * prio)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 */
interrupt_main_handler(void)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 */
interrupt_call_handlers(struct itr_chip * chip,size_t itr_num)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
interrupt_configure(struct itr_chip * chip,size_t itr_num,uint32_t type,uint32_t prio)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
add_configure_handler(struct itr_handler * hdl,uint32_t type,uint32_t prio,bool configure)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
interrupt_add_configure_handler(struct itr_handler * hdl,uint32_t type,uint32_t prio)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
interrupt_create_handler(struct itr_chip * itr_chip,size_t itr_num,itr_handler_t callback,void * priv,uint32_t flags,struct itr_handler ** out_hdl)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
interrupt_remove_handler(struct itr_handler * hdl)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
interrupt_alloc_add_conf_handler(struct itr_chip * chip,size_t itr_num,itr_handler_t handler,uint32_t flags,void * data,uint32_t type,uint32_t prio,struct itr_handler ** out_hdl)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
interrupt_remove_free_handler(struct itr_handler * hdl)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
interrupt_register_provider(const void * fdt,int node,itr_dt_get_func dt_get_itr,void * data)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 */
get_legacy_interrupt_by_index(const void * fdt,int node,unsigned int index,struct itr_desc * itr_desc)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 */
get_extended_interrupt_by_index(const void * fdt,int node,unsigned int index,struct itr_desc * itr_desc)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
interrupt_dt_get_by_index(const void * fdt,int node,unsigned int index,struct itr_chip ** chip,size_t * itr_num)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
interrupt_dt_get_by_name(const void * fdt,int node,const char * name,struct itr_chip ** chip,size_t * itr_num)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