10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*2973Sgovinda * Common Development and Distribution License (the "License"). 6*2973Sgovinda * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*2973Sgovinda * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Interrupt Vector Table Configuration 300Sstevel@tonic-gate */ 310Sstevel@tonic-gate 32*2973Sgovinda #include <sys/types.h> 330Sstevel@tonic-gate #include <sys/cpuvar.h> 340Sstevel@tonic-gate #include <sys/ivintr.h> 350Sstevel@tonic-gate #include <sys/intreg.h> 360Sstevel@tonic-gate #include <sys/cmn_err.h> 370Sstevel@tonic-gate #include <sys/privregs.h> 380Sstevel@tonic-gate #include <sys/sunddi.h> 390Sstevel@tonic-gate 40*2973Sgovinda /* 41*2973Sgovinda * Allocate an Interrupt Vector Table and some interrupt vector data structures 42*2973Sgovinda * for the reserved pool as part of the startup code. First try to allocate an 43*2973Sgovinda * interrupt vector data structure from the reserved pool, otherwise allocate it 44*2973Sgovinda * using kmem cache method. 45*2973Sgovinda */ 46*2973Sgovinda static kmutex_t intr_vec_mutex; /* Protect interrupt vector table */ 470Sstevel@tonic-gate 480Sstevel@tonic-gate /* 49*2973Sgovinda * Global softint linked list - used by softint mdb dcmd. 500Sstevel@tonic-gate */ 51*2973Sgovinda static kmutex_t softint_mutex; /* Protect global softint linked list */ 52*2973Sgovinda intr_vec_t *softint_list = NULL; 53*2973Sgovinda 54*2973Sgovinda /* Reserved pool for interrupt allocation */ 55*2973Sgovinda intr_vec_t *intr_vec_pool = NULL; /* For HW and single target SW intrs */ 56*2973Sgovinda intr_vecx_t *intr_vecx_pool = NULL; /* For multi target SW intrs */ 57*2973Sgovinda 58*2973Sgovinda /* Kmem cache handle for interrupt allocation */ 59*2973Sgovinda kmem_cache_t *intr_vec_cache = NULL; /* For HW and single target SW intrs */ 600Sstevel@tonic-gate 610Sstevel@tonic-gate /* 62*2973Sgovinda * init_ivintr() - Initialize an Interrupt Vector Table. 630Sstevel@tonic-gate */ 64*2973Sgovinda void 65*2973Sgovinda init_ivintr() 66*2973Sgovinda { 67*2973Sgovinda mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL); 68*2973Sgovinda mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL); 69*2973Sgovinda 70*2973Sgovinda /* 71*2973Sgovinda * Initialize the reserved interrupt vector data structure pools 72*2973Sgovinda * used for hardware and software interrupts. 73*2973Sgovinda */ 74*2973Sgovinda intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table + 75*2973Sgovinda (MAXIVNUM * sizeof (intr_vec_t *))); 76*2973Sgovinda intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool + 77*2973Sgovinda (MAX_RSVD_IV * sizeof (intr_vec_t))); 78*2973Sgovinda 79*2973Sgovinda bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *)); 80*2973Sgovinda bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t)); 81*2973Sgovinda bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t)); 82*2973Sgovinda } 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* 85*2973Sgovinda * fini_ivintr() - Uninitialize an Interrupt Vector Table. 860Sstevel@tonic-gate */ 87*2973Sgovinda void 88*2973Sgovinda fini_ivintr() 89*2973Sgovinda { 90*2973Sgovinda if (intr_vec_cache) 91*2973Sgovinda kmem_cache_destroy(intr_vec_cache); 92*2973Sgovinda 93*2973Sgovinda mutex_destroy(&intr_vec_mutex); 94*2973Sgovinda mutex_destroy(&softint_mutex); 95*2973Sgovinda } 960Sstevel@tonic-gate 970Sstevel@tonic-gate /* 98*2973Sgovinda * iv_alloc() - Allocate an interrupt vector data structure. 99*2973Sgovinda * 100*2973Sgovinda * This function allocates an interrupt vector data structure for hardware 101*2973Sgovinda * and single or multi target software interrupts either from the reserved 102*2973Sgovinda * pool or using kmem cache method. 1030Sstevel@tonic-gate */ 104*2973Sgovinda static intr_vec_t * 105*2973Sgovinda iv_alloc(softint_type_t type) 106*2973Sgovinda { 107*2973Sgovinda intr_vec_t *iv_p; 108*2973Sgovinda int i, count; 109*2973Sgovinda 110*2973Sgovinda count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV; 111*2973Sgovinda 112*2973Sgovinda /* 113*2973Sgovinda * First try to allocate an interrupt vector data structure from the 114*2973Sgovinda * reserved pool, otherwise allocate it using kmem_cache_alloc(). 115*2973Sgovinda */ 116*2973Sgovinda for (i = 0; i < count; i++) { 117*2973Sgovinda iv_p = (type == SOFTINT_MT) ? 118*2973Sgovinda (intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i]; 1190Sstevel@tonic-gate 120*2973Sgovinda if (iv_p->iv_pil == 0) 121*2973Sgovinda break; 122*2973Sgovinda } 123*2973Sgovinda 124*2973Sgovinda if (i < count) 125*2973Sgovinda return (iv_p); 126*2973Sgovinda 127*2973Sgovinda if (type == SOFTINT_MT) 128*2973Sgovinda cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi " 129*2973Sgovinda "target software interrupts, %d", MAX_RSVD_IVX); 130*2973Sgovinda 131*2973Sgovinda /* 132*2973Sgovinda * If the interrupt vector data structure reserved pool is already 133*2973Sgovinda * exhausted, then allocate an interrupt vector data structure using 134*2973Sgovinda * kmem_cache_alloc(), but only for the hardware and single software 135*2973Sgovinda * interrupts. Create a kmem cache for the interrupt allocation, 136*2973Sgovinda * if it is not already available. 137*2973Sgovinda */ 138*2973Sgovinda if (intr_vec_cache == NULL) 139*2973Sgovinda intr_vec_cache = kmem_cache_create("intr_vec_cache", 140*2973Sgovinda sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0); 141*2973Sgovinda 142*2973Sgovinda iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP); 143*2973Sgovinda bzero(iv_p, sizeof (intr_vec_t)); 144*2973Sgovinda 145*2973Sgovinda iv_p->iv_flags = IV_CACHE_ALLOC; 146*2973Sgovinda return (iv_p); 147*2973Sgovinda } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate /* 150*2973Sgovinda * iv_free() - Free an interrupt vector data structure. 1510Sstevel@tonic-gate */ 152*2973Sgovinda static void 153*2973Sgovinda iv_free(intr_vec_t *iv_p) 1540Sstevel@tonic-gate { 155*2973Sgovinda if (iv_p->iv_flags & IV_CACHE_ALLOC) { 156*2973Sgovinda ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT)); 157*2973Sgovinda kmem_cache_free(intr_vec_cache, iv_p); 1580Sstevel@tonic-gate } else { 159*2973Sgovinda (iv_p->iv_flags & IV_SOFTINT_MT) ? 160*2973Sgovinda bzero(iv_p, sizeof (intr_vecx_t)) : 161*2973Sgovinda bzero(iv_p, sizeof (intr_vec_t)); 1620Sstevel@tonic-gate } 1630Sstevel@tonic-gate } 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate /* 166*2973Sgovinda * add_ivintr() - Add an interrupt handler to the system 1670Sstevel@tonic-gate */ 1680Sstevel@tonic-gate int 1690Sstevel@tonic-gate add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler, 170*2973Sgovinda caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload) 1710Sstevel@tonic-gate { 172*2973Sgovinda intr_vec_t *iv_p, *new_iv_p; 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate if (inum >= MAXIVNUM || pil > PIL_MAX) 1750Sstevel@tonic-gate return (EINVAL); 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate ASSERT((uintptr_t)intr_handler > KERNELBASE); 178*2973Sgovinda 1790Sstevel@tonic-gate /* Make sure the payload buffer address is 64 bit aligned */ 1800Sstevel@tonic-gate VERIFY(((uint64_t)intr_payload & 0x7) == 0); 1810Sstevel@tonic-gate 182*2973Sgovinda new_iv_p = iv_alloc(SOFTINT_ST); 183*2973Sgovinda mutex_enter(&intr_vec_mutex); 184*2973Sgovinda 185*2973Sgovinda for (iv_p = (intr_vec_t *)intr_vec_table[inum]; 186*2973Sgovinda iv_p; iv_p = iv_p->iv_vec_next) { 187*2973Sgovinda if (iv_p->iv_pil == pil) { 188*2973Sgovinda mutex_exit(&intr_vec_mutex); 189*2973Sgovinda iv_free(new_iv_p); 190*2973Sgovinda return (EINVAL); 191*2973Sgovinda } 192*2973Sgovinda } 1930Sstevel@tonic-gate 194*2973Sgovinda ASSERT(iv_p == NULL); 1950Sstevel@tonic-gate 196*2973Sgovinda new_iv_p->iv_handler = intr_handler; 197*2973Sgovinda new_iv_p->iv_arg1 = intr_arg1; 198*2973Sgovinda new_iv_p->iv_arg2 = intr_arg2; 199*2973Sgovinda new_iv_p->iv_payload_buf = intr_payload; 200*2973Sgovinda new_iv_p->iv_pil = (ushort_t)pil; 201*2973Sgovinda new_iv_p->iv_inum = inum; 202*2973Sgovinda 203*2973Sgovinda new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum]; 204*2973Sgovinda intr_vec_table[inum] = (uint64_t)new_iv_p; 205*2973Sgovinda 206*2973Sgovinda mutex_exit(&intr_vec_mutex); 2070Sstevel@tonic-gate return (0); 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate /* 211*2973Sgovinda * rem_ivintr() - Remove an interrupt handler from the system 2120Sstevel@tonic-gate */ 213*2973Sgovinda int 214*2973Sgovinda rem_ivintr(uint_t inum, uint_t pil) 2150Sstevel@tonic-gate { 216*2973Sgovinda intr_vec_t *iv_p, *prev_iv_p; 2170Sstevel@tonic-gate 218*2973Sgovinda if (inum >= MAXIVNUM || pil > PIL_MAX) 219*2973Sgovinda return (EINVAL); 2200Sstevel@tonic-gate 221*2973Sgovinda mutex_enter(&intr_vec_mutex); 2220Sstevel@tonic-gate 223*2973Sgovinda for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum]; 224*2973Sgovinda iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next) 225*2973Sgovinda if (iv_p->iv_pil == pil) 226*2973Sgovinda break; 227*2973Sgovinda 228*2973Sgovinda if (iv_p == NULL) { 229*2973Sgovinda mutex_exit(&intr_vec_mutex); 230*2973Sgovinda return (EIO); 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate 233*2973Sgovinda ASSERT(iv_p->iv_pil_next == NULL); 234*2973Sgovinda 235*2973Sgovinda if (prev_iv_p == iv_p) 236*2973Sgovinda intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next; 237*2973Sgovinda else 238*2973Sgovinda prev_iv_p->iv_vec_next = iv_p->iv_vec_next; 239*2973Sgovinda 240*2973Sgovinda mutex_exit(&intr_vec_mutex); 241*2973Sgovinda 242*2973Sgovinda iv_free(iv_p); 243*2973Sgovinda return (0); 2440Sstevel@tonic-gate } 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate /* 2470Sstevel@tonic-gate * add_softintr() - add a software interrupt handler to the system 2480Sstevel@tonic-gate */ 249*2973Sgovinda uint64_t 250*2973Sgovinda add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1, 251*2973Sgovinda softint_type_t type) 2520Sstevel@tonic-gate { 253*2973Sgovinda intr_vec_t *iv_p; 2540Sstevel@tonic-gate 255*2973Sgovinda if (pil > PIL_MAX) 256*2973Sgovinda return (NULL); 257*2973Sgovinda 258*2973Sgovinda iv_p = iv_alloc(type); 2590Sstevel@tonic-gate 260*2973Sgovinda iv_p->iv_handler = (intrfunc)intr_handler; 261*2973Sgovinda iv_p->iv_arg1 = intr_arg1; 262*2973Sgovinda iv_p->iv_pil = (ushort_t)pil; 263*2973Sgovinda if (type == SOFTINT_MT) 264*2973Sgovinda iv_p->iv_flags |= IV_SOFTINT_MT; 2650Sstevel@tonic-gate 266*2973Sgovinda mutex_enter(&softint_mutex); 267*2973Sgovinda if (softint_list) 268*2973Sgovinda iv_p->iv_vec_next = softint_list; 269*2973Sgovinda softint_list = iv_p; 270*2973Sgovinda mutex_exit(&softint_mutex); 2710Sstevel@tonic-gate 272*2973Sgovinda return ((uint64_t)iv_p); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * rem_softintr() - remove a software interrupt handler from the system 2770Sstevel@tonic-gate */ 278*2973Sgovinda int 279*2973Sgovinda rem_softintr(uint64_t softint_id) 2800Sstevel@tonic-gate { 281*2973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id; 282*2973Sgovinda 283*2973Sgovinda ASSERT(iv_p != NULL); 284*2973Sgovinda 285*2973Sgovinda if (iv_p->iv_flags & IV_SOFTINT_PEND) 286*2973Sgovinda return (EIO); 287*2973Sgovinda 288*2973Sgovinda ASSERT(iv_p->iv_pil_next == NULL); 2890Sstevel@tonic-gate 290*2973Sgovinda mutex_enter(&softint_mutex); 291*2973Sgovinda if (softint_list == iv_p) { 292*2973Sgovinda softint_list = iv_p->iv_vec_next; 293*2973Sgovinda } else { 294*2973Sgovinda intr_vec_t *list = softint_list; 295*2973Sgovinda 296*2973Sgovinda while (list && (list->iv_vec_next != iv_p)) 297*2973Sgovinda list = list->iv_vec_next; 298*2973Sgovinda 299*2973Sgovinda list->iv_vec_next = iv_p->iv_vec_next; 300*2973Sgovinda } 301*2973Sgovinda mutex_exit(&softint_mutex); 302*2973Sgovinda 303*2973Sgovinda iv_free(iv_p); 304*2973Sgovinda return (0); 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 307*2973Sgovinda /* 308*2973Sgovinda * update_softint_arg2() - Update softint arg2. 309*2973Sgovinda * 310*2973Sgovinda * NOTE: Do not grab any mutex in this function since it may get called 311*2973Sgovinda * from the high-level interrupt context. 312*2973Sgovinda */ 3130Sstevel@tonic-gate int 314*2973Sgovinda update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2) 3150Sstevel@tonic-gate { 316*2973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id; 3170Sstevel@tonic-gate 318*2973Sgovinda ASSERT(iv_p != NULL); 3190Sstevel@tonic-gate 320*2973Sgovinda if (iv_p->iv_flags & IV_SOFTINT_PEND) 321*2973Sgovinda return (EIO); 3220Sstevel@tonic-gate 323*2973Sgovinda iv_p->iv_arg2 = intr_arg2; 324*2973Sgovinda return (0); 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 327*2973Sgovinda /* 328*2973Sgovinda * update_softint_pri() - Update softint priority. 329*2973Sgovinda */ 3300Sstevel@tonic-gate int 331*2973Sgovinda update_softint_pri(uint64_t softint_id, uint_t pil) 3320Sstevel@tonic-gate { 333*2973Sgovinda intr_vec_t *iv_p = (intr_vec_t *)softint_id; 3340Sstevel@tonic-gate 335*2973Sgovinda ASSERT(iv_p != NULL); 3360Sstevel@tonic-gate 337*2973Sgovinda if (pil > PIL_MAX) 338*2973Sgovinda return (EINVAL); 339*2973Sgovinda 340*2973Sgovinda iv_p->iv_pil = pil; 341*2973Sgovinda return (0); 3420Sstevel@tonic-gate } 343