xref: /optee_os/core/kernel/interrupt.c (revision 17a66904a791447da1356331f01e7c1ca25329be)
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 TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
108 					   uint32_t type, uint32_t prio)
109 {
110 	struct itr_handler *h = NULL;
111 
112 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
113 	       hdl->handler && is_unpaged(hdl->handler));
114 
115 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
116 		if (h->it == hdl->it &&
117 		    (!(hdl->flags & ITRF_SHARED) ||
118 		     !(h->flags & ITRF_SHARED))) {
119 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
120 			     hdl->chip->name, hdl->it);
121 			return TEE_ERROR_GENERIC;
122 		}
123 	}
124 
125 	interrupt_configure(hdl->chip, hdl->it, type, prio);
126 
127 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
128 
129 	return TEE_SUCCESS;
130 }
131 
132 void interrupt_remove_handler(struct itr_handler *hdl)
133 {
134 	struct itr_handler *h = NULL;
135 	bool disable_itr = true;
136 
137 	if (!hdl)
138 		return;
139 
140 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
141 		if (h == hdl)
142 			break;
143 	if (!h) {
144 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
145 		assert(false);
146 		return;
147 	}
148 
149 	if (hdl->flags & ITRF_SHARED) {
150 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
151 			if (h != hdl && h->it == hdl->it) {
152 				disable_itr = false;
153 				break;
154 			}
155 		}
156 	}
157 
158 	if (disable_itr)
159 		interrupt_disable(hdl->chip, hdl->it);
160 
161 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
162 }
163 
164 TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip,
165 					    size_t itr_num,
166 					    itr_handler_t handler,
167 					    uint32_t flags, void *data,
168 					    uint32_t type, uint32_t prio,
169 					    struct itr_handler **out_hdl)
170 {
171 	TEE_Result res = TEE_ERROR_GENERIC;
172 	struct itr_handler *hdl = NULL;
173 
174 	hdl = calloc(1, sizeof(*hdl));
175 	if (!hdl)
176 		return TEE_ERROR_OUT_OF_MEMORY;
177 
178 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
179 
180 	res = interrupt_add_configure_handler(hdl, type, prio);
181 	if (res) {
182 		free(hdl);
183 		return res;
184 	}
185 
186 	if (out_hdl)
187 		*out_hdl = hdl;
188 
189 	return TEE_SUCCESS;
190 }
191 
192 void interrupt_remove_free_handler(struct itr_handler *hdl)
193 {
194 	if (hdl) {
195 		interrupt_remove_handler(hdl);
196 		free(hdl);
197 	}
198 }
199