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