1 /* $NetBSD: intr.c,v 1.35 2026/03/18 04:08:38 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass, Gordon W. Ross, Jason R. Thorpe, and Leo Weppelman. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.35 2026/03/18 04:08:38 thorpej Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/kmem.h> 39 #include <sys/vmmeter.h> 40 #include <sys/queue.h> 41 #include <sys/device.h> 42 #include <sys/cpu.h> 43 44 #include <machine/intr.h> 45 46 #define AVEC_MIN 1 47 #define AVEC_MAX 7 48 #define AVEC_LOC 25 49 #define UVEC_MIN 0 50 #define UVEC_MAX 191 51 #define UVEC_LOC 64 52 53 typedef LIST_HEAD(, intrhand) ih_list_t; 54 static ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1]; 55 static ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1]; 56 volatile unsigned int intr_depth; 57 58 void 59 intr_init(void) 60 { 61 int i; 62 63 for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) { 64 LIST_INIT(&autovec_list[i]); 65 } 66 for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) { 67 LIST_INIT(&uservec_list[i]); 68 } 69 } 70 71 /* 72 * Establish an interrupt vector. 73 * - vector 74 * The vector number the interrupt should be hooked on. It can either 75 * be an auto-vector or a user-vector. 76 * - type 77 * A bit-wise of: 78 * - AUTO_VEC (mutually exclusive with USER_VEC) 79 * Attach to one of the 7 auto vectors 80 * - USER_VEC (mutually exclusive with AUTO_VEC) 81 * Attach to one of the 192 user vectors 82 * - FAST_VEC 83 * The interrupt function 'ih_fun' will be 84 * put into the 'real' interrupt table. This 85 * means: 86 * - This vector can't be shared 87 * - 'ih_fun' must save registers 88 * - 'ih_fun' must do its own interrupt accounting 89 * - The argument to 'ih_fun' is a standard 90 * interrupt frame. 91 * - ARG_CLOCKRAME 92 * The 'ih_fun' function will be called with 93 * a standard clock-frame instead of 'ih_arg'. 94 * 95 * - pri 96 * When multiple interrupts are established on the same vector, 97 * interrupts with the highest priority will be called first. The 98 * basic ordering is the order of establishment. 99 * - ih_fun 100 * The interrupt function to be called 101 * - ih_arg 102 * The argument given to 'ih_fun' when ARG_CLOCKFRAME is not 103 * specified. 104 */ 105 106 struct intrhand * 107 intr_establish(int vector, int type, int pri, hw_ifun_t ih_fun, void *ih_arg) 108 { 109 struct intrhand *ih, *cur_vec; 110 ih_list_t *vec_list; 111 u_long *hard_vec; 112 int s; 113 114 ih = kmem_alloc(sizeof *ih, KM_SLEEP); 115 ih->ih_fun = ih_fun; 116 ih->ih_arg = ih_arg; 117 ih->ih_type = type; 118 ih->ih_pri = pri; 119 ih->ih_vector = vector; 120 121 /* 122 * Do some validity checking on the 'vector' argument and determine 123 * vector list this interrupt should be on. 124 */ 125 switch (type & (AUTO_VEC|USER_VEC)) { 126 case AUTO_VEC: 127 if (vector < AVEC_MIN || vector > AVEC_MAX) { 128 kmem_free(ih, sizeof(*ih)); 129 return NULL; 130 } 131 vec_list = &autovec_list[vector - 1]; 132 hard_vec = &autovects[vector - 1]; 133 ih->ih_intrcnt = &intrcnt_auto[vector - 1]; 134 break; 135 case USER_VEC: 136 if (vector < UVEC_MIN || vector > UVEC_MAX) { 137 kmem_free(ih, sizeof(*ih)); 138 return NULL; 139 } 140 vec_list = &uservec_list[vector]; 141 hard_vec = &uservects[vector]; 142 ih->ih_intrcnt = &intrcnt_user[vector]; 143 break; 144 default: 145 printf("%s: bogus vector type\n", __func__); 146 kmem_free(ih, sizeof(*ih)); 147 return NULL; 148 } 149 150 /* 151 * If the vec_list is empty, we insert ourselves at the head of the 152 * list and we re-route the 'hard-vector' to the appropriate handler. 153 */ 154 if (LIST_EMPTY(vec_list)) { 155 156 s = splhigh(); 157 LIST_INSERT_HEAD(vec_list, ih, ih_link); 158 if ((type & FAST_VEC) != 0) 159 *hard_vec = (u_long)ih->ih_fun; 160 else if (*hard_vec != (u_long)intr_glue) { 161 /* 162 * Normally, all settable vectors are already 163 * re-routed to the intr_glue() function. The 164 * marvelous exception to these are the HBL/VBL 165 * interrupts. They happen *very* often and 166 * can't be turned off on the Falcon. So they 167 * are normally vectored to an 'rte' instruction. 168 */ 169 *hard_vec = (u_long)intr_glue; 170 } 171 172 splx(s); 173 174 return ih; 175 } 176 177 /* 178 * Check for FAST_VEC botches 179 */ 180 cur_vec = LIST_FIRST(vec_list); 181 if (cur_vec->ih_type & FAST_VEC) { 182 kmem_free(ih, sizeof(*ih)); 183 printf("%s: vector cannot be shared\n", __func__); 184 return NULL; 185 } 186 187 /* 188 * We traverse the list and place ourselves after any handlers with 189 * our current (or higher) priority level. 190 */ 191 for (cur_vec = LIST_FIRST(vec_list); 192 LIST_NEXT(cur_vec, ih_link) != NULL; 193 cur_vec = LIST_NEXT(cur_vec, ih_link)) { 194 if (ih->ih_pri > cur_vec->ih_pri) { 195 196 s = splhigh(); 197 LIST_INSERT_BEFORE(cur_vec, ih, ih_link); 198 splx(s); 199 200 return ih; 201 } 202 } 203 204 /* 205 * We're the least important entry, it seems. We just go 206 * on the end. 207 */ 208 s = splhigh(); 209 LIST_INSERT_AFTER(cur_vec, ih, ih_link); 210 splx(s); 211 212 return ih; 213 } 214 215 int 216 intr_disestablish(struct intrhand *ih) 217 { 218 ih_list_t *vec_list; 219 u_long *hard_vec; 220 int vector, s; 221 struct intrhand *cur_vec; 222 223 vector = ih->ih_vector; 224 switch (ih->ih_type & (AUTO_VEC|USER_VEC)) { 225 case AUTO_VEC: 226 if (vector < AVEC_MIN || vector > AVEC_MAX) 227 return 0; 228 vec_list = &autovec_list[vector - 1]; 229 hard_vec = &autovects[vector - 1]; 230 break; 231 case USER_VEC: 232 if (vector < UVEC_MIN || vector > UVEC_MAX) 233 return 0; 234 vec_list = &uservec_list[vector]; 235 hard_vec = &uservects[vector]; 236 break; 237 default: 238 printf("%s: bogus vector type\n", __func__); 239 return 0; 240 } 241 242 /* 243 * Check if the vector is really in the list we think it's in.... 244 */ 245 for (cur_vec = LIST_FIRST(vec_list); 246 LIST_NEXT(cur_vec, ih_link) != NULL; 247 cur_vec = LIST_NEXT(cur_vec, ih_link)) { 248 if (ih == cur_vec) 249 break; 250 } 251 if (ih != cur_vec) { 252 printf("%s: 'ih' has inconsistent data\n", __func__); 253 return 0; 254 } 255 256 s = splhigh(); 257 LIST_REMOVE(ih, ih_link); 258 if (LIST_EMPTY(vec_list) && (ih->ih_type & FAST_VEC) != 0) 259 *hard_vec = (u_long)intr_glue; 260 splx(s); 261 262 kmem_free(ih, sizeof(*ih)); 263 return 1; 264 } 265 266 /* 267 * This is the dispatcher called by the low-level 268 * assembly language interrupt-glue routine. 269 */ 270 void 271 intr_dispatch(struct clockframe frame) 272 { 273 static int unexpected, straycount; 274 int vector; 275 int handled = 0; 276 ih_list_t *vec_list; 277 struct intrhand *ih; 278 279 curcpu()->ci_data.cpu_nintr++; 280 vector = (frame.cf_vo & 0xfff) >> 2; 281 if (vector < (AVEC_LOC + AVEC_MAX) && vector >= AVEC_LOC) 282 vec_list = &autovec_list[vector - AVEC_LOC]; 283 else if (vector <= (UVEC_LOC + UVEC_MAX) && vector >= UVEC_LOC) 284 vec_list = &uservec_list[vector - UVEC_LOC]; 285 else 286 panic("%s: Bogus vector %d", __func__, vector); 287 288 if ((ih = LIST_FIRST(vec_list)) == NULL) { 289 printf("%s: vector %d unexpected\n", __func__, vector); 290 if (++unexpected > 10) 291 panic("%s: too many unexpected interrupts", __func__); 292 return; 293 } 294 ih->ih_intrcnt[0]++; 295 296 /* Give all the handlers a chance. */ 297 for (; ih != NULL; ih = LIST_NEXT(ih, ih_link)) 298 handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME) ? 299 &frame : ih->ih_arg, frame.cf_sr); 300 301 if (handled) 302 straycount = 0; 303 else if (++straycount > 50) 304 panic("%s: too many stray interrupts", __func__); 305 else 306 printf("%s: stray level %d interrupt\n", __func__, vector); 307 } 308 309 bool 310 cpu_intr_p(void) 311 { 312 313 return intr_depth != 0; 314 } 315 316 const uint16_t ipl2psl_table[NIPL] = { 317 [IPL_NONE] = PSL_S | PSL_IPL0, 318 [IPL_SOFTCLOCK] = PSL_S | PSL_IPL1, 319 [IPL_SOFTBIO] = PSL_S | PSL_IPL1, 320 [IPL_SOFTNET] = PSL_S | PSL_IPL1, 321 [IPL_SOFTSERIAL] = PSL_S | PSL_IPL1, 322 [IPL_VM] = PSL_S | PSL_IPL4, 323 [IPL_SCHED] = PSL_S | PSL_IPL6, 324 [IPL_HIGH] = PSL_S | PSL_IPL7, 325 }; 326