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