1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Interrupt Vector Table Configuration 30 */ 31 32 #include <sys/types.h> 33 #include <sys/cpuvar.h> 34 #include <sys/ivintr.h> 35 #include <sys/intreg.h> 36 #include <sys/cmn_err.h> 37 #include <sys/privregs.h> 38 #include <sys/sunddi.h> 39 40 /* 41 * Allocate an Interrupt Vector Table and some interrupt vector data structures 42 * for the reserved pool as part of the startup code. First try to allocate an 43 * interrupt vector data structure from the reserved pool, otherwise allocate it 44 * using kmem cache method. 45 */ 46 static kmutex_t intr_vec_mutex; /* Protect interrupt vector table */ 47 48 /* 49 * Global softint linked list - used by softint mdb dcmd. 50 */ 51 static kmutex_t softint_mutex; /* Protect global softint linked list */ 52 intr_vec_t *softint_list = NULL; 53 54 /* Reserved pool for interrupt allocation */ 55 intr_vec_t *intr_vec_pool = NULL; /* For HW and single target SW intrs */ 56 intr_vecx_t *intr_vecx_pool = NULL; /* For multi target SW intrs */ 57 58 /* Kmem cache handle for interrupt allocation */ 59 kmem_cache_t *intr_vec_cache = NULL; /* For HW and single target SW intrs */ 60 61 /* 62 * init_ivintr() - Initialize an Interrupt Vector Table. 63 */ 64 void 65 init_ivintr() 66 { 67 mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL); 68 mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL); 69 70 /* 71 * Initialize the reserved interrupt vector data structure pools 72 * used for hardware and software interrupts. 73 */ 74 intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table + 75 (MAXIVNUM * sizeof (intr_vec_t *))); 76 intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool + 77 (MAX_RSVD_IV * sizeof (intr_vec_t))); 78 79 bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *)); 80 bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t)); 81 bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t)); 82 } 83 84 /* 85 * fini_ivintr() - Uninitialize an Interrupt Vector Table. 86 */ 87 void 88 fini_ivintr() 89 { 90 if (intr_vec_cache) 91 kmem_cache_destroy(intr_vec_cache); 92 93 mutex_destroy(&intr_vec_mutex); 94 mutex_destroy(&softint_mutex); 95 } 96 97 /* 98 * iv_alloc() - Allocate an interrupt vector data structure. 99 * 100 * This function allocates an interrupt vector data structure for hardware 101 * and single or multi target software interrupts either from the reserved 102 * pool or using kmem cache method. 103 */ 104 static intr_vec_t * 105 iv_alloc(softint_type_t type) 106 { 107 intr_vec_t *iv_p; 108 int i, count; 109 110 count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV; 111 112 /* 113 * First try to allocate an interrupt vector data structure from the 114 * reserved pool, otherwise allocate it using kmem_cache_alloc(). 115 */ 116 for (i = 0; i < count; i++) { 117 iv_p = (type == SOFTINT_MT) ? 118 (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i]; 119 120 if (iv_p->iv_pil == 0) 121 break; 122 } 123 124 if (i < count) 125 return (iv_p); 126 127 if (type == SOFTINT_MT) 128 cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi " 129 "target software interrupts, %d", MAX_RSVD_IVX); 130 131 /* 132 * If the interrupt vector data structure reserved pool is already 133 * exhausted, then allocate an interrupt vector data structure using 134 * kmem_cache_alloc(), but only for the hardware and single software 135 * interrupts. Create a kmem cache for the interrupt allocation, 136 * if it is not already available. 137 */ 138 if (intr_vec_cache == NULL) 139 intr_vec_cache = kmem_cache_create("intr_vec_cache", 140 sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0); 141 142 iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP); 143 bzero(iv_p, sizeof (intr_vec_t)); 144 145 iv_p->iv_flags = IV_CACHE_ALLOC; 146 return (iv_p); 147 } 148 149 /* 150 * iv_free() - Free an interrupt vector data structure. 151 */ 152 static void 153 iv_free(intr_vec_t *iv_p) 154 { 155 if (iv_p->iv_flags & IV_CACHE_ALLOC) { 156 ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT)); 157 kmem_cache_free(intr_vec_cache, iv_p); 158 } else { 159 (iv_p->iv_flags & IV_SOFTINT_MT) ? 160 bzero(iv_p, sizeof (intr_vecx_t)) : 161 bzero(iv_p, sizeof (intr_vec_t)); 162 } 163 } 164 165 /* 166 * add_ivintr() - Add an interrupt handler to the system 167 */ 168 int 169 add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler, 170 caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload) 171 { 172 intr_vec_t *iv_p, *new_iv_p; 173 174 if (inum >= MAXIVNUM || pil > PIL_MAX) 175 return (EINVAL); 176 177 ASSERT((uintptr_t)intr_handler > KERNELBASE); 178 179 /* Make sure the payload buffer address is 64 bit aligned */ 180 VERIFY(((uint64_t)intr_payload & 0x7) == 0); 181 182 new_iv_p = iv_alloc(SOFTINT_ST); 183 mutex_enter(&intr_vec_mutex); 184 185 for (iv_p = (intr_vec_t *)intr_vec_table[inum]; 186 iv_p; iv_p = iv_p->iv_vec_next) { 187 if (iv_p->iv_pil == pil) { 188 mutex_exit(&intr_vec_mutex); 189 iv_free(new_iv_p); 190 return (EINVAL); 191 } 192 } 193 194 ASSERT(iv_p == NULL); 195 196 new_iv_p->iv_handler = intr_handler; 197 new_iv_p->iv_arg1 = intr_arg1; 198 new_iv_p->iv_arg2 = intr_arg2; 199 new_iv_p->iv_payload_buf = intr_payload; 200 new_iv_p->iv_pil = (ushort_t)pil; 201 new_iv_p->iv_inum = inum; 202 203 new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum]; 204 intr_vec_table[inum] = (uint64_t)new_iv_p; 205 206 mutex_exit(&intr_vec_mutex); 207 return (0); 208 } 209 210 /* 211 * rem_ivintr() - Remove an interrupt handler from the system 212 */ 213 int 214 rem_ivintr(uint_t inum, uint_t pil) 215 { 216 intr_vec_t *iv_p, *prev_iv_p; 217 218 if (inum >= MAXIVNUM || pil > PIL_MAX) 219 return (EINVAL); 220 221 mutex_enter(&intr_vec_mutex); 222 223 for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum]; 224 iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next) 225 if (iv_p->iv_pil == pil) 226 break; 227 228 if (iv_p == NULL) { 229 mutex_exit(&intr_vec_mutex); 230 return (EIO); 231 } 232 233 ASSERT(iv_p->iv_pil_next == NULL); 234 235 if (prev_iv_p == iv_p) 236 intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next; 237 else 238 prev_iv_p->iv_vec_next = iv_p->iv_vec_next; 239 240 mutex_exit(&intr_vec_mutex); 241 242 iv_free(iv_p); 243 return (0); 244 } 245 246 /* 247 * add_softintr() - add a software interrupt handler to the system 248 */ 249 uint64_t 250 add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1, 251 softint_type_t type) 252 { 253 intr_vec_t *iv_p; 254 255 if (pil > PIL_MAX) 256 return (NULL); 257 258 iv_p = iv_alloc(type); 259 260 iv_p->iv_handler = (intrfunc)intr_handler; 261 iv_p->iv_arg1 = intr_arg1; 262 iv_p->iv_pil = (ushort_t)pil; 263 if (type == SOFTINT_MT) 264 iv_p->iv_flags |= IV_SOFTINT_MT; 265 266 mutex_enter(&softint_mutex); 267 if (softint_list) 268 iv_p->iv_vec_next = softint_list; 269 softint_list = iv_p; 270 mutex_exit(&softint_mutex); 271 272 return ((uint64_t)iv_p); 273 } 274 275 /* 276 * rem_softintr() - remove a software interrupt handler from the system 277 */ 278 int 279 rem_softintr(uint64_t softint_id) 280 { 281 intr_vec_t *iv_p = (intr_vec_t *)softint_id; 282 283 ASSERT(iv_p != NULL); 284 285 if (iv_p->iv_flags & IV_SOFTINT_PEND) 286 return (EIO); 287 288 ASSERT(iv_p->iv_pil_next == NULL); 289 290 mutex_enter(&softint_mutex); 291 if (softint_list == iv_p) { 292 softint_list = iv_p->iv_vec_next; 293 } else { 294 intr_vec_t *list = softint_list; 295 296 while (list && (list->iv_vec_next != iv_p)) 297 list = list->iv_vec_next; 298 299 list->iv_vec_next = iv_p->iv_vec_next; 300 } 301 mutex_exit(&softint_mutex); 302 303 iv_free(iv_p); 304 return (0); 305 } 306 307 /* 308 * update_softint_arg2() - Update softint arg2. 309 * 310 * NOTE: Do not grab any mutex in this function since it may get called 311 * from the high-level interrupt context. 312 */ 313 int 314 update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2) 315 { 316 intr_vec_t *iv_p = (intr_vec_t *)softint_id; 317 318 ASSERT(iv_p != NULL); 319 320 if (iv_p->iv_flags & IV_SOFTINT_PEND) 321 return (EIO); 322 323 iv_p->iv_arg2 = intr_arg2; 324 return (0); 325 } 326 327 /* 328 * update_softint_pri() - Update softint priority. 329 */ 330 int 331 update_softint_pri(uint64_t softint_id, uint_t pil) 332 { 333 intr_vec_t *iv_p = (intr_vec_t *)softint_id; 334 335 ASSERT(iv_p != NULL); 336 337 if (pil > PIL_MAX) 338 return (EINVAL); 339 340 iv_p->iv_pil = pil; 341 return (0); 342 } 343