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